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 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 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 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 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 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 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 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 ** 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 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 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 * 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 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 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 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 * 574 first_list(void) 575 { 576 Bc = 0; 577 return (Buckethead[Bc]); 578 } 579 580 bucket_t * 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 * 592 first_from_list(const bucket_t *l) 593 { 594 return (Bp = (bucket_t *)l); 595 } 596 597 bucket_t * 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 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 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 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 * 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 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 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