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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2019 Joyent, Inc. 26 */ 27 28 /* 29 * Analyze the versioning information within a file. 30 * 31 * -C demangle symbol names. 32 * 33 * -d dump version definitions. 34 * 35 * -l print reduced (local) symbols. Implies -s. 36 * 37 * -n normalize any version definitions. 38 * 39 * -o dump output in one-line fashion (more suitable for grep'ing 40 * and diff'ing). 41 * 42 * -r dump the version requirements on library dependencies 43 * 44 * -s display the symbols associated with each version definition. 45 * 46 * -v verbose output. With the -r and -d options any WEAK attribute 47 * is displayed. With the -d option, any version inheritance, 48 * and the base version are displayed. With the -r option, 49 * WEAK and INFO attributes are displayed. With the -s option 50 * the version symbol is displayed. 51 * 52 * -I index only print the specifed version index, or index range. 53 * 54 * -N name only print the specifed `name'. 55 */ 56 #include <fcntl.h> 57 #include <stdio.h> 58 #include <libelf.h> 59 #include <link.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 #include <locale.h> 64 #include <errno.h> 65 #include <sgs.h> 66 #include <conv.h> 67 #include <gelf.h> 68 #include <debug.h> 69 #include <ctype.h> 70 #include <alist.h> 71 #include "msg.h" 72 73 /* 74 * Define Alist initialization sizes. 75 */ 76 #define AL_CNT_MATCH_LIST 5 /* match_list initial alist count */ 77 #define AL_CNT_GVER_DESC 25 /* version tracking descriptors */ 78 79 typedef struct cache { 80 Elf_Scn *c_scn; 81 Elf_Data *c_data; 82 char *c_name; 83 } Cache; 84 85 typedef struct gver_desc { 86 const char *vd_name; 87 unsigned long vd_hash; 88 GElf_Half vd_ndx; 89 GElf_Half vd_flags; 90 APlist *vd_deps; 91 } GVer_desc; 92 93 /* Versym related data used by gvers_syms() */ 94 typedef struct { 95 GElf_Versym *vsd_vsp; /* ptr to versym data */ 96 Elf_Data *vsd_sym_data; /* ptr to symtab data */ 97 Word vsd_symn; /* # of symbols in symtab */ 98 const char *vsd_strs; /* string table data */ 99 } Gver_sym_data; 100 101 /* 102 * Type used to manage -I and -N options: 103 * 104 * The -I option specifies a VERSYM index, or index range. The 105 * result is to select the VERDEF or VERNEED records with 106 * indexes that match those given. 107 * 108 * -N options come in two forms: 109 * 110 * 1) name 111 * 2) needobj (version) 112 * 113 * The meaning of the first case depends on the type of 114 * version record being matched: 115 * 116 * VERDEF - name is the name of a version defined 117 * by the object being processed (i.e. SUNW_1.1). 118 * 119 * VERNEED - name is the name of the object file 120 * on which the dependency exists (i.e. libc.so.1). 121 * 122 * -N options of the second form only apply to VERNEED records. 123 * They are used to specify a version from a needed object. 124 */ 125 /* match_opt_t is used to note which match option was used */ 126 typedef enum { 127 MATCH_OPT_NAME, /* Record contains a name */ 128 MATCH_OPT_NEED_VER, /* Record contains needed object and version */ 129 MATCH_OPT_NDX, /* Record contains a single index */ 130 MATCH_OPT_RANGE, /* Record contains an index range */ 131 } match_opt_t; 132 133 typedef struct { 134 match_opt_t opt_type; 135 union { 136 struct { 137 const char *version; /* MATCH_OPT_{NAME|NEED_VER} */ 138 const char *needobj; /* MATCH_OPT_NEED_VER only */ 139 } name; 140 struct { 141 int start; /* MATCH_OPT_{NDX|RANGE} */ 142 int end; /* MATCH_OPT_RANGE only) */ 143 } ndx; 144 } value; 145 } match_rec_t; 146 147 148 149 static const char *cname; 150 static int Cflag, dflag, lflag, nflag, oflag, rflag, sflag, vflag; 151 static Alist *match_list; 152 153 /* Used to track whether an option defaulted to on, or was explicitly set */ 154 #define DEF_DEFINED 1 155 #define USR_DEFINED 2 156 157 /* 158 * Determine whether a symbol name should be demangled. 159 */ 160 static const char * 161 demangle(const char *name) 162 { 163 if (Cflag) 164 return (Elf_demangle_name(name)); 165 else 166 return (name); 167 } 168 169 /* 170 * Append an item to the specified list, and return a pointer to the list 171 * node created. 172 * 173 * exit: 174 * On success, a new list node is created and the item is 175 * added to the list. On failure, a fatal error is issued 176 * and the process exits. 177 */ 178 static void 179 pvs_aplist_append(APlist **lst, const void *item, const char *file) 180 { 181 if (aplist_append(lst, item, AL_CNT_GVER_DESC) == NULL) { 182 int err = errno; 183 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, file, 184 strerror(err)); 185 exit(1); 186 } 187 } 188 189 /* 190 * Add an entry to match_list for use by match(). This routine is for 191 * use during getopt() processing. 192 * 193 * entry: 194 * opt - One of 'N' or 'I', indicating the option 195 * str - Value string corresponding to opt 196 * 197 * exit: 198 * The new match record has been added. On error, a fatal 199 * error is issued and and the process exits. 200 */ 201 static void 202 add_match_record(int opt, const char *str) 203 { 204 /* 205 * Macros for removing leading and trailing whitespace: 206 * WS_SKIP - Advance _str without passing the NULL termination, 207 * until the first character is not whitespace. 208 * WS_SKIP_LIMIT - Advance _str without passing _limit, 209 * until the first character is not whitespace. 210 * WS_RSKIP_LIMIT - Move _tail back without passing _str, 211 * until the character before it is not whitespace. 212 * Write a NULL termination at that point. 213 */ 214 #define WS_SKIP(_str) for (; *(_str) && isspace(*(_str)); (_str)++) 215 #define WS_SKIP_LIMIT(_str, _limit) \ 216 while (((_str) < s2) && isspace(*(_str))) \ 217 (_str)++ 218 #define WS_RSKIP_LIMIT(_str, _tail) \ 219 while (((_tail) > (_str)) && isspace(*((_tail) - 1))) \ 220 (_tail)--; \ 221 *(_tail) = '\0' 222 223 224 match_rec_t *rec; 225 char *lstr, *s1, *s2; 226 227 rec = alist_append(&match_list, NULL, sizeof (match_rec_t), 228 AL_CNT_MATCH_LIST); 229 if (rec == NULL) { 230 int err = errno; 231 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, 232 MSG_INTL(MSG_STR_MATCH_RECORD), strerror(err)); 233 exit(1); 234 } 235 236 if (opt == 'N') { 237 if ((lstr = strdup(str)) == NULL) { 238 int err = errno; 239 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), 240 cname, MSG_INTL(MSG_STR_MATCH_RECORD), 241 strerror(err)); 242 exit(1); 243 } 244 245 /* Strip leading/trailing whitespace */ 246 s2 = lstr + strlen(lstr); 247 WS_SKIP_LIMIT(lstr, s2); 248 WS_RSKIP_LIMIT(lstr, s2); 249 250 /* Assume this is a plain string */ 251 rec->opt_type = MATCH_OPT_NAME; 252 rec->value.name.version = lstr; 253 254 /* 255 * If s2 points at a closing paren, then this might 256 * be a MATCH_OPT_NEED_VER case. Otherwise we're done. 257 */ 258 if ((s2 == lstr) || (*(s2 - 1) != ')')) 259 return; 260 261 /* We have a closing paren. Locate the opening one. */ 262 for (s1 = lstr; *s1 && (*s1 != '('); s1++) 263 ; 264 if (*s1 != '(') 265 return; 266 267 rec->opt_type = MATCH_OPT_NEED_VER; 268 rec->value.name.needobj = lstr; 269 rec->value.name.version = s1 + 1; 270 s2--; /* Points at closing paren */ 271 272 /* Remove whitespace from head/tail of version */ 273 WS_SKIP_LIMIT(rec->value.name.version, s2); 274 WS_RSKIP_LIMIT(rec->value.name.version, s2); 275 276 /* Terminate needobj, skipping trailing whitespace */ 277 WS_RSKIP_LIMIT(rec->value.name.needobj, s1); 278 279 return; 280 } 281 282 283 /* If we get here, we are looking at a -I index option */ 284 rec->value.ndx.start = strtol(str, &s2, 10); 285 /* Value must use some of the input, and be positive */ 286 if ((str == s2) || (rec->value.ndx.start < 1)) 287 goto syntax_error; 288 str = s2; 289 290 WS_SKIP(str); 291 if (*str != ':') { 292 rec->opt_type = MATCH_OPT_NDX; 293 } else { 294 str++; /* Skip the ':' */ 295 rec->opt_type = MATCH_OPT_RANGE; 296 WS_SKIP(str); 297 if (*str == '\0') { 298 rec->value.ndx.end = -1; /* Indicates "to end" */ 299 } else { 300 rec->value.ndx.end = strtol(str, &s2, 10); 301 if ((str == s2) || (rec->value.ndx.end < 0)) 302 goto syntax_error; 303 str = s2; 304 WS_SKIP(str); 305 } 306 } 307 308 /* If we are successful, there is nothing left to parse */ 309 if (*str == '\0') 310 return; 311 312 /* 313 * If we get here, there is leftover input. Fall through 314 * to issue a syntax error. 315 */ 316 syntax_error: 317 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), cname); 318 exit(1); 319 320 #undef WS_SKIP 321 #undef WS_SKIP_LIMIT 322 #undef WS_RSKIP_LIMIT 323 } 324 325 /* 326 * Returns True (1) if the version with the given name or index should 327 * be displayed, and False (0) if it should not be. 328 * 329 * entry: 330 * needobj - NULL for VERDEF records, the name of the 331 * needed object for VERNEED. 332 * version - NULL, or needed version 333 * ndx - Versym index of version under consideration, or a value less 334 * than 1 to indicate that no valid index is given. 335 * 336 * exit: 337 * True will be returned if the given name/index matches those given 338 * by one of the -I or -N command line options, or if no such option 339 * was used in the command invocation. 340 */ 341 int 342 match(const char *needobj, const char *version, int ndx) 343 { 344 Aliste _idx; 345 match_rec_t *rec; 346 const char *str; 347 348 /* If there is no match list, then we approve everything */ 349 if (alist_nitems(match_list) == 0) 350 return (1); 351 352 /* Run through the match records and check for a hit */ 353 for (ALIST_TRAVERSE(match_list, _idx, rec)) { 354 switch (rec->opt_type) { 355 case MATCH_OPT_NAME: 356 if (needobj) 357 str = needobj; 358 else if (version) 359 str = version; 360 else 361 break; 362 if (strcmp(rec->value.name.version, str) == 0) 363 return (1); 364 break; 365 case MATCH_OPT_NEED_VER: 366 if (needobj && version && 367 (strcmp(rec->value.name.needobj, needobj) == 0) && 368 (strcmp(rec->value.name.version, version) == 0)) 369 return (1); 370 break; 371 case MATCH_OPT_NDX: 372 if ((ndx > 0) && (ndx == rec->value.ndx.start)) 373 return (1); 374 break; 375 case MATCH_OPT_RANGE: 376 /* 377 * A range end value less than 0 means that any value 378 * above the start is acceptible. 379 */ 380 if ((ndx > 0) && 381 (ndx >= rec->value.ndx.start) && 382 ((rec->value.ndx.end < 0) || 383 (ndx <= rec->value.ndx.end))) 384 return (1); 385 break; 386 } 387 } 388 389 /* Nothing matched */ 390 return (0); 391 } 392 393 /* 394 * List the symbols that belong to a specified version 395 * 396 * entry: 397 * vsdata - VERSYM related data from the object 398 * vd_ndx - The VERSYM index for symbols to display 399 * vd_name - Version name 400 * needobj - NULL for symbols corresponding to a VERDEF 401 * record. Name of the needed object in the case 402 * of a VERNEED record. 403 * file - Object file 404 */ 405 static void 406 gvers_syms(const Gver_sym_data *vsdata, GElf_Half vd_ndx, 407 const char *vd_name, const char *needobj, const char *file) 408 { 409 GElf_Sym sym; 410 int _symn; 411 412 for (_symn = 0; _symn < vsdata->vsd_symn; _symn++) { 413 size_t size = 0; 414 const char *name; 415 416 if (vsdata->vsd_vsp[_symn] != vd_ndx) 417 continue; 418 419 (void) gelf_getsym(vsdata->vsd_sym_data, _symn, &sym); 420 name = demangle(vsdata->vsd_strs + sym.st_name); 421 422 /* 423 * Symbols that reference a VERDEF record 424 * have some extra details to handle. 425 */ 426 if (needobj == NULL) { 427 /* 428 * For data symbols defined by this object, 429 * determine the size. 430 */ 431 if ((GELF_ST_TYPE(sym.st_info) == STT_OBJECT) || 432 (GELF_ST_TYPE(sym.st_info) == STT_COMMON) || 433 (GELF_ST_TYPE(sym.st_info) == STT_TLS)) 434 size = (size_t)sym.st_size; 435 436 /* 437 * Only output the version symbol when the verbose 438 * flag is used. 439 */ 440 if (!vflag && (sym.st_shndx == SHN_ABS) && 441 (strcmp(name, vd_name) == 0)) 442 continue; 443 } 444 445 if (oflag) { 446 if (needobj == NULL) 447 (void) printf(MSG_ORIG(MSG_FMT_SYM_OFIL), 448 file, vd_name); 449 else 450 (void) printf(MSG_ORIG(MSG_FMT_SYM_NEED_OFIL), 451 file, needobj, vd_name); 452 453 if (size) 454 (void) printf(MSG_ORIG(MSG_FMT_SYM_SZ_OFLG), 455 name, (ulong_t)size); 456 else 457 (void) printf(MSG_ORIG(MSG_FMT_SYM_OFLG), name); 458 } else { 459 if (size) 460 (void) printf(MSG_ORIG(MSG_FMT_SYM_SZ), name, 461 (ulong_t)size); 462 else 463 (void) printf(MSG_ORIG(MSG_FMT_SYM), name); 464 } 465 } 466 } 467 468 /* 469 * Print any reduced symbols. The convention is that reduced symbols exist as 470 * LOCL entries in the .symtab, between the FILE symbol for the output file and 471 * the first FILE symbol for any input file used to build the output file. 472 */ 473 static void 474 sym_local(Cache *cache, Cache *csym, const char *file) 475 { 476 int symn, _symn, found = 0; 477 GElf_Shdr shdr; 478 GElf_Sym sym; 479 char *strs; 480 481 (void) gelf_getshdr(csym->c_scn, &shdr); 482 strs = (char *)cache[shdr.sh_link].c_data->d_buf; 483 /* LINTED */ 484 symn = shdr.sh_info; 485 486 /* 487 * Verify symtab[1] is the output file symbol. 488 */ 489 (void) gelf_getsym(csym->c_data, 1, &sym); 490 if (GELF_ST_TYPE(sym.st_info) != STT_FILE) { 491 (void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS), cname, 492 file); 493 (void) fprintf(stderr, MSG_INTL(MSG_VER_NOTSTTFILE), 494 csym->c_name); 495 return; 496 } 497 498 /* 499 * Scan the remaining symbols until the next file symbol is found. 500 */ 501 for (_symn = 2; _symn < symn; _symn++) { 502 const char *name; 503 504 (void) gelf_getsym(csym->c_data, _symn, &sym); 505 if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) 506 continue; 507 if (GELF_ST_TYPE(sym.st_info) == STT_FILE) 508 break; 509 510 /* 511 * Its possible that section symbols are followed immediately 512 * by globals. This is the case if an object (filter) is 513 * generated exclusively from mapfile symbol definitions. 514 */ 515 if (GELF_ST_BIND(sym.st_info) != STB_LOCAL) 516 break; 517 518 name = demangle(strs + sym.st_name); 519 520 if (oflag) { 521 (void) printf(MSG_ORIG(MSG_FMT_LOCSYM_OFLG), 522 file, name); 523 } else { 524 if (found == 0) { 525 found = 1; 526 (void) printf(MSG_ORIG(MSG_FMT_LOCSYM_HDR)); 527 } 528 (void) printf(MSG_ORIG(MSG_FMT_LOCSYM), name); 529 } 530 } 531 } 532 533 /* 534 * Print data from the files VERNEED section. 535 * 536 * If we have been asked to display symbols, then the 537 * output format follows that used for verdef sections, 538 * with each version displayed separately. For instance: 539 * 540 * libc.so.1 (SUNW_1.7): 541 * sym1; 542 * sym2; 543 * libc.so.1 (SUNW_1.9): 544 * sym3; 545 * 546 * If we are not displaying symbols, then a terse format 547 * is used, which combines all the needed versions from 548 * a given object into a single line. In this case, the 549 * versions are shown whether or not they contribute symbols. 550 * 551 * libc.so.1 (SUNW_1.7, SUNW_1.9); 552 */ 553 static int 554 gvers_need(Cache *cache, Cache *need, const Gver_sym_data *vsdata, 555 const char *file) 556 { 557 unsigned int num, _num; 558 char *strs; 559 GElf_Verneed *vnd = need->c_data->d_buf; 560 GElf_Shdr shdr; 561 int error = 0; 562 int show = vflag || (vsdata == NULL) || !oflag; 563 564 565 (void) gelf_getshdr(need->c_scn, &shdr); 566 567 /* 568 * Verify the version revision. We only check the first version 569 * structure as it is assumed all other version structures in this 570 * data section will be of the same revision. 571 */ 572 if (vnd->vn_version > VER_DEF_CURRENT) 573 (void) fprintf(stderr, MSG_INTL(MSG_VER_HIGHREV), cname, file, 574 vnd->vn_version, VER_DEF_CURRENT); 575 576 /* 577 * Get the data buffer for the associated string table. 578 */ 579 strs = (char *)cache[shdr.sh_link].c_data->d_buf; 580 num = shdr.sh_info; 581 582 for (_num = 1; _num <= num; _num++, 583 vnd = (GElf_Verneed *)((uintptr_t)vnd + vnd->vn_next)) { 584 GElf_Vernaux *vnap; 585 Word ndx; 586 const char *needobj, *dep; 587 int started = 0, listcnt = 0; 588 589 vnap = (GElf_Vernaux *) ((uintptr_t)vnd + vnd->vn_aux); 590 591 /* Obtain the needed object file name */ 592 needobj = (char *)(strs + vnd->vn_file); 593 594 error = 1; 595 596 /* Process the versions needed from this object */ 597 for (ndx = 0; ndx < vnd->vn_cnt; ndx++, 598 vnap = (GElf_Vernaux *)((uintptr_t)vnap + vnap->vna_next)) { 599 Conv_ver_flags_buf_t ver_flags_buf; 600 601 dep = (char *)(strs + vnap->vna_name); 602 603 if (!match(needobj, dep, vnap->vna_other)) 604 continue; 605 606 if (show) { 607 if ((started == 0) || (vsdata != NULL)) { 608 /* 609 * If one-line ouput is called for 610 * display the filename being processed. 611 */ 612 if (oflag && show) 613 (void) printf( 614 MSG_ORIG(MSG_FMT_OFIL), 615 file); 616 617 (void) printf( 618 MSG_ORIG(MSG_FMT_LIST_BEGIN), 619 needobj); 620 started = 1; 621 } 622 623 /* 624 * If not showing symbols, only show INFO 625 * versions in verbose mode. They don't 626 * actually contribute to the version 627 * interface as seen by rtld, so listing them 628 * without qualification can be misleading. 629 */ 630 if (vflag || (vsdata != NULL) || 631 (alist_nitems(match_list) != 0) || 632 !(vnap->vna_flags & VER_FLG_INFO)) { 633 const char *fmt = (listcnt == 0) ? 634 MSG_ORIG(MSG_FMT_LIST_FIRST) : 635 MSG_ORIG(MSG_FMT_LIST_NEXT); 636 637 if (vsdata == NULL) 638 listcnt++; 639 (void) printf(fmt, dep); 640 641 /* Show non-zero flags */ 642 if (vflag && (vnap->vna_flags != 0)) 643 (void) printf( 644 MSG_ORIG(MSG_FMT_VER_FLG), 645 conv_ver_flags( 646 vnap->vna_flags, 647 CONV_FMT_NOBKT, 648 &ver_flags_buf)); 649 } 650 if (vsdata != NULL) 651 (void) printf(oflag ? 652 MSG_ORIG(MSG_FMT_LIST_END_SEM) : 653 MSG_ORIG(MSG_FMT_LIST_END_COL)); 654 } 655 656 /* 657 * If we are showing symbols, and vna_other is 658 * non-zero, list them here. 659 * 660 * A value of 0 means that this object uses 661 * traditional Solaris versioning rules, under 662 * which VERSYM does not contain indexes to VERNEED 663 * records. In this case, there is nothing to show. 664 */ 665 if (vsdata && (vnap->vna_other > 0)) 666 gvers_syms(vsdata, vnap->vna_other, 667 dep, needobj, file); 668 } 669 if (show && started && (vsdata == NULL)) 670 (void) printf(MSG_ORIG(MSG_FMT_LIST_END_SEM)); 671 } 672 return (error); 673 } 674 675 /* 676 * Return a GVer_desc descriptor for the given version if one 677 * exists. 678 * 679 * entry: 680 * name - Version name 681 * hash - ELF hash of name 682 * lst - APlist of existing descriptors. 683 * file - Object file containing the version 684 * 685 * exit: 686 * Return the corresponding GVer_desc struct if it 687 * exists, and NULL otherwise. 688 */ 689 static GVer_desc * 690 gvers_find(const char *name, unsigned long hash, APlist *lst) 691 { 692 Aliste idx; 693 GVer_desc *vdp; 694 695 for (APLIST_TRAVERSE(lst, idx, vdp)) 696 if ((vdp->vd_hash == hash) && 697 (strcmp(vdp->vd_name, name) == 0)) 698 return (vdp); 699 700 return (NULL); 701 } 702 703 /* 704 * Return a GVer_desc descriptor for the given version. 705 * 706 * entry: 707 * name - Version name 708 * hash - ELF hash of name 709 * lst - List of existing descriptors. 710 * file - Object file containing the version 711 * 712 * exit: 713 * Return the corresponding GVer_desc struct. If the 714 * descriptor does not already exist, it is created. 715 * On error, a fatal error is issued and the process exits. 716 */ 717 static GVer_desc * 718 gvers_desc(const char *name, unsigned long hash, APlist **lst, const char *file) 719 { 720 GVer_desc *vdp; 721 722 if ((vdp = gvers_find(name, hash, *lst)) == NULL) { 723 if ((vdp = calloc(sizeof (GVer_desc), 1)) == NULL) { 724 int err = errno; 725 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, 726 file, strerror(err)); 727 exit(1); 728 } 729 730 vdp->vd_name = name; 731 vdp->vd_hash = hash; 732 733 pvs_aplist_append(lst, vdp, file); 734 } 735 return (vdp); 736 } 737 738 /* 739 * Insert a version dependency for the given GVer_desc descriptor. 740 * 741 * entry: 742 * name - Dependency version name 743 * hash - ELF hash of name 744 * lst - List of existing descriptors. 745 * vdp - Existing version descriptor to which the dependency 746 * is to be added. 747 * file - Object file containing the version 748 * 749 * exit: 750 * A descriptor for the dependency version is looked up 751 * (created if necessary), and then added to the dependency 752 * list for vdp. Returns the dependency descriptor. On error, 753 * a fatal error is issued and the process exits. 754 */ 755 static GVer_desc * 756 gvers_depend(const char *name, unsigned long hash, GVer_desc *vdp, APlist **lst, 757 const char *file) 758 { 759 GVer_desc *_vdp; 760 761 _vdp = gvers_desc(name, hash, lst, file); 762 pvs_aplist_append(&vdp->vd_deps, _vdp, file); 763 return (vdp); 764 } 765 766 static void 767 gvers_derefer(GVer_desc *vdp, int weak) 768 { 769 Aliste idx; 770 GVer_desc *_vdp; 771 772 /* 773 * If the head of the list was a weak then we only clear out 774 * weak dependencies, but if the head of the list was 'strong' 775 * we clear the REFER bit on all dependencies. 776 */ 777 if ((weak && (vdp->vd_flags & VER_FLG_WEAK)) || (!weak)) 778 vdp->vd_flags &= ~FLG_VER_AVAIL; 779 780 for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp)) 781 gvers_derefer(_vdp, weak); 782 } 783 784 785 static void 786 recurse_syms(const Gver_sym_data *vsdata, GVer_desc *vdp, const char *file) 787 { 788 Aliste idx; 789 GVer_desc *_vdp; 790 791 for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp)) { 792 if (!oflag) 793 (void) printf(MSG_ORIG(MSG_FMT_TNCO), _vdp->vd_name); 794 gvers_syms(vsdata, _vdp->vd_ndx, _vdp->vd_name, NULL, file); 795 if (aplist_nitems(_vdp->vd_deps) != 0) 796 recurse_syms(vsdata, _vdp, file); 797 } 798 } 799 800 801 /* 802 * Print the files version definition sections. 803 */ 804 static int 805 gvers_def(Cache *cache, Cache *def, const Gver_sym_data *vsdata, 806 const char *file) 807 { 808 unsigned int num, _num; 809 char *strs; 810 GElf_Verdef *vdf = def->c_data->d_buf; 811 GElf_Shdr shdr; 812 GVer_desc *vdp, *bvdp = NULL; 813 Aliste idx1; 814 APlist *verdefs = NULL; 815 int error = 0; 816 817 /* 818 * Verify the version revision. We only check the first version 819 * structure as it is assumed all other version structures in this 820 * data section will be of the same revision. 821 */ 822 if (vdf->vd_version > VER_DEF_CURRENT) { 823 (void) fprintf(stderr, MSG_INTL(MSG_VER_HIGHREV), cname, file, 824 vdf->vd_version, VER_DEF_CURRENT); 825 } 826 827 /* 828 * Get the data buffer for the associated string table. 829 */ 830 (void) gelf_getshdr(def->c_scn, &shdr); 831 strs = (char *)cache[shdr.sh_link].c_data->d_buf; 832 num = shdr.sh_info; 833 834 /* 835 * Process the version definitions placing each on a version dependency 836 * list. 837 */ 838 for (_num = 1; _num <= num; _num++, 839 vdf = (GElf_Verdef *)((uintptr_t)vdf + vdf->vd_next)) { 840 GElf_Half cnt = vdf->vd_cnt; 841 GElf_Half ndx = vdf->vd_ndx; 842 GElf_Verdaux *vdap; 843 const char *_name; 844 845 vdap = (GElf_Verdaux *)((uintptr_t)vdf + vdf->vd_aux); 846 847 /* 848 * Determine the version name and any dependencies. 849 */ 850 _name = (char *)(strs + vdap->vda_name); 851 852 vdp = gvers_desc(_name, elf_hash(_name), &verdefs, file); 853 vdp->vd_ndx = ndx; 854 vdp->vd_flags = vdf->vd_flags | FLG_VER_AVAIL; 855 856 vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next); 857 for (cnt--; cnt; cnt--, 858 vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next)) { 859 _name = (char *)(strs + vdap->vda_name); 860 if (gvers_depend(_name, elf_hash(_name), vdp, 861 &verdefs, file) == NULL) 862 return (0); 863 } 864 865 /* 866 * Remember the base version for possible later use. 867 */ 868 if (ndx == VER_NDX_GLOBAL) 869 bvdp = vdp; 870 } 871 872 /* 873 * Normalize the dependency list if required. 874 */ 875 if (nflag) { 876 for (APLIST_TRAVERSE(verdefs, idx1, vdp)) { 877 Aliste idx2; 878 GVer_desc *_vdp; 879 int type = vdp->vd_flags & VER_FLG_WEAK; 880 881 for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) 882 gvers_derefer(_vdp, type); 883 } 884 885 /* 886 * Always dereference the base version. 887 */ 888 if (bvdp) 889 bvdp->vd_flags &= ~FLG_VER_AVAIL; 890 } 891 892 893 /* 894 * Traverse the dependency list and print out the appropriate 895 * information. 896 */ 897 for (APLIST_TRAVERSE(verdefs, idx1, vdp)) { 898 Aliste idx2; 899 GVer_desc *_vdp; 900 int count; 901 902 if (!match(NULL, vdp->vd_name, vdp->vd_ndx)) 903 continue; 904 if ((alist_nitems(match_list) == 0) && 905 !(vdp->vd_flags & FLG_VER_AVAIL)) 906 continue; 907 908 error = 1; 909 910 if (vflag) { 911 /* 912 * If the verbose flag is set determine if this version 913 * has a `weak' attribute, and print any version 914 * dependencies this version inherits. 915 */ 916 if (oflag) 917 (void) printf(MSG_ORIG(MSG_FMT_OFIL), file); 918 (void) printf(MSG_ORIG(MSG_FMT_VER_NAME), vdp->vd_name); 919 if ((vdp->vd_flags & MSK_VER_USER) != 0) { 920 Conv_ver_flags_buf_t ver_flags_buf; 921 922 (void) printf(MSG_ORIG(MSG_FMT_VER_FLG), 923 conv_ver_flags( 924 vdp->vd_flags & MSK_VER_USER, 925 CONV_FMT_NOBKT, &ver_flags_buf)); 926 } 927 928 count = 1; 929 for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) { 930 const char *_name = _vdp->vd_name; 931 932 if (count++ == 1) { 933 934 if (oflag) 935 (void) printf( 936 MSG_ORIG(MSG_FMT_IN_OFLG), 937 _name); 938 else if (vdp->vd_flags & VER_FLG_WEAK) 939 (void) printf( 940 MSG_ORIG(MSG_FMT_IN_WEAK), 941 _name); 942 else 943 (void) printf( 944 MSG_ORIG(MSG_FMT_IN), 945 _name); 946 } else 947 (void) printf( 948 MSG_ORIG(MSG_FMT_LIST_NEXT), _name); 949 } 950 951 if (count != 1) 952 (void) printf(MSG_ORIG(MSG_FMT_IN_END)); 953 954 if (vsdata && !oflag) 955 (void) printf(MSG_ORIG(MSG_FMT_COL_NL)); 956 else 957 (void) printf(MSG_ORIG(MSG_FMT_SEM_NL)); 958 } else { 959 if (vsdata && !oflag) 960 (void) printf(MSG_ORIG(MSG_FMT_TNCO), 961 vdp->vd_name); 962 else if (!vsdata) { 963 if (oflag) 964 (void) printf(MSG_ORIG(MSG_FMT_OFIL), 965 file); 966 (void) printf(MSG_ORIG(MSG_FMT_TNSE), 967 vdp->vd_name); 968 } 969 } 970 971 /* If we are not printing symbols, we're done */ 972 if (vsdata == NULL) 973 continue; 974 975 /* 976 * If a specific version to match has been specified then 977 * display any of its own symbols plus any inherited from 978 * other versions. Otherwise simply print out the symbols 979 * for this version. 980 */ 981 gvers_syms(vsdata, vdp->vd_ndx, vdp->vd_name, NULL, file); 982 if (alist_nitems(match_list) != 0) { 983 recurse_syms(vsdata, vdp, file); 984 985 /* 986 * If the verbose flag is set, and this is not 987 * the base version, then add the base version as a 988 * dependency. 989 */ 990 if (vflag && bvdp && 991 !match(NULL, bvdp->vd_name, bvdp->vd_ndx)) { 992 if (!oflag) 993 (void) printf(MSG_ORIG(MSG_FMT_TNCO), 994 bvdp->vd_name); 995 gvers_syms(vsdata, bvdp->vd_ndx, 996 bvdp->vd_name, NULL, file); 997 } 998 } 999 } 1000 return (error); 1001 } 1002 1003 int 1004 main(int argc, char **argv, char **envp) 1005 { 1006 GElf_Shdr shdr; 1007 Elf *elf; 1008 Elf_Scn *scn; 1009 Elf_Data *data; 1010 GElf_Ehdr ehdr; 1011 int nfile, var; 1012 char *names; 1013 Cache *cache, *_cache; 1014 Cache *_cache_def, *_cache_need, *_cache_sym, *_cache_loc; 1015 int error = 0; 1016 Gver_sym_data vsdata_s; 1017 const Gver_sym_data *vsdata = NULL; 1018 1019 /* 1020 * Check for a binary that better fits this architecture. 1021 */ 1022 (void) conv_check_native(argv, envp); 1023 1024 /* 1025 * Establish locale. 1026 */ 1027 (void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY)); 1028 (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS)); 1029 1030 cname = argv[0]; 1031 Cflag = dflag = lflag = nflag = oflag = rflag = sflag = vflag = 0; 1032 1033 opterr = 0; 1034 while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) { 1035 switch (var) { 1036 case 'C': 1037 Cflag = USR_DEFINED; 1038 break; 1039 case 'd': 1040 dflag = USR_DEFINED; 1041 break; 1042 case 'l': 1043 lflag = sflag = USR_DEFINED; 1044 break; 1045 case 'n': 1046 nflag = USR_DEFINED; 1047 break; 1048 case 'o': 1049 oflag = USR_DEFINED; 1050 break; 1051 case 'r': 1052 rflag = USR_DEFINED; 1053 break; 1054 case 's': 1055 sflag = USR_DEFINED; 1056 break; 1057 case 'v': 1058 vflag = USR_DEFINED; 1059 break; 1060 case 'I': 1061 case 'N': 1062 add_match_record(var, optarg); 1063 break; 1064 case '?': 1065 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), 1066 cname); 1067 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL)); 1068 exit(1); 1069 default: 1070 break; 1071 } 1072 } 1073 1074 /* 1075 * No files specified on the command line? 1076 */ 1077 if ((nfile = argc - optind) == 0) { 1078 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), cname); 1079 exit(1); 1080 } 1081 1082 /* 1083 * By default print both version definitions and needed dependencies. 1084 */ 1085 if ((dflag == 0) && (rflag == 0) && (lflag == 0)) 1086 dflag = rflag = DEF_DEFINED; 1087 1088 /* 1089 * Open the input file and initialize the elf interface. 1090 */ 1091 for (; optind < argc; optind++) { 1092 int derror = 0, nerror = 0, err; 1093 const char *file = argv[optind]; 1094 size_t shnum = 0; 1095 1096 if ((var = open(file, O_RDONLY)) == -1) { 1097 err = errno; 1098 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 1099 cname, file, strerror(err)); 1100 error = 1; 1101 continue; 1102 } 1103 (void) elf_version(EV_CURRENT); 1104 if ((elf = elf_begin(var, ELF_C_READ, NULL)) == NULL) { 1105 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_BEGIN), cname, 1106 file, elf_errmsg(elf_errno())); 1107 error = 1; 1108 (void) close(var); 1109 continue; 1110 } 1111 if (elf_kind(elf) != ELF_K_ELF) { 1112 (void) fprintf(stderr, MSG_INTL(MSG_ELF_NOTELF), cname, 1113 file); 1114 error = 1; 1115 (void) close(var); 1116 (void) elf_end(elf); 1117 continue; 1118 } 1119 if (gelf_getehdr(elf, &ehdr) == NULL) { 1120 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETEHDR), cname, 1121 file, elf_errmsg(elf_errno())); 1122 error = 1; 1123 (void) close(var); 1124 (void) elf_end(elf); 1125 continue; 1126 } 1127 1128 /* 1129 * Obtain the .shstrtab data buffer to provide the required 1130 * section name strings. 1131 */ 1132 if ((scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL) { 1133 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETSCN), cname, 1134 file, elf_errmsg(elf_errno())); 1135 error = 1; 1136 (void) close(var); 1137 (void) elf_end(elf); 1138 continue; 1139 } 1140 if ((data = elf_getdata(scn, NULL)) == NULL) { 1141 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETDATA), cname, 1142 file, elf_errmsg(elf_errno())); 1143 error = 1; 1144 (void) close(var); 1145 (void) elf_end(elf); 1146 continue; 1147 } 1148 names = data->d_buf; 1149 1150 /* 1151 * Fill in the cache descriptor with information for each 1152 * section we might need. We probably only need to save 1153 * read-only allocable sections as this is where the version 1154 * structures and their associated symbols and strings live. 1155 * However, God knows what someone can do with a mapfile, and 1156 * as elf_begin has already gone through all the overhead we 1157 * might as well set up the cache for every section. 1158 */ 1159 if (elf_getshdrnum(elf, &shnum) == -1) { 1160 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETSHDRNUM), 1161 cname, file, elf_errmsg(elf_errno())); 1162 exit(1); 1163 } 1164 1165 if ((cache = calloc(shnum, sizeof (Cache))) == NULL) { 1166 int err = errno; 1167 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, 1168 file, strerror(err)); 1169 exit(1); 1170 } 1171 1172 _cache_def = _cache_need = _cache_sym = _cache_loc = NULL; 1173 _cache = cache; 1174 _cache++; 1175 for (scn = NULL; scn = elf_nextscn(elf, scn); _cache++) { 1176 if (gelf_getshdr(scn, &shdr) == NULL) { 1177 (void) fprintf(stderr, 1178 MSG_ORIG(MSG_ELF_GETSHDR), cname, file, 1179 elf_errmsg(elf_errno())); 1180 error = 1; 1181 continue; 1182 } 1183 if ((_cache->c_data = elf_getdata(scn, NULL)) == 1184 NULL) { 1185 (void) fprintf(stderr, 1186 MSG_ORIG(MSG_ELF_GETDATA), cname, file, 1187 elf_errmsg(elf_errno())); 1188 error = 1; 1189 continue; 1190 } 1191 _cache->c_scn = scn; 1192 _cache->c_name = names + shdr.sh_name; 1193 1194 /* 1195 * Remember the version sections and symbol table. 1196 */ 1197 switch (shdr.sh_type) { 1198 case SHT_SUNW_verdef: 1199 if (dflag) 1200 _cache_def = _cache; 1201 break; 1202 case SHT_SUNW_verneed: 1203 if (rflag) 1204 _cache_need = _cache; 1205 break; 1206 case SHT_SUNW_versym: 1207 if (sflag) 1208 _cache_sym = _cache; 1209 break; 1210 case SHT_SYMTAB: 1211 if (lflag) 1212 _cache_loc = _cache; 1213 break; 1214 } 1215 } 1216 1217 /* 1218 * Before printing anything out determine if any warnings are 1219 * necessary. 1220 */ 1221 if (lflag && (_cache_loc == NULL)) { 1222 (void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS), 1223 cname, file); 1224 (void) fprintf(stderr, MSG_INTL(MSG_VER_NOSYMTAB)); 1225 } 1226 1227 /* 1228 * If there is more than one input file, and we're not printing 1229 * one-line output, display the filename being processed. 1230 */ 1231 if ((nfile > 1) && !oflag) 1232 (void) printf(MSG_ORIG(MSG_FMT_FILE), file); 1233 1234 /* 1235 * If we're printing symbols, then collect the data 1236 * necessary to do that. 1237 */ 1238 if (_cache_sym != NULL) { 1239 vsdata = &vsdata_s; 1240 (void) gelf_getshdr(_cache_sym->c_scn, &shdr); 1241 vsdata_s.vsd_vsp = 1242 (GElf_Versym *)_cache_sym->c_data->d_buf; 1243 vsdata_s.vsd_sym_data = cache[shdr.sh_link].c_data; 1244 (void) gelf_getshdr(cache[shdr.sh_link].c_scn, &shdr); 1245 vsdata_s.vsd_symn = shdr.sh_size / shdr.sh_entsize; 1246 vsdata_s.vsd_strs = 1247 (const char *)cache[shdr.sh_link].c_data->d_buf; 1248 } 1249 1250 1251 /* 1252 * Print the files version needed sections. 1253 */ 1254 if (_cache_need) 1255 nerror = gvers_need(cache, _cache_need, vsdata, file); 1256 1257 /* 1258 * Print the files version definition sections. 1259 */ 1260 if (_cache_def) 1261 derror = gvers_def(cache, _cache_def, vsdata, file); 1262 1263 /* 1264 * Print any local symbol reductions. 1265 */ 1266 if (_cache_loc) 1267 sym_local(cache, _cache_loc, file); 1268 1269 /* 1270 * Determine the error return. There are three conditions that 1271 * may produce an error (a non-zero return): 1272 * 1273 * o if the user specified -d and no version definitions 1274 * were found. 1275 * 1276 * o if the user specified -r and no version requirements 1277 * were found. 1278 * 1279 * o if the user specified neither -d or -r, (thus both are 1280 * enabled by default), and no version definitions or 1281 * version dependencies were found. 1282 */ 1283 if (((dflag == USR_DEFINED) && (derror == 0)) || 1284 ((rflag == USR_DEFINED) && (nerror == 0)) || 1285 (rflag && dflag && (derror == 0) && (nerror == 0))) 1286 error = 1; 1287 1288 (void) close(var); 1289 (void) elf_end(elf); 1290 free(cache); 1291 } 1292 return (error); 1293 } 1294 1295 const char * 1296 _pvs_msg(Msg mid) 1297 { 1298 return (gettext(MSG_ORIG(mid))); 1299 } 1300