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 }