Home | History | Annotate | Download | only in common
      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