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