1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <stdio.h>
28 #include <malloc.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <string.h>
32 #include "xlator.h"
33 #include "util.h"
34 #include "bucket.h"
35 #include "errlog.h"
36
37 /* Statics: */
38 #define TRUE 1
39 #define FALSE 0
40 #define NLISTS 50
41 #define NPAR 25
42
43 static bucket_t **Buckethead;
44 static int N_lists;
45
46 static int Bc = -1; /* For iterators. */
47 static bucket_t *Bp;
48
49 static void start_new_list(const bucket_t *);
50 static void grow_lists(void);
51 static bucket_t *new_bucket(const char *, int);
52 static void print_iface(const Interface *);
53 static void new_hashmap(void);
54 static int add_to_hashmap(const char *, const bucket_t *);
55 static bucket_t *find_in_hashmap(const char *);
56 /*
57 * initialization interfaces.
58 */
59
60 /*
61 * create_lists -- initialize the bucket list and hash map.
62 */
63 void
create_lists(void)64 create_lists(void)
65 {
66
67 errlog(BEGIN, "create_lists() {");
68 new_hashmap();
69 if ((Buckethead = calloc(sizeof (bucket_t *), NLISTS)) == NULL) {
70 errlog(FATAL, "out of memory creating initial "
71 "list of versions");
72
73 }
74 N_lists = NLISTS;
75 errlog(END, "}");
76 }
77
78
79 /*
80 * data-loading interfaces -- adding buckets to lists and
81 * interfaces to buckets.
82 */
83
84 /*
85 * add_parent -- add a parent node. Returns TRUE or FALSE.
86 *
87 * if *version == NULL, then
88 * the bucket version (eg, SUNW_1.1) hasn't
89 * been parsed correctly. Die.
90 * if *after == NULL, then this is the ``initial case'',
91 * where no predecessor (child) exists. We start a new
92 * tree of buckets.
93 * if *after != NULL, we have the normal case, and
94 * add to an existing tree.
95 * if *after is not a version name found among the buckets,
96 * then something got misparsed or the versions file is
97 * malformed. Function will print problem and
98 * return 0 so caller can report location of error.
99 * If either version or after is NULL, it's a programmer error.
100 */
101 int
add_parent(const char * version,const char * after,int weak)102 add_parent(const char *version, const char *after, int weak)
103 {
104 bucket_t *new, *child;
105
106 /* Sanity-check parameters. */
107 assert(version != NULL, "passed a null version to add_parent");
108 assert(after != NULL, "passed a null after to add_parent");
109 errlog(BEGIN, "add_parent(%s,%s,%d) {", version, after, weak);
110 if ((new = find_in_hashmap(version)) == NULL) {
111 /* We don't have one have one yet. */
112 new = new_bucket(version, weak);
113 }
114 new->b_weak = weak;
115 if (*after == '\0') {
116 /*
117 * This is the ``initial case'', where no
118 * child exists. We start a new tree of buckets.
119 */
120 (void) add_to_hashmap(version, new);
121 start_new_list(new);
122 } else {
123 if ((child = find_in_hashmap(after)) == NULL) {
124 /*
125 * The version in the spec doesn't appear in the
126 * versions file. One or the other is lying.
127 */
128 errlog(WARNING, "set file: can't find version \"%s\","
129 "therefor can't add it's parent, \"%s\"",
130 after, version);
131 errlog(END, "} /* add_parent */");
132 return (FALSE);
133 }
134 (void) add_to_hashmap(version, new);
135 child->b_parent = new;
136 }
137 errlog(END, "} /* add_parent */");
138 return (TRUE);
139 }
140
141 /*
142 * add_uncle -- adds an uncle node
143 */
144 int
add_uncle(const char * version,const char * after,int weak)145 add_uncle(const char *version, const char *after, int weak)
146 {
147 bucket_t *new, *child;
148 struct bucketlist *uncle;
149
150 /* Sanity-check parameters. */
151 assert(version != NULL, "passed a null version to add_uncle");
152 assert(after != NULL, "passed a null after to add_uncle");
153 errlog(BEGIN, "add_uncle(%s,%s,%d) {", version, after, weak);
154 if ((new = find_in_hashmap(version)) == NULL) {
155 /* We don't have one have one yet. */
156 new = new_bucket(version, weak);
157 }
158 if (*after == '\0') {
159 /*
160 * This is the ``initial case'', where no
161 * child exists. We start a new tree of buckets.
162 */
163 (void) add_to_hashmap(version, new);
164 start_new_list(new);
165 } else {
166 if ((child = find_in_hashmap(after)) == NULL) {
167 /*
168 * The version in the spec doesn't appear in the
169 * versions file. One or the other is lying.
170 */
171 errlog(WARNING, "set file: can't find version \"%s\","
172 "therefor can't add it's uncle, \"%s\"",
173 after, version);
174 errlog(END, "}");
175 return (FALSE);
176 }
177 (void) add_to_hashmap(version, new);
178 uncle = malloc(sizeof (struct bucketlist));
179 uncle->bl_next = child->b_uncles;
180 uncle->bl_bucket = new;
181 child->b_uncles = uncle;
182 }
183 errlog(END, "}");
184 return (TRUE);
185 }
186
187 /*
188 * set_weak -- set a version to be a weak version
189 */
190 void
set_weak(const char * version,int weak)191 set_weak(const char *version, int weak)
192 {
193 bucket_t *v;
194 if ((v = find_in_hashmap(version)) == NULL) {
195 /* We don't have one have one yet. */
196 errlog(ERROR|FATAL, "Unable to set weak. Version not found");
197 }
198 v->b_weak = weak;
199 }
200
201 /*
202 * add_by_name -- look up bucket and add an interface to it.
203 * Returns 0 for success or an errno.h value for failure.
204 *
205 * if *version is not among the buckets, then the
206 * version in the spec doesn't appear in the
207 * set file. One or the other is lying. Function will
208 * report the problem and return ENOENT
209 * so the front end can report and exit (or
210 * continue if it wants).
211 * if interface ore version is NULL, then
212 * the begin line code should
213 * have caught it long before, to avoid passing
214 * a null pointer around. Die.
215 *
216 */
217 #define ADD_EQUALS(str) if (strchr(str, '=') == NULL) (void) strcat(str, " =")
218
219 int
add_by_name(const char * version,const Interface * interface)220 add_by_name(const char *version, const Interface *interface)
221 {
222 bucket_t *b;
223 char buffer[1024];
224
225 assert(version != NULL, "passed a null version to add_by_name");
226 assert(interface != NULL, "passed a null interface to add_by_name");
227
228 errlog(BEGIN, "add_by_name(%s,", version);
229 print_iface(interface);
230 errlog(TRACING, ");");
231
232 /* Sanity-check the parameters. */
233 if ((b = find_in_hashmap(version)) == NULL) {
234 /*
235 * The version in the spec doesn't appear in the
236 * versions file. Alas, this isn't an error. It can
237 * happen whenever doing just sparc, just i386
238 * or the like.
239 */
240 errlog(END, "}");
241 return (ENOENT);
242 }
243 /*
244 * Add to bucket.
245 */
246 (void) snprintf(buffer, sizeof (buffer), "%s", interface->IF_name);
247
248 if (interface->IF_filter && interface->IF_auxiliary) {
249 errlog(FATAL, "Error: cannot set both FILTER and AUXILIARY "
250 "for an interface: %s", interface->IF_name);
251 }
252
253 if (interface->IF_filter) {
254 ADD_EQUALS(buffer);
255 if (interface->IF_type == FUNCTION) {
256 (void) strcat(buffer, " FUNCTION");
257 } else if (interface->IF_type == DATA) {
258 (void) strcat(buffer, " DATA");
259 }
260 (void) strcat(buffer, " FILTER ");
261 (void) strcat(buffer, interface->IF_filter);
262 } else if (interface->IF_auxiliary) {
263 ADD_EQUALS(buffer);
264 (void) strcat(buffer, " AUXILIARY ");
265 (void) strcat(buffer, interface->IF_auxiliary);
266 } else if (IsFilterLib) {
267 /*
268 * For DATA types it is currently assumed that they are
269 * handled via a minimal C file, e.g. 'data.c', in the
270 * library's build. Hence, we do not append '= DATA' here.
271 */
272 if (interface->IF_type == FUNCTION) {
273 ADD_EQUALS(buffer);
274 (void) strcat(buffer, " FUNCTION");
275 }
276 }
277
278 switch (interface->IF_binding) {
279 case DIRECT:
280 ADD_EQUALS(buffer);
281 (void) strcat(buffer, " DIRECT");
282 break;
283 case NODIRECT:
284 ADD_EQUALS(buffer);
285 (void) strcat(buffer, " NODIRECT");
286 break;
287 }
288
289 if (interface->IF_binding == PROTECTED) {
290 /* Assign in case of realloc. */
291 b->b_protected_table =
292 add_to_stringtable(b->b_protected_table, buffer);
293 b->b_has_protecteds = 1;
294 errlog(VERBOSE, "set has_protecteds on bucket 0x%p", b);
295 } else {
296 /* Assign in case of realloc. */
297 b->b_global_table = add_to_stringtable(b->b_global_table,
298 buffer);
299 }
300 errlog(END, "}");
301 return (0);
302 }
303
304
305 /*
306 * Processing interfaces
307 */
308
309 /*
310 * sort_buckets -- sort the interfaces within the buckets into
311 * alphabetical order.
312 */
313 void
sort_buckets(void)314 sort_buckets(void)
315 {
316 bucket_t *l, *b;
317
318 errlog(BEGIN, "sort_buckets() {");
319 for (l = first_list(); l != NULL; l = next_list()) {
320 errlog(VERBOSE, "l-bucket: %s", l->b_name);
321 for (b = first_from_list(l); b != NULL; b = next_from_list()) {
322 errlog(VERBOSE, " b-bkt: %s", b->b_name);
323 sort_stringtable(b->b_global_table);
324 sort_stringtable(b->b_protected_table);
325 if (b->b_uncles) {
326
327 if (b->b_uncles->bl_bucket) {
328 sort_stringtable(b->b_uncles->bl_bucket->b_global_table);
329 sort_stringtable(b->b_uncles->bl_bucket->b_protected_table);
330 }
331 }
332 }
333 }
334 errlog(END, "}");
335 }
336
337
338 /*
339 * add_local -- set the local flag on the logically first bucket.
340 * This decision may belong in the caller, as it is about
341 * mapfiles, not inherent ordering or bucket contents...
342 */
343 void
add_local(void)344 add_local(void)
345 {
346 bucket_t *b, *list;
347 int done = 0;
348
349 errlog(BEGIN, "add_local() {");
350 /* Iterate across lists of buckets */
351 for (list = first_list(); list != NULL; list = next_list()) {
352 /* Traverse the list found. */
353 for (b = list; b != NULL; b = b->b_parent) {
354 if (b->b_weak != 1) {
355 /* We've found the first non-weak. */
356 b->b_has_locals = done = 1;
357 errlog(VERBOSE,
358 "set has_locals on bucket 0x%p", b);
359 break;
360 }
361 }
362 if (b != NULL && b->b_has_locals == 1)
363 break;
364 }
365 if (done == 0) {
366 errlog(WARNING, "has_locals never set");
367 }
368 errlog(END, "}");
369 }
370
371
372 /*
373 * Output interfaces, mostly iterators
374 */
375
376
377 /*
378 * parents_of -- return a list of all parents.
379 */
380 char **
parents_of(const bucket_t * start)381 parents_of(const bucket_t *start)
382 {
383 static char *a[NPAR] = {NULL};
384 const bucket_t *b = start;
385 char **p = &a[0];
386
387 assert(start != NULL, "passed a null start to parents_of");
388 errlog(BEGIN, "parents_of() {");
389 a[0] = '\0';
390
391 /* Go to parent, print it and all its uncle. */
392 if (b->b_parent == NULL) {
393 errlog(TRACING, "returning empty string");
394 errlog(END, "}");
395 return (a);
396 }
397 b = b->b_parent;
398 *p++ = b->b_name;
399 *p = '\0';
400
401 assert(p < &a[NPAR], "p fell off the end of a in parents_of");
402 errlog(END, "}");
403 return (a);
404 }
405
406 /*
407 * first, next_from_bucket --iterators for bucket contents. Serially
408 * reusable only.
409 */
410 int Ic = -1;
411
412 /*
413 * debugging interfaces
414 */
415 void
print_bucket(const bucket_t * b)416 print_bucket(const bucket_t *b)
417 {
418
419 errlog(TRACING, "bucket_t at 0x%p {", (void *)b);
420 errlog(TRACING, " char *b_name = \"%s\";", b->b_name);
421 errlog(TRACING, " struct bucket_t *b_parent = 0x%p;",
422 (void *)b->b_parent);
423 errlog(TRACING, " struct bucketlist *b_uncles = 0x%p;",
424 (void *)b->b_uncles);
425 errlog(TRACING, " struct bucket_t *b_thread = 0x%p;",
426 (void *)b->b_thread);
427 errlog(TRACING, " int b_has_locals = %d;",
428 b->b_has_locals);
429 errlog(TRACING, " int b_has_protecteds = %d;",
430 b->b_has_protecteds);
431 errlog(TRACING, " int b_was_printed = %d;",
432 b->b_was_printed);
433 errlog(TRACING, " int b_weak = %d;",
434 b->b_weak);
435 errlog(TRACING, " table_t *b_global_table = 0x%p;",
436 (void *)b->b_global_table);
437 errlog(TRACING, " table_t *b_protected_table = 0x%p;",
438 (void *)b->b_protected_table);
439 errlog(TRACING, "}");
440 }
441
442 void
print_all_buckets(void)443 print_all_buckets(void)
444 {
445 bucket_t *l, *b;
446 int i = 0, j = 0;
447 char **p;
448
449 for (i = 0, l = first_list(); l != NULL; l = next_list(), ++i) {
450 errlog(TRACING, "list %d", i);
451 for (j = 0, b = first_from_list(l);
452 b != NULL; b = next_from_list(), ++j) {
453 errlog(TRACING, "bucket %d", j);
454 print_bucket(b);
455 errlog(TRACING, "global interfaces = {");
456 print_stringtable(b->b_global_table);
457 errlog(TRACING, "}");
458 errlog(TRACING, "protected interfaces = {");
459 print_stringtable(b->b_protected_table);
460 errlog(TRACING, "}");
461
462 for (p = parents_of(b); p != NULL && *p != NULL; ++p) {
463 errlog(TRACING, " %s", *p);
464 }
465 errlog(TRACING, ";");
466
467 if (b->b_uncles) {
468 errlog(TRACING, " uncle bucket %d.1", j);
469 print_bucket(b->b_uncles->bl_bucket);
470 errlog(TRACING, "global interfaces = {");
471 print_stringtable(
472 b->b_uncles->bl_bucket->b_global_table);
473 errlog(TRACING, "}");
474 errlog(TRACING, "protected interfaces = {");
475 print_stringtable(
476 b->b_uncles->bl_bucket->b_protected_table);
477 errlog(TRACING, "}");
478 }
479 }
480 }
481 }
482
483
484 /*
485 * lower-level functions, not visible outside the file.
486 */
487
488 /*
489 * new_bucket -- create a bucket for a given version. Must not fail.
490 */
491 static bucket_t *
new_bucket(const char * name,int weak)492 new_bucket(const char *name, int weak)
493 {
494 bucket_t *b;
495
496 if ((b = (bucket_t *)calloc(1, sizeof (bucket_t))) == NULL) {
497 errlog(FATAL, "out of memory creating a bucket "
498 "to store interfaces in");
499 }
500 if ((b->b_name = strdup(name)) == NULL) {
501 errlog(FATAL, "out of memory storing an interface "
502 "in a version bucket");
503 }
504 b->b_uncles = NULL;
505 b->b_global_table = create_stringtable(TABLE_INITIAL);
506 b->b_protected_table = create_stringtable(TABLE_INITIAL);
507 b->b_weak = weak;
508 return (b);
509 }
510
511
512 /*
513 * start_new_list -- start a list of buckets.
514 */
515 static void
start_new_list(const bucket_t * b)516 start_new_list(const bucket_t *b)
517 {
518 int i;
519
520 errlog(BEGIN, "start_new_list() {");
521 assert(Buckethead != NULL, "Buckethead null in start_new_list");
522 for (i = 0; Buckethead[i] != NULL && i < N_lists; ++i)
523 continue;
524 if (i >= N_lists) {
525 grow_lists();
526 }
527 Buckethead[i] = (bucket_t *)b;
528 errlog(END, "}");
529 }
530
531 /*
532 * grow_list -- make more lists. This should never occur...
533 */
534 static void
grow_lists(void)535 grow_lists(void)
536 {
537 int i = N_lists;
538
539 errlog(BEGIN, "grow_lists() {");
540 errlog(WARNING, "Warning: more than %d version lists "
541 "required (< %d is normal). Check sets file "
542 "to see why so many lines appear.",
543 N_lists, NLISTS);
544
545 N_lists *= 2;
546 if ((Buckethead = realloc(Buckethead, sizeof (bucket_t *) * N_lists))
547 == NULL) {
548 errlog(FATAL, "out of memory growing list of "
549 "version buckets");
550 }
551 for (; i < N_lists; ++i) {
552 Buckethead[i] = NULL;
553 }
554 }
555
556 /*
557 * delete_lists -- clean up afterwards.
558 */
559 void
delete_lists(void)560 delete_lists(void)
561 {
562 N_lists = 0;
563 free(Buckethead);
564 Buckethead = 0;
565 }
566
567 /*
568 * first_list, next_list -- an iterator for lists themselves. Serially
569 * reusable only.
570 */
571 bucket_t *
first_list(void)572 first_list(void)
573 {
574 Bc = 0;
575 return (Buckethead[Bc]);
576 }
577
578 bucket_t *
next_list(void)579 next_list(void)
580 {
581 return (Buckethead[++Bc]);
582 }
583
584
585 /*
586 * first, next, last_from_list -- iterators for individual lists. Serially
587 * reusable only.
588 */
589 bucket_t *
first_from_list(const bucket_t * l)590 first_from_list(const bucket_t *l)
591 {
592 return (Bp = (bucket_t *)l);
593 }
594
595 bucket_t *
next_from_list(void)596 next_from_list(void)
597 {
598 return (Bp = Bp->b_parent);
599 }
600
601
602
603 /*
604 * Iface print utility
605 */
606 static void
print_iface(const Interface * p)607 print_iface(const Interface * p)
608 {
609
610 errlog(TRACING, "%s (%s, %s, %s %d)", p->IF_name,
611 (p->IF_type == FUNCTION) ? "function" :
612 (p->IF_type == DATA) ? "data" : "unknown type",
613 (p->IF_version) ? p->IF_version : "unknown version",
614 (p->IF_class) ? p->IF_class : "unknown class",
615 p->IF_binding);
616 }
617
618
619
620 #define HASHMAPSIZE 100
621 #define ERR (-1)
622
623 static struct {
624 hashmap_t *hh_map;
625 int hh_map_size;
626 int hh_mapC;
627 hashmap_t *hh_last;
628 } Hashhead = {
629 NULL, -1, -1, NULL
630 };
631
632 static int checksum(const char *);
633 static void print_hashmap(const hashmap_t *);
634
635 /*
636 * new_hashmap -- create the hash.
637 */
638 static void
new_hashmap(void)639 new_hashmap(void)
640 {
641
642 errlog(BEGIN, "new_hashmap() {");
643 if ((Hashhead.hh_map = calloc(sizeof (hashmap_t), HASHMAPSIZE))
644 == NULL) {
645 errlog(FATAL, "out of memory creating a hash-map of "
646 "the versions");
647 }
648 Hashhead.hh_mapC = 0;
649 errlog(END, "}");
650 }
651
652 /*
653 * add_to_hashmap -- add a bucket to the map. This is strictly for
654 * use by add_parent()/add_uncle().
655 */
656 static int
add_to_hashmap(const char * version_name,const bucket_t * bucket)657 add_to_hashmap(const char *version_name, const bucket_t *bucket)
658 {
659 hashmap_t *p;
660
661 assert(Hashhead.hh_map != NULL,
662 "Hashead.map was null in add_to_hashmap");
663 assert(Hashhead.hh_mapC < HASHMAPSIZE,
664 "mapC too big in add_to_hashmap");
665 errlog(BEGIN, "add_to_hashmap(%s, %s) {", version_name, bucket);
666 if (find_in_hashmap(version_name) != NULL) {
667 /* Seen for the second time. TBD... */
668 errlog(END, "} /* add_to_hashmap */");
669 return (ERR);
670 }
671 p = &Hashhead.hh_map[Hashhead.hh_mapC++];
672 if ((p->h_version_name = strdup(version_name)) == NULL) {
673 errlog(FATAL, "out of memory storing a version name");
674
675 }
676 p->h_bucket = (bucket_t *)bucket;
677 p->h_hash = checksum(version_name);
678 Hashhead.hh_last = p;
679 print_hashmap(p);
680 errlog(END, "} /* add_to_hashmap */");
681 return (0);
682 }
683
684
685 /*
686 * find_in_hashmap -- find a bucket by name. Strictly for use by addByName().
687 */
688 static bucket_t *
find_in_hashmap(const char * version_name)689 find_in_hashmap(const char *version_name)
690 {
691 hashmap_t *current;
692 int hash = checksum(version_name);
693
694 assert(Hashhead.hh_map != NULL,
695 "Hashhead.hh_map was null in find_in_hashmap");
696 errlog(BEGIN, "find_in_hashmap(%s) {", version_name);
697 if (Hashhead.hh_last != NULL && Hashhead.hh_last->h_hash == hash &&
698 strcmp(Hashhead.hh_last->h_version_name, version_name) == 0) {
699 errlog(END, "}");
700 return (Hashhead.hh_last->h_bucket);
701 }
702 for (current = Hashhead.hh_map;
703 current->h_version_name != NULL; ++current) {
704 if (current->h_hash == hash &&
705 strcmp(current->h_version_name, version_name) == 0) {
706 /* Found it */
707 Hashhead.hh_last = current;
708 errlog(END, "}");
709 return (current->h_bucket);
710 }
711 }
712 /* Doesn't exist, meaning version name is bogus. */
713 errlog(END, "}");
714 return (NULL);
715 }
716
717 /*
718 * checksum -- from sum(1), algorithm 1.
719 */
720 static int
checksum(const char * p)721 checksum(const char *p)
722 {
723 int sum;
724
725 for (sum = 0; *p != '\0'; ++p) {
726 if (sum & 01)
727 sum = (sum >> 1) + 0x8000;
728 else
729 sum >>= 1;
730 sum += *p;
731 sum &= 0xFFFF;
732 }
733 return (sum);
734 }
735
736 static void
print_hashmap(const hashmap_t * h)737 print_hashmap(const hashmap_t *h)
738 {
739 errlog(VERBOSE, "struct hashmap_t at 0x4.4x {", h);
740 errlog(VERBOSE, " int h_hash = %d;", h->h_hash);
741 errlog(VERBOSE, " char *h_version_name = \"%s\";",
742 h->h_version_name);
743 errlog(VERBOSE, " bucket_t *h_bucket = 0x%p;;",
744 (void *) h->h_bucket);
745 errlog(VERBOSE, "}");
746 }
747