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 2008 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, *fmt; 586 int started = 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 620 fmt = MSG_ORIG(MSG_FMT_LIST_FIRST); 621 started = 1; 622 } else { 623 fmt = MSG_ORIG(MSG_FMT_LIST_NEXT); 624 } 625 626 /* 627 * If not showing symbols, only show INFO 628 * versions in verbose mode. They don't 629 * actually contribute to the version 630 * interface as seen by rtld, so listing them 631 * without qualification can be misleading. 632 */ 633 if (vflag || (vsdata != NULL) || 634 (alist_nitems(match_list) != 0) || 635 !(vnap->vna_flags & VER_FLG_INFO)) { 636 (void) printf(fmt, dep); 637 638 /* Show non-zero flags */ 639 if (vflag && (vnap->vna_flags != 0)) 640 (void) printf( 641 MSG_ORIG(MSG_FMT_VER_FLG), 642 conv_ver_flags( 643 vnap->vna_flags, 644 CONV_FMT_NOBKT, 645 &ver_flags_buf)); 646 } 647 if (vsdata != NULL) 648 (void) printf(oflag ? 649 MSG_ORIG(MSG_FMT_LIST_END_SEM) : 650 MSG_ORIG(MSG_FMT_LIST_END_COL)); 651 } 652 653 /* 654 * If we are showing symbols, and vna_other is 655 * non-zero, list them here. 656 * 657 * A value of 0 means that this object uses 658 * traditional Solaris versioning rules, under 659 * which VERSYM does not contain indexes to VERNEED 660 * records. In this case, there is nothing to show. 661 */ 662 if (vsdata && (vnap->vna_other > 0)) 663 gvers_syms(vsdata, vnap->vna_other, 664 dep, needobj, file); 665 } 666 if (show && started && (vsdata == NULL)) 667 (void) printf(MSG_ORIG(MSG_FMT_LIST_END_SEM)); 668 } 669 return (error); 670 } 671 672 /* 673 * Return a GVer_desc descriptor for the given version if one 674 * exists. 675 * 676 * entry: 677 * name - Version name 678 * hash - ELF hash of name 679 * lst - APlist of existing descriptors. 680 * file - Object file containing the version 681 * 682 * exit: 683 * Return the corresponding GVer_desc struct if it 684 * exists, and NULL otherwise. 685 */ 686 static GVer_desc * 687 gvers_find(const char *name, unsigned long hash, APlist *lst) 688 { 689 Aliste idx; 690 GVer_desc *vdp; 691 692 for (APLIST_TRAVERSE(lst, idx, vdp)) 693 if ((vdp->vd_hash == hash) && 694 (strcmp(vdp->vd_name, name) == 0)) 695 return (vdp); 696 697 return (NULL); 698 } 699 700 /* 701 * Return a GVer_desc descriptor for the given version. 702 * 703 * entry: 704 * name - Version name 705 * hash - ELF hash of name 706 * lst - List of existing descriptors. 707 * file - Object file containing the version 708 * 709 * exit: 710 * Return the corresponding GVer_desc struct. If the 711 * descriptor does not already exist, it is created. 712 * On error, a fatal error is issued and the process exits. 713 */ 714 static GVer_desc * 715 gvers_desc(const char *name, unsigned long hash, APlist **lst, const char *file) 716 { 717 GVer_desc *vdp; 718 719 if ((vdp = gvers_find(name, hash, *lst)) == NULL) { 720 if ((vdp = calloc(sizeof (GVer_desc), 1)) == NULL) { 721 int err = errno; 722 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, 723 file, strerror(err)); 724 exit(1); 725 } 726 727 vdp->vd_name = name; 728 vdp->vd_hash = hash; 729 730 pvs_aplist_append(lst, vdp, file); 731 } 732 return (vdp); 733 } 734 735 /* 736 * Insert a version dependency for the given GVer_desc descriptor. 737 * 738 * entry: 739 * name - Dependency version name 740 * hash - ELF hash of name 741 * lst - List of existing descriptors. 742 * vdp - Existing version descriptor to which the dependency 743 * is to be added. 744 * file - Object file containing the version 745 * 746 * exit: 747 * A descriptor for the dependency version is looked up 748 * (created if necessary), and then added to the dependency 749 * list for vdp. Returns the dependency descriptor. On error, 750 * a fatal error is issued and the process exits. 751 */ 752 static GVer_desc * 753 gvers_depend(const char *name, unsigned long hash, GVer_desc *vdp, APlist **lst, 754 const char *file) 755 { 756 GVer_desc *_vdp; 757 758 _vdp = gvers_desc(name, hash, lst, file); 759 pvs_aplist_append(&vdp->vd_deps, _vdp, file); 760 return (vdp); 761 } 762 763 static void 764 gvers_derefer(GVer_desc *vdp, int weak) 765 { 766 Aliste idx; 767 GVer_desc *_vdp; 768 769 /* 770 * If the head of the list was a weak then we only clear out 771 * weak dependencies, but if the head of the list was 'strong' 772 * we clear the REFER bit on all dependencies. 773 */ 774 if ((weak && (vdp->vd_flags & VER_FLG_WEAK)) || (!weak)) 775 vdp->vd_flags &= ~FLG_VER_AVAIL; 776 777 for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp)) 778 gvers_derefer(_vdp, weak); 779 } 780 781 782 static void 783 recurse_syms(const Gver_sym_data *vsdata, GVer_desc *vdp, const char *file) 784 { 785 Aliste idx; 786 GVer_desc *_vdp; 787 788 for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp)) { 789 if (!oflag) 790 (void) printf(MSG_ORIG(MSG_FMT_TNCO), _vdp->vd_name); 791 gvers_syms(vsdata, _vdp->vd_ndx, _vdp->vd_name, NULL, file); 792 if (aplist_nitems(_vdp->vd_deps) != 0) 793 recurse_syms(vsdata, _vdp, file); 794 } 795 } 796 797 798 /* 799 * Print the files version definition sections. 800 */ 801 static int 802 gvers_def(Cache *cache, Cache *def, const Gver_sym_data *vsdata, 803 const char *file) 804 { 805 unsigned int num, _num; 806 char *strs; 807 GElf_Verdef *vdf = def->c_data->d_buf; 808 GElf_Shdr shdr; 809 GVer_desc *vdp, *bvdp = NULL; 810 Aliste idx1; 811 APlist *verdefs = NULL; 812 int error = 0; 813 814 /* 815 * Verify the version revision. We only check the first version 816 * structure as it is assumed all other version structures in this 817 * data section will be of the same revision. 818 */ 819 if (vdf->vd_version > VER_DEF_CURRENT) { 820 (void) fprintf(stderr, MSG_INTL(MSG_VER_HIGHREV), cname, file, 821 vdf->vd_version, VER_DEF_CURRENT); 822 } 823 824 /* 825 * Get the data buffer for the associated string table. 826 */ 827 (void) gelf_getshdr(def->c_scn, &shdr); 828 strs = (char *)cache[shdr.sh_link].c_data->d_buf; 829 num = shdr.sh_info; 830 831 /* 832 * Process the version definitions placing each on a version dependency 833 * list. 834 */ 835 for (_num = 1; _num <= num; _num++, 836 vdf = (GElf_Verdef *)((uintptr_t)vdf + vdf->vd_next)) { 837 GElf_Half cnt = vdf->vd_cnt; 838 GElf_Half ndx = vdf->vd_ndx; 839 GElf_Verdaux *vdap; 840 const char *_name; 841 842 vdap = (GElf_Verdaux *)((uintptr_t)vdf + vdf->vd_aux); 843 844 /* 845 * Determine the version name and any dependencies. 846 */ 847 _name = (char *)(strs + vdap->vda_name); 848 849 vdp = gvers_desc(_name, elf_hash(_name), &verdefs, file); 850 vdp->vd_ndx = ndx; 851 vdp->vd_flags = vdf->vd_flags | FLG_VER_AVAIL; 852 853 vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next); 854 for (cnt--; cnt; cnt--, 855 vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next)) { 856 _name = (char *)(strs + vdap->vda_name); 857 if (gvers_depend(_name, elf_hash(_name), vdp, 858 &verdefs, file) == NULL) 859 return (0); 860 } 861 862 /* 863 * Remember the base version for possible later use. 864 */ 865 if (ndx == VER_NDX_GLOBAL) 866 bvdp = vdp; 867 } 868 869 /* 870 * Normalize the dependency list if required. 871 */ 872 if (nflag) { 873 for (APLIST_TRAVERSE(verdefs, idx1, vdp)) { 874 Aliste idx2; 875 GVer_desc *_vdp; 876 int type = vdp->vd_flags & VER_FLG_WEAK; 877 878 for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) 879 gvers_derefer(_vdp, type); 880 } 881 882 /* 883 * Always dereference the base version. 884 */ 885 if (bvdp) 886 bvdp->vd_flags &= ~FLG_VER_AVAIL; 887 } 888 889 890 /* 891 * Traverse the dependency list and print out the appropriate 892 * information. 893 */ 894 for (APLIST_TRAVERSE(verdefs, idx1, vdp)) { 895 Aliste idx2; 896 GVer_desc *_vdp; 897 int count; 898 899 if (!match(NULL, vdp->vd_name, vdp->vd_ndx)) 900 continue; 901 if ((alist_nitems(match_list) == 0) && 902 !(vdp->vd_flags & FLG_VER_AVAIL)) 903 continue; 904 905 error = 1; 906 907 if (vflag) { 908 /* 909 * If the verbose flag is set determine if this version 910 * has a `weak' attribute, and print any version 911 * dependencies this version inherits. 912 */ 913 if (oflag) 914 (void) printf(MSG_ORIG(MSG_FMT_OFIL), file); 915 (void) printf(MSG_ORIG(MSG_FMT_VER_NAME), vdp->vd_name); 916 if ((vdp->vd_flags & MSK_VER_USER) != 0) { 917 Conv_ver_flags_buf_t ver_flags_buf; 918 919 (void) printf(MSG_ORIG(MSG_FMT_VER_FLG), 920 conv_ver_flags( 921 vdp->vd_flags & MSK_VER_USER, 922 CONV_FMT_NOBKT, &ver_flags_buf)); 923 } 924 925 count = 1; 926 for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) { 927 const char *_name = _vdp->vd_name; 928 929 if (count++ == 1) { 930 931 if (oflag) 932 (void) printf( 933 MSG_ORIG(MSG_FMT_IN_OFLG), 934 _name); 935 else if (vdp->vd_flags & VER_FLG_WEAK) 936 (void) printf( 937 MSG_ORIG(MSG_FMT_IN_WEAK), 938 _name); 939 else 940 (void) printf( 941 MSG_ORIG(MSG_FMT_IN), 942 _name); 943 } else 944 (void) printf( 945 MSG_ORIG(MSG_FMT_LIST_NEXT), _name); 946 } 947 948 if (count != 1) 949 (void) printf(MSG_ORIG(MSG_FMT_IN_END)); 950 951 if (vsdata && !oflag) 952 (void) printf(MSG_ORIG(MSG_FMT_COL_NL)); 953 else 954 (void) printf(MSG_ORIG(MSG_FMT_SEM_NL)); 955 } else { 956 if (vsdata && !oflag) 957 (void) printf(MSG_ORIG(MSG_FMT_TNCO), 958 vdp->vd_name); 959 else if (!vsdata) { 960 if (oflag) 961 (void) printf(MSG_ORIG(MSG_FMT_OFIL), 962 file); 963 (void) printf(MSG_ORIG(MSG_FMT_TNSE), 964 vdp->vd_name); 965 } 966 } 967 968 /* If we are not printing symbols, we're done */ 969 if (vsdata == NULL) 970 continue; 971 972 /* 973 * If a specific version to match has been specified then 974 * display any of its own symbols plus any inherited from 975 * other versions. Otherwise simply print out the symbols 976 * for this version. 977 */ 978 gvers_syms(vsdata, vdp->vd_ndx, vdp->vd_name, NULL, file); 979 if (alist_nitems(match_list) != 0) { 980 recurse_syms(vsdata, vdp, file); 981 982 /* 983 * If the verbose flag is set, and this is not 984 * the base version, then add the base version as a 985 * dependency. 986 */ 987 if (vflag && bvdp && 988 !match(NULL, bvdp->vd_name, bvdp->vd_ndx)) { 989 if (!oflag) 990 (void) printf(MSG_ORIG(MSG_FMT_TNCO), 991 bvdp->vd_name); 992 gvers_syms(vsdata, bvdp->vd_ndx, 993 bvdp->vd_name, NULL, file); 994 } 995 } 996 } 997 return (error); 998 } 999 1000 int 1001 main(int argc, char **argv, char **envp) 1002 { 1003 GElf_Shdr shdr; 1004 Elf *elf; 1005 Elf_Scn *scn; 1006 Elf_Data *data; 1007 GElf_Ehdr ehdr; 1008 int nfile, var; 1009 char *names; 1010 Cache *cache, *_cache; 1011 Cache *_cache_def, *_cache_need, *_cache_sym, *_cache_loc; 1012 int error = 0; 1013 Gver_sym_data vsdata_s; 1014 const Gver_sym_data *vsdata = NULL; 1015 1016 /* 1017 * Check for a binary that better fits this architecture. 1018 */ 1019 (void) conv_check_native(argv, envp); 1020 1021 /* 1022 * Establish locale. 1023 */ 1024 (void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY)); 1025 (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS)); 1026 1027 cname = argv[0]; 1028 Cflag = dflag = lflag = nflag = oflag = rflag = sflag = vflag = 0; 1029 1030 opterr = 0; 1031 while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) { 1032 switch (var) { 1033 case 'C': 1034 Cflag = USR_DEFINED; 1035 break; 1036 case 'd': 1037 dflag = USR_DEFINED; 1038 break; 1039 case 'l': 1040 lflag = sflag = USR_DEFINED; 1041 break; 1042 case 'n': 1043 nflag = USR_DEFINED; 1044 break; 1045 case 'o': 1046 oflag = USR_DEFINED; 1047 break; 1048 case 'r': 1049 rflag = USR_DEFINED; 1050 break; 1051 case 's': 1052 sflag = USR_DEFINED; 1053 break; 1054 case 'v': 1055 vflag = USR_DEFINED; 1056 break; 1057 case 'I': 1058 case 'N': 1059 add_match_record(var, optarg); 1060 break; 1061 case '?': 1062 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), 1063 cname); 1064 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL)); 1065 exit(1); 1066 default: 1067 break; 1068 } 1069 } 1070 1071 /* 1072 * No files specified on the command line? 1073 */ 1074 if ((nfile = argc - optind) == 0) { 1075 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), cname); 1076 exit(1); 1077 } 1078 1079 /* 1080 * By default print both version definitions and needed dependencies. 1081 */ 1082 if ((dflag == 0) && (rflag == 0) && (lflag == 0)) 1083 dflag = rflag = DEF_DEFINED; 1084 1085 /* 1086 * Open the input file and initialize the elf interface. 1087 */ 1088 for (; optind < argc; optind++) { 1089 int derror = 0, nerror = 0, err; 1090 const char *file = argv[optind]; 1091 1092 if ((var = open(file, O_RDONLY)) == -1) { 1093 err = errno; 1094 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 1095 cname, file, strerror(err)); 1096 error = 1; 1097 continue; 1098 } 1099 (void) elf_version(EV_CURRENT); 1100 if ((elf = elf_begin(var, ELF_C_READ, NULL)) == NULL) { 1101 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_BEGIN), cname, 1102 file, elf_errmsg(elf_errno())); 1103 error = 1; 1104 (void) close(var); 1105 continue; 1106 } 1107 if (elf_kind(elf) != ELF_K_ELF) { 1108 (void) fprintf(stderr, MSG_INTL(MSG_ELF_NOTELF), cname, 1109 file); 1110 error = 1; 1111 (void) close(var); 1112 (void) elf_end(elf); 1113 continue; 1114 } 1115 if (gelf_getehdr(elf, &ehdr) == NULL) { 1116 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETEHDR), cname, 1117 file, elf_errmsg(elf_errno())); 1118 error = 1; 1119 (void) close(var); 1120 (void) elf_end(elf); 1121 continue; 1122 } 1123 1124 /* 1125 * Obtain the .shstrtab data buffer to provide the required 1126 * section name strings. 1127 */ 1128 if ((scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL) { 1129 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETSCN), cname, 1130 file, elf_errmsg(elf_errno())); 1131 error = 1; 1132 (void) close(var); 1133 (void) elf_end(elf); 1134 continue; 1135 } 1136 if ((data = elf_getdata(scn, NULL)) == NULL) { 1137 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETDATA), cname, 1138 file, elf_errmsg(elf_errno())); 1139 error = 1; 1140 (void) close(var); 1141 (void) elf_end(elf); 1142 continue; 1143 } 1144 names = data->d_buf; 1145 1146 /* 1147 * Fill in the cache descriptor with information for each 1148 * section we might need. We probably only need to save 1149 * read-only allocable sections as this is where the version 1150 * structures and their associated symbols and strings live. 1151 * However, God knows what someone can do with a mapfile, and 1152 * as elf_begin has already gone through all the overhead we 1153 * might as well set up the cache for every section. 1154 */ 1155 if ((cache = calloc(ehdr.e_shnum, sizeof (Cache))) == NULL) { 1156 int err = errno; 1157 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, 1158 file, strerror(err)); 1159 exit(1); 1160 } 1161 1162 _cache_def = _cache_need = _cache_sym = _cache_loc = NULL; 1163 _cache = cache; 1164 _cache++; 1165 for (scn = NULL; scn = elf_nextscn(elf, scn); _cache++) { 1166 if (gelf_getshdr(scn, &shdr) == NULL) { 1167 (void) fprintf(stderr, 1168 MSG_ORIG(MSG_ELF_GETSHDR), cname, file, 1169 elf_errmsg(elf_errno())); 1170 error = 1; 1171 continue; 1172 } 1173 if ((_cache->c_data = elf_getdata(scn, NULL)) == 1174 NULL) { 1175 (void) fprintf(stderr, 1176 MSG_ORIG(MSG_ELF_GETDATA), cname, file, 1177 elf_errmsg(elf_errno())); 1178 error = 1; 1179 continue; 1180 } 1181 _cache->c_scn = scn; 1182 _cache->c_name = names + shdr.sh_name; 1183 1184 /* 1185 * Remember the version sections and symbol table. 1186 */ 1187 switch (shdr.sh_type) { 1188 case SHT_SUNW_verdef: 1189 if (dflag) 1190 _cache_def = _cache; 1191 break; 1192 case SHT_SUNW_verneed: 1193 if (rflag) 1194 _cache_need = _cache; 1195 break; 1196 case SHT_SUNW_versym: 1197 if (sflag) 1198 _cache_sym = _cache; 1199 break; 1200 case SHT_SYMTAB: 1201 if (lflag) 1202 _cache_loc = _cache; 1203 break; 1204 } 1205 } 1206 1207 /* 1208 * Before printing anything out determine if any warnings are 1209 * necessary. 1210 */ 1211 if (lflag && (_cache_loc == NULL)) { 1212 (void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS), 1213 cname, file); 1214 (void) fprintf(stderr, MSG_INTL(MSG_VER_NOSYMTAB)); 1215 } 1216 1217 /* 1218 * If there is more than one input file, and we're not printing 1219 * one-line output, display the filename being processed. 1220 */ 1221 if ((nfile > 1) && !oflag) 1222 (void) printf(MSG_ORIG(MSG_FMT_FILE), file); 1223 1224 /* 1225 * If we're printing symbols, then collect the data 1226 * necessary to do that. 1227 */ 1228 if (_cache_sym != NULL) { 1229 vsdata = &vsdata_s; 1230 (void) gelf_getshdr(_cache_sym->c_scn, &shdr); 1231 vsdata_s.vsd_vsp = 1232 (GElf_Versym *)_cache_sym->c_data->d_buf; 1233 vsdata_s.vsd_sym_data = cache[shdr.sh_link].c_data; 1234 (void) gelf_getshdr(cache[shdr.sh_link].c_scn, &shdr); 1235 vsdata_s.vsd_symn = shdr.sh_size / shdr.sh_entsize; 1236 vsdata_s.vsd_strs = 1237 (const char *)cache[shdr.sh_link].c_data->d_buf; 1238 } 1239 1240 1241 /* 1242 * Print the files version needed sections. 1243 */ 1244 if (_cache_need) 1245 nerror = gvers_need(cache, _cache_need, vsdata, file); 1246 1247 /* 1248 * Print the files version definition sections. 1249 */ 1250 if (_cache_def) 1251 derror = gvers_def(cache, _cache_def, vsdata, file); 1252 1253 /* 1254 * Print any local symbol reductions. 1255 */ 1256 if (_cache_loc) 1257 sym_local(cache, _cache_loc, file); 1258 1259 /* 1260 * Determine the error return. There are three conditions that 1261 * may produce an error (a non-zero return): 1262 * 1263 * o if the user specified -d and no version definitions 1264 * were found. 1265 * 1266 * o if the user specified -r and no version requirements 1267 * were found. 1268 * 1269 * o if the user specified neither -d or -r, (thus both are 1270 * enabled by default), and no version definitions or 1271 * version dependencies were found. 1272 */ 1273 if (((dflag == USR_DEFINED) && (derror == 0)) || 1274 ((rflag == USR_DEFINED) && (nerror == 0)) || 1275 (rflag && dflag && (derror == 0) && (nerror == 0))) 1276 error = 1; 1277 1278 (void) close(var); 1279 (void) elf_end(elf); 1280 free(cache); 1281 } 1282 return (error); 1283 } 1284 1285 const char * 1286 _pvs_msg(Msg mid) 1287 { 1288 return (gettext(MSG_ORIG(mid))); 1289 } 1290