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