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