xref: /onnv-gate/usr/src/cmd/sgs/pvs/common/pvs.c (revision 7682:b04d06fd448f)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51618Srie  * Common Development and Distribution License (the "License").
61618Srie  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211618Srie 
220Sstevel@tonic-gate /*
23*7682SAli.Bahrami@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * Analyze the versioning information within a file.
290Sstevel@tonic-gate  *
300Sstevel@tonic-gate  *   -C		demangle C++ symbol names.
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  *   -d		dump version definitions.
330Sstevel@tonic-gate  *
34*7682SAli.Bahrami@Sun.COM  *   -l		print reduced (local) symbols. Implies -s.
350Sstevel@tonic-gate  *
360Sstevel@tonic-gate  *   -n		normalize any version definitions.
370Sstevel@tonic-gate  *
380Sstevel@tonic-gate  *   -o		dump output in one-line fashion	(more suitable for grep'ing
390Sstevel@tonic-gate  *		and diff'ing).
400Sstevel@tonic-gate  *
410Sstevel@tonic-gate  *   -r		dump the version requirements on library dependencies
420Sstevel@tonic-gate  *
430Sstevel@tonic-gate  *   -s		display the symbols associated with each version definition.
440Sstevel@tonic-gate  *
450Sstevel@tonic-gate  *   -v		verbose output.  With the -r and -d options any WEAK attribute
460Sstevel@tonic-gate  *		is displayed.  With the -d option, any version inheritance,
47*7682SAli.Bahrami@Sun.COM  *		and the base version are displayed.  With the -r option,
48*7682SAli.Bahrami@Sun.COM  *		WEAK and INFO attributes are displayed. With the -s option
49*7682SAli.Bahrami@Sun.COM  *		the version symbol is displayed.
50*7682SAli.Bahrami@Sun.COM  *
51*7682SAli.Bahrami@Sun.COM  *   -I index	only print the specifed version index, or index range.
520Sstevel@tonic-gate  *
530Sstevel@tonic-gate  *   -N name	only print the specifed `name'.
540Sstevel@tonic-gate  */
550Sstevel@tonic-gate #include	<fcntl.h>
560Sstevel@tonic-gate #include	<stdio.h>
570Sstevel@tonic-gate #include	<libelf.h>
580Sstevel@tonic-gate #include	<link.h>
590Sstevel@tonic-gate #include	<stdlib.h>
600Sstevel@tonic-gate #include	<string.h>
610Sstevel@tonic-gate #include	<unistd.h>
620Sstevel@tonic-gate #include	<locale.h>
630Sstevel@tonic-gate #include	<errno.h>
641618Srie #include	<sgs.h>
651618Srie #include	<conv.h>
661618Srie #include	<gelf.h>
671618Srie #include	<debug.h>
68*7682SAli.Bahrami@Sun.COM #include	<ctype.h>
69*7682SAli.Bahrami@Sun.COM #include	<alist.h>
700Sstevel@tonic-gate #include	"msg.h"
710Sstevel@tonic-gate 
72*7682SAli.Bahrami@Sun.COM /*
73*7682SAli.Bahrami@Sun.COM  * Define Alist initialization sizes.
74*7682SAli.Bahrami@Sun.COM  */
75*7682SAli.Bahrami@Sun.COM #define	AL_CNT_MATCH_LIST	5	/* match_list initial alist count */
76*7682SAli.Bahrami@Sun.COM #define	AL_CNT_GVER_DESC	25	/* version tracking descriptors */
770Sstevel@tonic-gate 
780Sstevel@tonic-gate typedef struct cache {
790Sstevel@tonic-gate 	Elf_Scn		*c_scn;
800Sstevel@tonic-gate 	Elf_Data	*c_data;
810Sstevel@tonic-gate 	char		*c_name;
820Sstevel@tonic-gate } Cache;
830Sstevel@tonic-gate 
840Sstevel@tonic-gate typedef struct gver_desc {
850Sstevel@tonic-gate 	const char	*vd_name;
860Sstevel@tonic-gate 	unsigned long	vd_hash;
870Sstevel@tonic-gate 	GElf_Half	vd_ndx;
880Sstevel@tonic-gate 	GElf_Half	vd_flags;
89*7682SAli.Bahrami@Sun.COM 	APlist		*vd_deps;
900Sstevel@tonic-gate } GVer_desc;
910Sstevel@tonic-gate 
92*7682SAli.Bahrami@Sun.COM /* Versym related data used by gvers_syms() */
93*7682SAli.Bahrami@Sun.COM typedef struct {
94*7682SAli.Bahrami@Sun.COM 	GElf_Versym	*vsd_vsp;   	/* ptr to versym data */
95*7682SAli.Bahrami@Sun.COM 	Elf_Data	*vsd_sym_data;	/* ptr to symtab data */
96*7682SAli.Bahrami@Sun.COM 	Word		vsd_symn;	/* # of symbols in symtab */
97*7682SAli.Bahrami@Sun.COM 	const char	*vsd_strs;	/* string table data */
98*7682SAli.Bahrami@Sun.COM } Gver_sym_data;
99*7682SAli.Bahrami@Sun.COM 
100*7682SAli.Bahrami@Sun.COM /*
101*7682SAli.Bahrami@Sun.COM  * Type used to manage -I and -N options:
102*7682SAli.Bahrami@Sun.COM  *
103*7682SAli.Bahrami@Sun.COM  * The -I option specifies a VERSYM index, or index range. The
104*7682SAli.Bahrami@Sun.COM  * result is to select the VERDEF or VERNEED records with
105*7682SAli.Bahrami@Sun.COM  * indexes that match those given.
106*7682SAli.Bahrami@Sun.COM  *
107*7682SAli.Bahrami@Sun.COM  * -N options come in two forms:
108*7682SAli.Bahrami@Sun.COM  *
109*7682SAli.Bahrami@Sun.COM  *	1) name
110*7682SAli.Bahrami@Sun.COM  *	2) needobj (version)
111*7682SAli.Bahrami@Sun.COM  *
112*7682SAli.Bahrami@Sun.COM  * The meaning of the first case depends on the type of
113*7682SAli.Bahrami@Sun.COM  * version record being matched:
114*7682SAli.Bahrami@Sun.COM  *
115*7682SAli.Bahrami@Sun.COM  *	VERDEF - name is the name of a version defined
116*7682SAli.Bahrami@Sun.COM  *		by the object being processed (i.e. SUNW_1.1).
117*7682SAli.Bahrami@Sun.COM  *
118*7682SAli.Bahrami@Sun.COM  *	VERNEED - name is the name of the object file
119*7682SAli.Bahrami@Sun.COM  *		on which the dependency exists (i.e. libc.so.1).
120*7682SAli.Bahrami@Sun.COM  *
121*7682SAli.Bahrami@Sun.COM  * -N options of the second form only apply to VERNEED records.
122*7682SAli.Bahrami@Sun.COM  * They are used to specify a version from a needed object.
123*7682SAli.Bahrami@Sun.COM  */
124*7682SAli.Bahrami@Sun.COM /* match_opt_t is  used to note which match option was used */
125*7682SAli.Bahrami@Sun.COM typedef enum {
126*7682SAli.Bahrami@Sun.COM 	MATCH_OPT_NAME,		/* Record contains a name */
127*7682SAli.Bahrami@Sun.COM 	MATCH_OPT_NEED_VER,	/* Record contains needed object and version */
128*7682SAli.Bahrami@Sun.COM 	MATCH_OPT_NDX,		/* Record contains a single index */
129*7682SAli.Bahrami@Sun.COM 	MATCH_OPT_RANGE,	/* Record contains an index range */
130*7682SAli.Bahrami@Sun.COM } match_opt_t;
131*7682SAli.Bahrami@Sun.COM 
132*7682SAli.Bahrami@Sun.COM typedef struct {
133*7682SAli.Bahrami@Sun.COM 	match_opt_t	opt_type;
134*7682SAli.Bahrami@Sun.COM 	union {
135*7682SAli.Bahrami@Sun.COM 		struct {
136*7682SAli.Bahrami@Sun.COM 			const char *version;	/* MATCH_OPT_{NAME|NEED_VER} */
137*7682SAli.Bahrami@Sun.COM 			const char *needobj;	/* MATCH_OPT_NEED_VER only */
138*7682SAli.Bahrami@Sun.COM 		} name;
139*7682SAli.Bahrami@Sun.COM 		struct {
140*7682SAli.Bahrami@Sun.COM 			int start;		/* MATCH_OPT_{NDX|RANGE} */
141*7682SAli.Bahrami@Sun.COM 			int end;		/* MATCH_OPT_RANGE only) */
142*7682SAli.Bahrami@Sun.COM 		} ndx;
143*7682SAli.Bahrami@Sun.COM 	} value;
144*7682SAli.Bahrami@Sun.COM } match_rec_t;
145*7682SAli.Bahrami@Sun.COM 
146*7682SAli.Bahrami@Sun.COM 
147*7682SAli.Bahrami@Sun.COM 
1480Sstevel@tonic-gate static const char	*cname;
1490Sstevel@tonic-gate static int		Cflag, dflag, lflag, nflag, oflag, rflag, sflag, vflag;
150*7682SAli.Bahrami@Sun.COM static Alist		*match_list;
1510Sstevel@tonic-gate 
152*7682SAli.Bahrami@Sun.COM /* Used to track whether an option defaulted to on, or was explicitly set */
1530Sstevel@tonic-gate #define	DEF_DEFINED	1
1540Sstevel@tonic-gate #define	USR_DEFINED	2
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate  * Determine whether a symbol name should be demangled.
1580Sstevel@tonic-gate  */
1590Sstevel@tonic-gate static const char *
1600Sstevel@tonic-gate demangle(const char *name)
1610Sstevel@tonic-gate {
1620Sstevel@tonic-gate 	if (Cflag)
1631618Srie 		return (Elf_demangle_name(name));
1640Sstevel@tonic-gate 	else
1650Sstevel@tonic-gate 		return (name);
1660Sstevel@tonic-gate }
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate /*
169*7682SAli.Bahrami@Sun.COM  * Append an item to the specified list, and return a pointer to the list
170*7682SAli.Bahrami@Sun.COM  * node created.
171*7682SAli.Bahrami@Sun.COM  *
172*7682SAli.Bahrami@Sun.COM  * exit:
173*7682SAli.Bahrami@Sun.COM  *	On success, a new list node is created and the item is
174*7682SAli.Bahrami@Sun.COM  *	added to the list. On failure, a fatal error is issued
175*7682SAli.Bahrami@Sun.COM  *	and the process exits.
176*7682SAli.Bahrami@Sun.COM  */
177*7682SAli.Bahrami@Sun.COM static void
178*7682SAli.Bahrami@Sun.COM pvs_aplist_append(APlist **lst, const void *item, const char *file)
179*7682SAli.Bahrami@Sun.COM {
180*7682SAli.Bahrami@Sun.COM 	if (aplist_append(lst, item, AL_CNT_GVER_DESC) == NULL) {
181*7682SAli.Bahrami@Sun.COM 		int err = errno;
182*7682SAli.Bahrami@Sun.COM 		(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, file,
183*7682SAli.Bahrami@Sun.COM 		    strerror(err));
184*7682SAli.Bahrami@Sun.COM 		exit(1);
185*7682SAli.Bahrami@Sun.COM 	}
186*7682SAli.Bahrami@Sun.COM }
187*7682SAli.Bahrami@Sun.COM 
188*7682SAli.Bahrami@Sun.COM /*
189*7682SAli.Bahrami@Sun.COM  * Add an entry to match_list for use by match(). This routine is for
190*7682SAli.Bahrami@Sun.COM  * use during getopt() processing.
191*7682SAli.Bahrami@Sun.COM  *
192*7682SAli.Bahrami@Sun.COM  * entry:
193*7682SAli.Bahrami@Sun.COM  *	opt - One of 'N' or 'I', indicating the option
194*7682SAli.Bahrami@Sun.COM  *	str - Value string corresponding to opt
195*7682SAli.Bahrami@Sun.COM  *
196*7682SAli.Bahrami@Sun.COM  * exit:
197*7682SAli.Bahrami@Sun.COM  *	The new match record has been added. On error, a fatal
198*7682SAli.Bahrami@Sun.COM  *	error is issued and and the process exits.
199*7682SAli.Bahrami@Sun.COM  */
200*7682SAli.Bahrami@Sun.COM static void
201*7682SAli.Bahrami@Sun.COM add_match_record(int opt, const char *str)
202*7682SAli.Bahrami@Sun.COM {
203*7682SAli.Bahrami@Sun.COM 	/*
204*7682SAli.Bahrami@Sun.COM 	 * Macros for removing leading and trailing whitespace:
205*7682SAli.Bahrami@Sun.COM 	 *	WS_SKIP - Advance _str without passing the NULL termination,
206*7682SAli.Bahrami@Sun.COM 	 *		until the first character is not whitespace.
207*7682SAli.Bahrami@Sun.COM 	 *	WS_SKIP_LIMIT - Advance _str without passing _limit,
208*7682SAli.Bahrami@Sun.COM 	 *		until the first character is not whitespace.
209*7682SAli.Bahrami@Sun.COM 	 *	WS_RSKIP_LIMIT - Move _tail back without passing _str,
210*7682SAli.Bahrami@Sun.COM 	 *		until the character before it is not whitespace.
211*7682SAli.Bahrami@Sun.COM 	 *		Write a NULL termination at that point.
212*7682SAli.Bahrami@Sun.COM 	 */
213*7682SAli.Bahrami@Sun.COM #define	WS_SKIP(_str) for (; *(_str) && isspace(*(_str)); (_str)++)
214*7682SAli.Bahrami@Sun.COM #define	WS_SKIP_LIMIT(_str, _limit) \
215*7682SAli.Bahrami@Sun.COM 	while (((_str) < s2) && isspace(*(_str))) \
216*7682SAli.Bahrami@Sun.COM 		(_str)++
217*7682SAli.Bahrami@Sun.COM #define	WS_RSKIP_LIMIT(_str, _tail) \
218*7682SAli.Bahrami@Sun.COM 	while (((_tail) > (_str)) && isspace(*((_tail) - 1)))	\
219*7682SAli.Bahrami@Sun.COM 		(_tail)--;					\
220*7682SAli.Bahrami@Sun.COM 	*(_tail) = '\0'
221*7682SAli.Bahrami@Sun.COM 
222*7682SAli.Bahrami@Sun.COM 
223*7682SAli.Bahrami@Sun.COM 	match_rec_t	*rec;
224*7682SAli.Bahrami@Sun.COM 	char		*lstr, *s1, *s2;
225*7682SAli.Bahrami@Sun.COM 
226*7682SAli.Bahrami@Sun.COM 	rec = alist_append(&match_list, NULL, sizeof (match_rec_t),
227*7682SAli.Bahrami@Sun.COM 	    AL_CNT_MATCH_LIST);
228*7682SAli.Bahrami@Sun.COM 	if (rec == NULL) {
229*7682SAli.Bahrami@Sun.COM 		int err = errno;
230*7682SAli.Bahrami@Sun.COM 		(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
231*7682SAli.Bahrami@Sun.COM 		    MSG_INTL(MSG_STR_MATCH_RECORD), strerror(err));
232*7682SAli.Bahrami@Sun.COM 		exit(1);
233*7682SAli.Bahrami@Sun.COM 	}
234*7682SAli.Bahrami@Sun.COM 
235*7682SAli.Bahrami@Sun.COM 	if (opt == 'N') {
236*7682SAli.Bahrami@Sun.COM 		if ((lstr = strdup(str)) == NULL) {
237*7682SAli.Bahrami@Sun.COM 			int err = errno;
238*7682SAli.Bahrami@Sun.COM 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC),
239*7682SAli.Bahrami@Sun.COM 			    cname, MSG_INTL(MSG_STR_MATCH_RECORD),
240*7682SAli.Bahrami@Sun.COM 			    strerror(err));
241*7682SAli.Bahrami@Sun.COM 			exit(1);
242*7682SAli.Bahrami@Sun.COM 		}
243*7682SAli.Bahrami@Sun.COM 
244*7682SAli.Bahrami@Sun.COM 		/* Strip leading/trailing whitespace */
245*7682SAli.Bahrami@Sun.COM 		s2 = lstr + strlen(lstr);
246*7682SAli.Bahrami@Sun.COM 		WS_SKIP_LIMIT(lstr, s2);
247*7682SAli.Bahrami@Sun.COM 		WS_RSKIP_LIMIT(lstr, s2);
248*7682SAli.Bahrami@Sun.COM 
249*7682SAli.Bahrami@Sun.COM 		/* Assume this is a plain string */
250*7682SAli.Bahrami@Sun.COM 		rec->opt_type = MATCH_OPT_NAME;
251*7682SAli.Bahrami@Sun.COM 		rec->value.name.version = lstr;
252*7682SAli.Bahrami@Sun.COM 
253*7682SAli.Bahrami@Sun.COM 		/*
254*7682SAli.Bahrami@Sun.COM 		 * If s2 points at a closing paren, then this might
255*7682SAli.Bahrami@Sun.COM 		 * be a MATCH_OPT_NEED_VER case. Otherwise we're done.
256*7682SAli.Bahrami@Sun.COM 		 */
257*7682SAli.Bahrami@Sun.COM 		if ((s2 == lstr) || (*(s2 - 1) != ')'))
258*7682SAli.Bahrami@Sun.COM 			return;
259*7682SAli.Bahrami@Sun.COM 
260*7682SAli.Bahrami@Sun.COM 		/* We have a closing paren. Locate the opening one. */
261*7682SAli.Bahrami@Sun.COM 		for (s1 = lstr; *s1 && (*s1 != '('); s1++)
262*7682SAli.Bahrami@Sun.COM 			;
263*7682SAli.Bahrami@Sun.COM 		if (*s1 != '(')
264*7682SAli.Bahrami@Sun.COM 			return;
265*7682SAli.Bahrami@Sun.COM 
266*7682SAli.Bahrami@Sun.COM 		rec->opt_type = MATCH_OPT_NEED_VER;
267*7682SAli.Bahrami@Sun.COM 		rec->value.name.needobj = lstr;
268*7682SAli.Bahrami@Sun.COM 		rec->value.name.version = s1 + 1;
269*7682SAli.Bahrami@Sun.COM 		s2--;		/* Points at closing paren */
270*7682SAli.Bahrami@Sun.COM 
271*7682SAli.Bahrami@Sun.COM 		/* Remove whitespace from head/tail of version */
272*7682SAli.Bahrami@Sun.COM 		WS_SKIP_LIMIT(rec->value.name.version, s2);
273*7682SAli.Bahrami@Sun.COM 		WS_RSKIP_LIMIT(rec->value.name.version, s2);
274*7682SAli.Bahrami@Sun.COM 
275*7682SAli.Bahrami@Sun.COM 		/* Terminate needobj, skipping trailing whitespace */
276*7682SAli.Bahrami@Sun.COM 		WS_RSKIP_LIMIT(rec->value.name.needobj, s1);
277*7682SAli.Bahrami@Sun.COM 
278*7682SAli.Bahrami@Sun.COM 		return;
279*7682SAli.Bahrami@Sun.COM 	}
280*7682SAli.Bahrami@Sun.COM 
281*7682SAli.Bahrami@Sun.COM 
282*7682SAli.Bahrami@Sun.COM 	/* If we get here, we are looking at a -I index option */
283*7682SAli.Bahrami@Sun.COM 	rec->value.ndx.start = strtol(str, &s2, 10);
284*7682SAli.Bahrami@Sun.COM 	/* Value must use some of the input, and be positive */
285*7682SAli.Bahrami@Sun.COM 	if ((str == s2) || (rec->value.ndx.start < 1))
286*7682SAli.Bahrami@Sun.COM 		goto syntax_error;
287*7682SAli.Bahrami@Sun.COM 	str = s2;
288*7682SAli.Bahrami@Sun.COM 
289*7682SAli.Bahrami@Sun.COM 	WS_SKIP(str);
290*7682SAli.Bahrami@Sun.COM 	if (*str != ':') {
291*7682SAli.Bahrami@Sun.COM 		rec->opt_type = MATCH_OPT_NDX;
292*7682SAli.Bahrami@Sun.COM 	} else {
293*7682SAli.Bahrami@Sun.COM 		str++;					/* Skip the ':' */
294*7682SAli.Bahrami@Sun.COM 		rec->opt_type = MATCH_OPT_RANGE;
295*7682SAli.Bahrami@Sun.COM 		WS_SKIP(str);
296*7682SAli.Bahrami@Sun.COM 		if (*str == '\0') {
297*7682SAli.Bahrami@Sun.COM 			rec->value.ndx.end = -1;	/* Indicates "to end" */
298*7682SAli.Bahrami@Sun.COM 		} else {
299*7682SAli.Bahrami@Sun.COM 			rec->value.ndx.end = strtol(str, &s2, 10);
300*7682SAli.Bahrami@Sun.COM 			if ((str == s2) || (rec->value.ndx.end < 0))
301*7682SAli.Bahrami@Sun.COM 				goto syntax_error;
302*7682SAli.Bahrami@Sun.COM 			str = s2;
303*7682SAli.Bahrami@Sun.COM 			WS_SKIP(str);
304*7682SAli.Bahrami@Sun.COM 		}
305*7682SAli.Bahrami@Sun.COM 	}
306*7682SAli.Bahrami@Sun.COM 
307*7682SAli.Bahrami@Sun.COM 	/* If we are successful, there is nothing left to parse */
308*7682SAli.Bahrami@Sun.COM 	if (*str == '\0')
309*7682SAli.Bahrami@Sun.COM 		return;
310*7682SAli.Bahrami@Sun.COM 
311*7682SAli.Bahrami@Sun.COM 	/*
312*7682SAli.Bahrami@Sun.COM 	 * If we get here, there is leftover input. Fall through
313*7682SAli.Bahrami@Sun.COM 	 * to issue a syntax error.
314*7682SAli.Bahrami@Sun.COM 	 */
315*7682SAli.Bahrami@Sun.COM syntax_error:
316*7682SAli.Bahrami@Sun.COM 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), cname);
317*7682SAli.Bahrami@Sun.COM 	exit(1);
318*7682SAli.Bahrami@Sun.COM 
319*7682SAli.Bahrami@Sun.COM #undef	WS_SKIP
320*7682SAli.Bahrami@Sun.COM #undef	WS_SKIP_LIMIT
321*7682SAli.Bahrami@Sun.COM #undef	WS_RSKIP_LIMIT
322*7682SAli.Bahrami@Sun.COM }
323*7682SAli.Bahrami@Sun.COM 
324*7682SAli.Bahrami@Sun.COM /*
325*7682SAli.Bahrami@Sun.COM  * Returns True (1) if the version with the given name or index should
326*7682SAli.Bahrami@Sun.COM  * be displayed, and False (0) if it should not be.
327*7682SAli.Bahrami@Sun.COM  *
328*7682SAli.Bahrami@Sun.COM  * entry:
329*7682SAli.Bahrami@Sun.COM  *	needobj - NULL for VERDEF records, the name of the
330*7682SAli.Bahrami@Sun.COM  *		needed object for VERNEED.
331*7682SAli.Bahrami@Sun.COM  *	version - NULL, or needed version
332*7682SAli.Bahrami@Sun.COM  *	ndx - Versym index of version under consideration, or a value less
333*7682SAli.Bahrami@Sun.COM  *		than 1 to indicate that no valid index is given.
334*7682SAli.Bahrami@Sun.COM  *
335*7682SAli.Bahrami@Sun.COM  * exit:
336*7682SAli.Bahrami@Sun.COM  *	True will be returned if the given name/index matches those given
337*7682SAli.Bahrami@Sun.COM  *	by one of the -I or -N command line options, or if no such option
338*7682SAli.Bahrami@Sun.COM  *	was used in the command invocation.
339*7682SAli.Bahrami@Sun.COM  */
340*7682SAli.Bahrami@Sun.COM int
341*7682SAli.Bahrami@Sun.COM match(const char *needobj, const char *version, int ndx)
342*7682SAli.Bahrami@Sun.COM {
343*7682SAli.Bahrami@Sun.COM 	Aliste		_idx;
344*7682SAli.Bahrami@Sun.COM 	match_rec_t	*rec;
345*7682SAli.Bahrami@Sun.COM 	const char	*str;
346*7682SAli.Bahrami@Sun.COM 
347*7682SAli.Bahrami@Sun.COM 	/* If there is no match list, then we approve everything */
348*7682SAli.Bahrami@Sun.COM 	if (alist_nitems(match_list) == 0)
349*7682SAli.Bahrami@Sun.COM 		return (1);
350*7682SAli.Bahrami@Sun.COM 
351*7682SAli.Bahrami@Sun.COM 	/* Run through the match records and check for a hit */
352*7682SAli.Bahrami@Sun.COM 	for (ALIST_TRAVERSE(match_list, _idx, rec)) {
353*7682SAli.Bahrami@Sun.COM 		switch (rec->opt_type) {
354*7682SAli.Bahrami@Sun.COM 		case MATCH_OPT_NAME:
355*7682SAli.Bahrami@Sun.COM 			if (needobj)
356*7682SAli.Bahrami@Sun.COM 				str = needobj;
357*7682SAli.Bahrami@Sun.COM 			else if (version)
358*7682SAli.Bahrami@Sun.COM 				str = version;
359*7682SAli.Bahrami@Sun.COM 			else
360*7682SAli.Bahrami@Sun.COM 				break;
361*7682SAli.Bahrami@Sun.COM 			if (strcmp(rec->value.name.version, str) == 0)
362*7682SAli.Bahrami@Sun.COM 				return (1);
363*7682SAli.Bahrami@Sun.COM 			break;
364*7682SAli.Bahrami@Sun.COM 		case MATCH_OPT_NEED_VER:
365*7682SAli.Bahrami@Sun.COM 			if (needobj && version &&
366*7682SAli.Bahrami@Sun.COM 			    (strcmp(rec->value.name.needobj, needobj) == 0) &&
367*7682SAli.Bahrami@Sun.COM 			    (strcmp(rec->value.name.version, version) == 0))
368*7682SAli.Bahrami@Sun.COM 				return (1);
369*7682SAli.Bahrami@Sun.COM 			break;
370*7682SAli.Bahrami@Sun.COM 		case MATCH_OPT_NDX:
371*7682SAli.Bahrami@Sun.COM 			if ((ndx > 0) && (ndx == rec->value.ndx.start))
372*7682SAli.Bahrami@Sun.COM 				return (1);
373*7682SAli.Bahrami@Sun.COM 			break;
374*7682SAli.Bahrami@Sun.COM 		case MATCH_OPT_RANGE:
375*7682SAli.Bahrami@Sun.COM 			/*
376*7682SAli.Bahrami@Sun.COM 			 * A range end value less than 0 means that any value
377*7682SAli.Bahrami@Sun.COM 			 * above the start is acceptible.
378*7682SAli.Bahrami@Sun.COM 			 */
379*7682SAli.Bahrami@Sun.COM 			if ((ndx > 0) &&
380*7682SAli.Bahrami@Sun.COM 			    (ndx >= rec->value.ndx.start) &&
381*7682SAli.Bahrami@Sun.COM 			    ((rec->value.ndx.end < 0) ||
382*7682SAli.Bahrami@Sun.COM 			    (ndx <= rec->value.ndx.end)))
383*7682SAli.Bahrami@Sun.COM 				return (1);
384*7682SAli.Bahrami@Sun.COM 			break;
385*7682SAli.Bahrami@Sun.COM 		}
386*7682SAli.Bahrami@Sun.COM 	}
387*7682SAli.Bahrami@Sun.COM 
388*7682SAli.Bahrami@Sun.COM 	/* Nothing matched */
389*7682SAli.Bahrami@Sun.COM 	return (0);
390*7682SAli.Bahrami@Sun.COM }
391*7682SAli.Bahrami@Sun.COM 
392*7682SAli.Bahrami@Sun.COM /*
393*7682SAli.Bahrami@Sun.COM  * List the symbols that belong to a specified version
394*7682SAli.Bahrami@Sun.COM  *
395*7682SAli.Bahrami@Sun.COM  * entry:
396*7682SAli.Bahrami@Sun.COM  *	vsdata - VERSYM related data from the object
397*7682SAli.Bahrami@Sun.COM  *	vd_ndx - The VERSYM index for symbols to display
398*7682SAli.Bahrami@Sun.COM  *	vd_name - Version name
399*7682SAli.Bahrami@Sun.COM  *	needobj - NULL for symbols corresponding to a VERDEF
400*7682SAli.Bahrami@Sun.COM  *		record. Name of the needed object in the case
401*7682SAli.Bahrami@Sun.COM  *		of a VERNEED record.
402*7682SAli.Bahrami@Sun.COM  *	file - Object file
403*7682SAli.Bahrami@Sun.COM  */
404*7682SAli.Bahrami@Sun.COM static void
405*7682SAli.Bahrami@Sun.COM gvers_syms(const Gver_sym_data *vsdata, GElf_Half vd_ndx,
406*7682SAli.Bahrami@Sun.COM     const char *vd_name, const char *needobj, const char *file)
407*7682SAli.Bahrami@Sun.COM {
408*7682SAli.Bahrami@Sun.COM 	GElf_Sym	sym;
409*7682SAli.Bahrami@Sun.COM 	int		_symn;
410*7682SAli.Bahrami@Sun.COM 
411*7682SAli.Bahrami@Sun.COM 	for (_symn = 0; _symn < vsdata->vsd_symn; _symn++) {
412*7682SAli.Bahrami@Sun.COM 		size_t		size =	0;
413*7682SAli.Bahrami@Sun.COM 		const char	*name;
414*7682SAli.Bahrami@Sun.COM 
415*7682SAli.Bahrami@Sun.COM 		if (vsdata->vsd_vsp[_symn] != vd_ndx)
416*7682SAli.Bahrami@Sun.COM 			continue;
417*7682SAli.Bahrami@Sun.COM 
418*7682SAli.Bahrami@Sun.COM 		(void) gelf_getsym(vsdata->vsd_sym_data, _symn, &sym);
419*7682SAli.Bahrami@Sun.COM 		name = demangle(vsdata->vsd_strs + sym.st_name);
420*7682SAli.Bahrami@Sun.COM 
421*7682SAli.Bahrami@Sun.COM 		/*
422*7682SAli.Bahrami@Sun.COM 		 * Symbols that reference a VERDEF record
423*7682SAli.Bahrami@Sun.COM 		 * have some extra details to handle.
424*7682SAli.Bahrami@Sun.COM 		 */
425*7682SAli.Bahrami@Sun.COM 		if (needobj == NULL) {
426*7682SAli.Bahrami@Sun.COM 			/*
427*7682SAli.Bahrami@Sun.COM 			 * For data symbols defined by this object,
428*7682SAli.Bahrami@Sun.COM 			 * determine the size.
429*7682SAli.Bahrami@Sun.COM 			 */
430*7682SAli.Bahrami@Sun.COM 			if ((GELF_ST_TYPE(sym.st_info) == STT_OBJECT) ||
431*7682SAli.Bahrami@Sun.COM 			    (GELF_ST_TYPE(sym.st_info) == STT_COMMON) ||
432*7682SAli.Bahrami@Sun.COM 			    (GELF_ST_TYPE(sym.st_info) == STT_TLS))
433*7682SAli.Bahrami@Sun.COM 				size = (size_t)sym.st_size;
434*7682SAli.Bahrami@Sun.COM 
435*7682SAli.Bahrami@Sun.COM 			/*
436*7682SAli.Bahrami@Sun.COM 			 * Only output the version symbol when the verbose
437*7682SAli.Bahrami@Sun.COM 			 * flag is used.
438*7682SAli.Bahrami@Sun.COM 			 */
439*7682SAli.Bahrami@Sun.COM 			if (!vflag && (sym.st_shndx == SHN_ABS) &&
440*7682SAli.Bahrami@Sun.COM 			    (strcmp(name, vd_name) == 0))
441*7682SAli.Bahrami@Sun.COM 				continue;
442*7682SAli.Bahrami@Sun.COM 		}
443*7682SAli.Bahrami@Sun.COM 
444*7682SAli.Bahrami@Sun.COM 		if (oflag) {
445*7682SAli.Bahrami@Sun.COM 			if (needobj == NULL)
446*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_SYM_OFIL),
447*7682SAli.Bahrami@Sun.COM 				    file, vd_name);
448*7682SAli.Bahrami@Sun.COM 			else
449*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_SYM_NEED_OFIL),
450*7682SAli.Bahrami@Sun.COM 				    file, needobj, vd_name);
451*7682SAli.Bahrami@Sun.COM 
452*7682SAli.Bahrami@Sun.COM 			if (size)
453*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_SYM_SZ_OFLG),
454*7682SAli.Bahrami@Sun.COM 				    name, (ulong_t)size);
455*7682SAli.Bahrami@Sun.COM 			else
456*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_SYM_OFLG), name);
457*7682SAli.Bahrami@Sun.COM 		} else {
458*7682SAli.Bahrami@Sun.COM 			if (size)
459*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_SYM_SZ), name,
460*7682SAli.Bahrami@Sun.COM 				    (ulong_t)size);
461*7682SAli.Bahrami@Sun.COM 			else
462*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_SYM), name);
463*7682SAli.Bahrami@Sun.COM 		}
464*7682SAli.Bahrami@Sun.COM 	}
465*7682SAli.Bahrami@Sun.COM }
466*7682SAli.Bahrami@Sun.COM 
467*7682SAli.Bahrami@Sun.COM /*
4680Sstevel@tonic-gate  * Print any reduced symbols.  The convention is that reduced symbols exist as
4690Sstevel@tonic-gate  * LOCL entries in the .symtab, between the FILE symbol for the output file and
4700Sstevel@tonic-gate  * the first FILE symbol for any input file used to build the output file.
4710Sstevel@tonic-gate  */
4720Sstevel@tonic-gate static void
4730Sstevel@tonic-gate sym_local(Cache *cache, Cache *csym, const char *file)
4740Sstevel@tonic-gate {
4750Sstevel@tonic-gate 	int		symn, _symn, found = 0;
4760Sstevel@tonic-gate 	GElf_Shdr	shdr;
4770Sstevel@tonic-gate 	GElf_Sym	sym;
478*7682SAli.Bahrami@Sun.COM 	char		*strs;
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	(void) gelf_getshdr(csym->c_scn, &shdr);
4810Sstevel@tonic-gate 	strs = (char *)cache[shdr.sh_link].c_data->d_buf;
4820Sstevel@tonic-gate 	/* LINTED */
4830Sstevel@tonic-gate 	symn = shdr.sh_info;
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	/*
4860Sstevel@tonic-gate 	 * Verify symtab[1] is the output file symbol.
4870Sstevel@tonic-gate 	 */
4880Sstevel@tonic-gate 	(void) gelf_getsym(csym->c_data, 1, &sym);
4890Sstevel@tonic-gate 	if (GELF_ST_TYPE(sym.st_info) != STT_FILE) {
4900Sstevel@tonic-gate 		(void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS), cname,
4910Sstevel@tonic-gate 		    file);
4920Sstevel@tonic-gate 		(void) fprintf(stderr, MSG_INTL(MSG_VER_NOTSTTFILE),
4930Sstevel@tonic-gate 		    csym->c_name);
4940Sstevel@tonic-gate 		return;
4950Sstevel@tonic-gate 	}
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 	/*
4980Sstevel@tonic-gate 	 * Scan the remaining symbols until the next file symbol is found.
4990Sstevel@tonic-gate 	 */
5000Sstevel@tonic-gate 	for (_symn = 2; _symn < symn; _symn++) {
5010Sstevel@tonic-gate 		const char	*name;
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 		(void) gelf_getsym(csym->c_data, _symn, &sym);
5040Sstevel@tonic-gate 		if (GELF_ST_TYPE(sym.st_info) == STT_SECTION)
5050Sstevel@tonic-gate 			continue;
5060Sstevel@tonic-gate 		if (GELF_ST_TYPE(sym.st_info) == STT_FILE)
5070Sstevel@tonic-gate 			break;
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 		/*
5100Sstevel@tonic-gate 		 * Its possible that section symbols are followed immediately
5110Sstevel@tonic-gate 		 * by globals.  This is the case if an object (filter) is
5120Sstevel@tonic-gate 		 * generated exclusively from mapfile symbol definitions.
5130Sstevel@tonic-gate 		 */
5140Sstevel@tonic-gate 		if (GELF_ST_BIND(sym.st_info) != STB_LOCAL)
5150Sstevel@tonic-gate 			break;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 		name = demangle(strs + sym.st_name);
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 		if (oflag) {
520*7682SAli.Bahrami@Sun.COM 			(void) printf(MSG_ORIG(MSG_FMT_LOCSYM_OFLG),
521*7682SAli.Bahrami@Sun.COM 			    file, name);
5220Sstevel@tonic-gate 		} else {
5230Sstevel@tonic-gate 			if (found == 0) {
5240Sstevel@tonic-gate 				found = 1;
525*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_LOCSYM_HDR));
5260Sstevel@tonic-gate 			}
527*7682SAli.Bahrami@Sun.COM 			(void) printf(MSG_ORIG(MSG_FMT_LOCSYM), name);
5280Sstevel@tonic-gate 		}
5290Sstevel@tonic-gate 	}
5300Sstevel@tonic-gate }
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate /*
533*7682SAli.Bahrami@Sun.COM  * Print data from the files VERNEED section.
534*7682SAli.Bahrami@Sun.COM  *
535*7682SAli.Bahrami@Sun.COM  * If we have been asked to display symbols, then the
536*7682SAli.Bahrami@Sun.COM  * output format follows that used for verdef sections,
537*7682SAli.Bahrami@Sun.COM  * with each version displayed separately. For instance:
538*7682SAli.Bahrami@Sun.COM  *
539*7682SAli.Bahrami@Sun.COM  *	libc.so.1 (SUNW_1.7):
540*7682SAli.Bahrami@Sun.COM  *		sym1;
541*7682SAli.Bahrami@Sun.COM  *		sym2;
542*7682SAli.Bahrami@Sun.COM  *	libc.so.1 (SUNW_1.9):
543*7682SAli.Bahrami@Sun.COM  *		sym3;
544*7682SAli.Bahrami@Sun.COM  *
545*7682SAli.Bahrami@Sun.COM  * If we are not displaying symbols, then a terse format
546*7682SAli.Bahrami@Sun.COM  * is used, which combines all the needed versions from
547*7682SAli.Bahrami@Sun.COM  * a given object into a single line. In this case, the
548*7682SAli.Bahrami@Sun.COM  * versions are shown whether or not they contribute symbols.
549*7682SAli.Bahrami@Sun.COM  *
550*7682SAli.Bahrami@Sun.COM  *	libc.so.1 (SUNW_1.7, SUNW_1.9);
5510Sstevel@tonic-gate  */
5520Sstevel@tonic-gate static int
553*7682SAli.Bahrami@Sun.COM gvers_need(Cache *cache, Cache *need, const Gver_sym_data *vsdata,
554*7682SAli.Bahrami@Sun.COM     const char *file)
5550Sstevel@tonic-gate {
5560Sstevel@tonic-gate 	unsigned int	num, _num;
5570Sstevel@tonic-gate 	char		*strs;
5580Sstevel@tonic-gate 	GElf_Verneed	*vnd = need->c_data->d_buf;
5590Sstevel@tonic-gate 	GElf_Shdr	shdr;
5600Sstevel@tonic-gate 	int		error = 0;
561*7682SAli.Bahrami@Sun.COM 	int		show = vflag || (vsdata == NULL) || !oflag;
562*7682SAli.Bahrami@Sun.COM 
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	(void) gelf_getshdr(need->c_scn, &shdr);
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	/*
5670Sstevel@tonic-gate 	 * Verify the version revision.  We only check the first version
5680Sstevel@tonic-gate 	 * structure as it is assumed all other version structures in this
5690Sstevel@tonic-gate 	 * data section will be of the same revision.
5700Sstevel@tonic-gate 	 */
5710Sstevel@tonic-gate 	if (vnd->vn_version > VER_DEF_CURRENT)
5720Sstevel@tonic-gate 		(void) fprintf(stderr, MSG_INTL(MSG_VER_HIGHREV), cname, file,
5730Sstevel@tonic-gate 		    vnd->vn_version, VER_DEF_CURRENT);
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	/*
5760Sstevel@tonic-gate 	 * Get the data buffer for the associated string table.
5770Sstevel@tonic-gate 	 */
5780Sstevel@tonic-gate 	strs = (char *)cache[shdr.sh_link].c_data->d_buf;
5790Sstevel@tonic-gate 	num = shdr.sh_info;
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 	for (_num = 1; _num <= num; _num++,
5820Sstevel@tonic-gate 	    vnd = (GElf_Verneed *)((uintptr_t)vnd + vnd->vn_next)) {
583*7682SAli.Bahrami@Sun.COM 		GElf_Vernaux	*vnap;
584*7682SAli.Bahrami@Sun.COM 		Word		ndx;
585*7682SAli.Bahrami@Sun.COM 		const char	*needobj, *dep, *fmt;
586*7682SAli.Bahrami@Sun.COM 		int		started = 0;
5870Sstevel@tonic-gate 
588*7682SAli.Bahrami@Sun.COM 		vnap = (GElf_Vernaux *) ((uintptr_t)vnd + vnd->vn_aux);
589*7682SAli.Bahrami@Sun.COM 
590*7682SAli.Bahrami@Sun.COM 		/* Obtain the needed object file name */
591*7682SAli.Bahrami@Sun.COM 		needobj = (char *)(strs + vnd->vn_file);
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 		error = 1;
5940Sstevel@tonic-gate 
595*7682SAli.Bahrami@Sun.COM 		/* Process the versions needed from this object */
596*7682SAli.Bahrami@Sun.COM 		for (ndx = 0; ndx < vnd->vn_cnt; ndx++,
597*7682SAli.Bahrami@Sun.COM 		    vnap = (GElf_Vernaux *)((uintptr_t)vnap + vnap->vna_next)) {
598*7682SAli.Bahrami@Sun.COM 			Conv_ver_flags_buf_t	ver_flags_buf;
599*7682SAli.Bahrami@Sun.COM 
600*7682SAli.Bahrami@Sun.COM 			dep = (char *)(strs + vnap->vna_name);
601*7682SAli.Bahrami@Sun.COM 
602*7682SAli.Bahrami@Sun.COM 			if (!match(needobj, dep, vnap->vna_other))
603*7682SAli.Bahrami@Sun.COM 				continue;
6040Sstevel@tonic-gate 
605*7682SAli.Bahrami@Sun.COM 			if (show) {
606*7682SAli.Bahrami@Sun.COM 				if ((started == 0) || (vsdata != NULL))  {
607*7682SAli.Bahrami@Sun.COM 					/*
608*7682SAli.Bahrami@Sun.COM 					 * If one-line ouput is called for
609*7682SAli.Bahrami@Sun.COM 					 * display the filename being processed.
610*7682SAli.Bahrami@Sun.COM 					 */
611*7682SAli.Bahrami@Sun.COM 					if (oflag && show)
612*7682SAli.Bahrami@Sun.COM 						(void) printf(
613*7682SAli.Bahrami@Sun.COM 						    MSG_ORIG(MSG_FMT_OFIL),
614*7682SAli.Bahrami@Sun.COM 						    file);
615*7682SAli.Bahrami@Sun.COM 
616*7682SAli.Bahrami@Sun.COM 					(void) printf(
617*7682SAli.Bahrami@Sun.COM 					    MSG_ORIG(MSG_FMT_LIST_BEGIN),
618*7682SAli.Bahrami@Sun.COM 					    needobj);
619*7682SAli.Bahrami@Sun.COM 
620*7682SAli.Bahrami@Sun.COM 					fmt = MSG_ORIG(MSG_FMT_LIST_FIRST);
621*7682SAli.Bahrami@Sun.COM 					started = 1;
622*7682SAli.Bahrami@Sun.COM 				} else {
623*7682SAli.Bahrami@Sun.COM 					fmt = MSG_ORIG(MSG_FMT_LIST_NEXT);
624*7682SAli.Bahrami@Sun.COM 				}
6250Sstevel@tonic-gate 
626*7682SAli.Bahrami@Sun.COM 				/*
627*7682SAli.Bahrami@Sun.COM 				 * If not showing symbols, only show INFO
628*7682SAli.Bahrami@Sun.COM 				 * versions in verbose mode. They don't
629*7682SAli.Bahrami@Sun.COM 				 * actually contribute to the version
630*7682SAli.Bahrami@Sun.COM 				 * interface as seen by rtld, so listing them
631*7682SAli.Bahrami@Sun.COM 				 * without qualification can be misleading.
632*7682SAli.Bahrami@Sun.COM 				 */
633*7682SAli.Bahrami@Sun.COM 				if (vflag || (vsdata != NULL) ||
634*7682SAli.Bahrami@Sun.COM 				    (alist_nitems(match_list) != 0) ||
635*7682SAli.Bahrami@Sun.COM 				    !(vnap->vna_flags & VER_FLG_INFO)) {
636*7682SAli.Bahrami@Sun.COM 					(void) printf(fmt, dep);
6370Sstevel@tonic-gate 
638*7682SAli.Bahrami@Sun.COM 					/* Show non-zero flags */
639*7682SAli.Bahrami@Sun.COM 					if (vflag && (vnap->vna_flags != 0))
640*7682SAli.Bahrami@Sun.COM 						(void) printf(
641*7682SAli.Bahrami@Sun.COM 						    MSG_ORIG(MSG_FMT_VER_FLG),
642*7682SAli.Bahrami@Sun.COM 						    conv_ver_flags(
643*7682SAli.Bahrami@Sun.COM 						    vnap->vna_flags,
644*7682SAli.Bahrami@Sun.COM 						    CONV_FMT_NOBKT,
645*7682SAli.Bahrami@Sun.COM 						    &ver_flags_buf));
646*7682SAli.Bahrami@Sun.COM 				}
647*7682SAli.Bahrami@Sun.COM 				if (vsdata != NULL)
648*7682SAli.Bahrami@Sun.COM 					(void) printf(oflag ?
649*7682SAli.Bahrami@Sun.COM 					    MSG_ORIG(MSG_FMT_LIST_END_SEM) :
650*7682SAli.Bahrami@Sun.COM 					    MSG_ORIG(MSG_FMT_LIST_END_COL));
651*7682SAli.Bahrami@Sun.COM 			}
652*7682SAli.Bahrami@Sun.COM 
653*7682SAli.Bahrami@Sun.COM 			/*
654*7682SAli.Bahrami@Sun.COM 			 * If we are showing symbols, and vna_other is
655*7682SAli.Bahrami@Sun.COM 			 * non-zero, list them here.
656*7682SAli.Bahrami@Sun.COM 			 *
657*7682SAli.Bahrami@Sun.COM 			 * A value of 0 means that this object uses
658*7682SAli.Bahrami@Sun.COM 			 * traditional Solaris versioning rules, under
659*7682SAli.Bahrami@Sun.COM 			 * which VERSYM does not contain indexes to VERNEED
660*7682SAli.Bahrami@Sun.COM 			 * records. In this case, there is nothing to show.
661*7682SAli.Bahrami@Sun.COM 			 */
662*7682SAli.Bahrami@Sun.COM 			if (vsdata && (vnap->vna_other > 0))
663*7682SAli.Bahrami@Sun.COM 				gvers_syms(vsdata, vnap->vna_other,
664*7682SAli.Bahrami@Sun.COM 				    dep, needobj, file);
6650Sstevel@tonic-gate 		}
666*7682SAli.Bahrami@Sun.COM 		if (show && started && (vsdata == NULL))
667*7682SAli.Bahrami@Sun.COM 			(void) printf(MSG_ORIG(MSG_FMT_LIST_END_SEM));
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 	return (error);
6700Sstevel@tonic-gate }
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate /*
673*7682SAli.Bahrami@Sun.COM  * Return a GVer_desc descriptor for the given version if one
674*7682SAli.Bahrami@Sun.COM  * exists.
675*7682SAli.Bahrami@Sun.COM  *
676*7682SAli.Bahrami@Sun.COM  * entry:
677*7682SAli.Bahrami@Sun.COM  *	name - Version name
678*7682SAli.Bahrami@Sun.COM  *	hash - ELF hash of name
679*7682SAli.Bahrami@Sun.COM  *	lst - APlist of existing descriptors.
680*7682SAli.Bahrami@Sun.COM  *	file - Object file containing the version
681*7682SAli.Bahrami@Sun.COM  *
682*7682SAli.Bahrami@Sun.COM  * exit:
683*7682SAli.Bahrami@Sun.COM  *	Return the corresponding GVer_desc struct if it
684*7682SAli.Bahrami@Sun.COM  *	exists, and NULL otherwise.
6850Sstevel@tonic-gate  */
686*7682SAli.Bahrami@Sun.COM static GVer_desc *
687*7682SAli.Bahrami@Sun.COM gvers_find(const char *name, unsigned long hash, APlist *lst)
6880Sstevel@tonic-gate {
689*7682SAli.Bahrami@Sun.COM 	Aliste		idx;
690*7682SAli.Bahrami@Sun.COM 	GVer_desc	*vdp;
6910Sstevel@tonic-gate 
692*7682SAli.Bahrami@Sun.COM 	for (APLIST_TRAVERSE(lst, idx, vdp))
693*7682SAli.Bahrami@Sun.COM 		if ((vdp->vd_hash == hash) &&
694*7682SAli.Bahrami@Sun.COM 		    (strcmp(vdp->vd_name, name) == 0))
695*7682SAli.Bahrami@Sun.COM 			return (vdp);
6960Sstevel@tonic-gate 
697*7682SAli.Bahrami@Sun.COM 	return (NULL);
6980Sstevel@tonic-gate }
6990Sstevel@tonic-gate 
700*7682SAli.Bahrami@Sun.COM /*
701*7682SAli.Bahrami@Sun.COM  * Return a GVer_desc descriptor for the given version.
702*7682SAli.Bahrami@Sun.COM  *
703*7682SAli.Bahrami@Sun.COM  * entry:
704*7682SAli.Bahrami@Sun.COM  *	name - Version name
705*7682SAli.Bahrami@Sun.COM  *	hash - ELF hash of name
706*7682SAli.Bahrami@Sun.COM  *	lst - List of existing descriptors.
707*7682SAli.Bahrami@Sun.COM  *	file - Object file containing the version
708*7682SAli.Bahrami@Sun.COM  *
709*7682SAli.Bahrami@Sun.COM  * exit:
710*7682SAli.Bahrami@Sun.COM  *	Return the corresponding GVer_desc struct. If the
711*7682SAli.Bahrami@Sun.COM  * 	descriptor does not already exist, it is created.
712*7682SAli.Bahrami@Sun.COM  *	On error, a fatal error is issued and the process exits.
713*7682SAli.Bahrami@Sun.COM  */
7140Sstevel@tonic-gate static GVer_desc *
715*7682SAli.Bahrami@Sun.COM gvers_desc(const char *name, unsigned long hash, APlist **lst, const char *file)
7160Sstevel@tonic-gate {
7170Sstevel@tonic-gate 	GVer_desc	*vdp;
7180Sstevel@tonic-gate 
719*7682SAli.Bahrami@Sun.COM 	if ((vdp = gvers_find(name, hash, *lst)) == NULL) {
720*7682SAli.Bahrami@Sun.COM 		if ((vdp = calloc(sizeof (GVer_desc), 1)) == NULL) {
7210Sstevel@tonic-gate 			int err = errno;
7220Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
7230Sstevel@tonic-gate 			    file, strerror(err));
7240Sstevel@tonic-gate 			exit(1);
7250Sstevel@tonic-gate 		}
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 		vdp->vd_name = name;
7280Sstevel@tonic-gate 		vdp->vd_hash = hash;
7290Sstevel@tonic-gate 
730*7682SAli.Bahrami@Sun.COM 		pvs_aplist_append(lst, vdp, file);
7310Sstevel@tonic-gate 	}
7320Sstevel@tonic-gate 	return (vdp);
7330Sstevel@tonic-gate }
7340Sstevel@tonic-gate 
735*7682SAli.Bahrami@Sun.COM /*
736*7682SAli.Bahrami@Sun.COM  * Insert a version dependency for the given GVer_desc descriptor.
737*7682SAli.Bahrami@Sun.COM  *
738*7682SAli.Bahrami@Sun.COM  * entry:
739*7682SAli.Bahrami@Sun.COM  *	name - Dependency version name
740*7682SAli.Bahrami@Sun.COM  *	hash - ELF hash of name
741*7682SAli.Bahrami@Sun.COM  *	lst - List of existing descriptors.
742*7682SAli.Bahrami@Sun.COM  *	vdp - Existing version descriptor to which the dependency
743*7682SAli.Bahrami@Sun.COM  *		is to be added.
744*7682SAli.Bahrami@Sun.COM  *	file - Object file containing the version
745*7682SAli.Bahrami@Sun.COM  *
746*7682SAli.Bahrami@Sun.COM  * exit:
747*7682SAli.Bahrami@Sun.COM  *	A descriptor for the dependency version is looked up
748*7682SAli.Bahrami@Sun.COM  *	(created if necessary), and then added to the dependency
749*7682SAli.Bahrami@Sun.COM  *	list for vdp. Returns the dependency descriptor. On error,
750*7682SAli.Bahrami@Sun.COM  *	a fatal error is issued and the process exits.
751*7682SAli.Bahrami@Sun.COM  */
7520Sstevel@tonic-gate static GVer_desc *
753*7682SAli.Bahrami@Sun.COM gvers_depend(const char *name, unsigned long hash, GVer_desc *vdp, APlist **lst,
7540Sstevel@tonic-gate     const char *file)
7550Sstevel@tonic-gate {
7560Sstevel@tonic-gate 	GVer_desc	*_vdp;
7570Sstevel@tonic-gate 
758*7682SAli.Bahrami@Sun.COM 	_vdp = gvers_desc(name, hash, lst, file);
759*7682SAli.Bahrami@Sun.COM 	pvs_aplist_append(&vdp->vd_deps, _vdp, file);
7600Sstevel@tonic-gate 	return (vdp);
7610Sstevel@tonic-gate }
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate static void
764*7682SAli.Bahrami@Sun.COM gvers_derefer(GVer_desc *vdp, int weak)
7650Sstevel@tonic-gate {
766*7682SAli.Bahrami@Sun.COM 	Aliste		idx;
767*7682SAli.Bahrami@Sun.COM 	GVer_desc 	*_vdp;
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	/*
7700Sstevel@tonic-gate 	 * If the head of the list was a weak then we only clear out
7710Sstevel@tonic-gate 	 * weak dependencies, but if the head of the list was 'strong'
7720Sstevel@tonic-gate 	 * we clear the REFER bit on all dependencies.
7730Sstevel@tonic-gate 	 */
7740Sstevel@tonic-gate 	if ((weak && (vdp->vd_flags & VER_FLG_WEAK)) || (!weak))
7750Sstevel@tonic-gate 		vdp->vd_flags &= ~FLG_VER_AVAIL;
7760Sstevel@tonic-gate 
777*7682SAli.Bahrami@Sun.COM 	for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp))
7780Sstevel@tonic-gate 		gvers_derefer(_vdp, weak);
7790Sstevel@tonic-gate }
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate static void
783*7682SAli.Bahrami@Sun.COM recurse_syms(const Gver_sym_data *vsdata, GVer_desc *vdp, const char *file)
7840Sstevel@tonic-gate {
785*7682SAli.Bahrami@Sun.COM 	Aliste		idx;
7860Sstevel@tonic-gate 	GVer_desc	*_vdp;
7870Sstevel@tonic-gate 
788*7682SAli.Bahrami@Sun.COM 	for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp)) {
7890Sstevel@tonic-gate 		if (!oflag)
790*7682SAli.Bahrami@Sun.COM 			(void) printf(MSG_ORIG(MSG_FMT_TNCO), _vdp->vd_name);
791*7682SAli.Bahrami@Sun.COM 		gvers_syms(vsdata, _vdp->vd_ndx, _vdp->vd_name, NULL, file);
792*7682SAli.Bahrami@Sun.COM 		if (aplist_nitems(_vdp->vd_deps) != 0)
793*7682SAli.Bahrami@Sun.COM 			recurse_syms(vsdata, _vdp, file);
7940Sstevel@tonic-gate 	}
7950Sstevel@tonic-gate }
7960Sstevel@tonic-gate 
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate /*
7990Sstevel@tonic-gate  * Print the files version definition sections.
8000Sstevel@tonic-gate  */
8010Sstevel@tonic-gate static int
802*7682SAli.Bahrami@Sun.COM gvers_def(Cache *cache, Cache *def, const Gver_sym_data *vsdata,
803*7682SAli.Bahrami@Sun.COM     const char *file)
8040Sstevel@tonic-gate {
8050Sstevel@tonic-gate 	unsigned int	num, _num;
8060Sstevel@tonic-gate 	char		*strs;
8070Sstevel@tonic-gate 	GElf_Verdef	*vdf = def->c_data->d_buf;
8080Sstevel@tonic-gate 	GElf_Shdr	shdr;
809*7682SAli.Bahrami@Sun.COM 	GVer_desc	*vdp, *bvdp = NULL;
810*7682SAli.Bahrami@Sun.COM 	Aliste		idx1;
811*7682SAli.Bahrami@Sun.COM 	APlist		*verdefs = NULL;
8120Sstevel@tonic-gate 	int		error = 0;
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	/*
8150Sstevel@tonic-gate 	 * Verify the version revision.  We only check the first version
8160Sstevel@tonic-gate 	 * structure as it is assumed all other version structures in this
8170Sstevel@tonic-gate 	 * data section will be of the same revision.
8180Sstevel@tonic-gate 	 */
8190Sstevel@tonic-gate 	if (vdf->vd_version > VER_DEF_CURRENT) {
8200Sstevel@tonic-gate 		(void) fprintf(stderr, MSG_INTL(MSG_VER_HIGHREV), cname, file,
8210Sstevel@tonic-gate 		    vdf->vd_version, VER_DEF_CURRENT);
8220Sstevel@tonic-gate 	}
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	/*
8250Sstevel@tonic-gate 	 * Get the data buffer for the associated string table.
8260Sstevel@tonic-gate 	 */
8270Sstevel@tonic-gate 	(void) gelf_getshdr(def->c_scn, &shdr);
8280Sstevel@tonic-gate 	strs = (char *)cache[shdr.sh_link].c_data->d_buf;
8290Sstevel@tonic-gate 	num = shdr.sh_info;
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 	/*
8320Sstevel@tonic-gate 	 * Process the version definitions placing each on a version dependency
8330Sstevel@tonic-gate 	 * list.
8340Sstevel@tonic-gate 	 */
8350Sstevel@tonic-gate 	for (_num = 1; _num <= num; _num++,
8360Sstevel@tonic-gate 	    vdf = (GElf_Verdef *)((uintptr_t)vdf + vdf->vd_next)) {
8370Sstevel@tonic-gate 		GElf_Half	cnt = vdf->vd_cnt;
8380Sstevel@tonic-gate 		GElf_Half	ndx = vdf->vd_ndx;
839*7682SAli.Bahrami@Sun.COM 		GElf_Verdaux	*vdap;
8400Sstevel@tonic-gate 		const char	*_name;
8410Sstevel@tonic-gate 
842*7682SAli.Bahrami@Sun.COM 		vdap = (GElf_Verdaux *)((uintptr_t)vdf + vdf->vd_aux);
843*7682SAli.Bahrami@Sun.COM 
8440Sstevel@tonic-gate 		/*
8450Sstevel@tonic-gate 		 * Determine the version name and any dependencies.
8460Sstevel@tonic-gate 		 */
8470Sstevel@tonic-gate 		_name = (char *)(strs + vdap->vda_name);
8480Sstevel@tonic-gate 
849*7682SAli.Bahrami@Sun.COM 		vdp = gvers_desc(_name, elf_hash(_name), &verdefs, file);
8500Sstevel@tonic-gate 		vdp->vd_ndx = ndx;
8510Sstevel@tonic-gate 		vdp->vd_flags = vdf->vd_flags | FLG_VER_AVAIL;
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 		vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next);
8540Sstevel@tonic-gate 		for (cnt--; cnt; cnt--,
8550Sstevel@tonic-gate 		    vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next)) {
8560Sstevel@tonic-gate 			_name = (char *)(strs + vdap->vda_name);
8570Sstevel@tonic-gate 			if (gvers_depend(_name, elf_hash(_name), vdp,
858*7682SAli.Bahrami@Sun.COM 			    &verdefs, file) == NULL)
8590Sstevel@tonic-gate 				return (0);
8600Sstevel@tonic-gate 		}
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 		/*
8630Sstevel@tonic-gate 		 * Remember the base version for possible later use.
8640Sstevel@tonic-gate 		 */
8650Sstevel@tonic-gate 		if (ndx == VER_NDX_GLOBAL)
8660Sstevel@tonic-gate 			bvdp = vdp;
8670Sstevel@tonic-gate 	}
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	/*
8700Sstevel@tonic-gate 	 * Normalize the dependency list if required.
8710Sstevel@tonic-gate 	 */
8720Sstevel@tonic-gate 	if (nflag) {
873*7682SAli.Bahrami@Sun.COM 		for (APLIST_TRAVERSE(verdefs, idx1, vdp)) {
874*7682SAli.Bahrami@Sun.COM 			Aliste		idx2;
875*7682SAli.Bahrami@Sun.COM 			GVer_desc 	*_vdp;
8760Sstevel@tonic-gate 			int		type = vdp->vd_flags & VER_FLG_WEAK;
8770Sstevel@tonic-gate 
878*7682SAli.Bahrami@Sun.COM 			for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp))
8790Sstevel@tonic-gate 				gvers_derefer(_vdp, type);
8800Sstevel@tonic-gate 		}
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 		/*
8830Sstevel@tonic-gate 		 * Always dereference the base version.
8840Sstevel@tonic-gate 		 */
8850Sstevel@tonic-gate 		if (bvdp)
8860Sstevel@tonic-gate 			bvdp->vd_flags &= ~FLG_VER_AVAIL;
8870Sstevel@tonic-gate 	}
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 	/*
8910Sstevel@tonic-gate 	 * Traverse the dependency list and print out the appropriate
8920Sstevel@tonic-gate 	 * information.
8930Sstevel@tonic-gate 	 */
894*7682SAli.Bahrami@Sun.COM 	for (APLIST_TRAVERSE(verdefs, idx1, vdp)) {
895*7682SAli.Bahrami@Sun.COM 		Aliste		idx2;
896*7682SAli.Bahrami@Sun.COM 		GVer_desc 	*_vdp;
8970Sstevel@tonic-gate 		int		count;
8980Sstevel@tonic-gate 
899*7682SAli.Bahrami@Sun.COM 		if (!match(NULL, vdp->vd_name, vdp->vd_ndx))
9000Sstevel@tonic-gate 			continue;
901*7682SAli.Bahrami@Sun.COM 		if ((alist_nitems(match_list) == 0) &&
902*7682SAli.Bahrami@Sun.COM 		    !(vdp->vd_flags & FLG_VER_AVAIL))
9030Sstevel@tonic-gate 			continue;
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 		error = 1;
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 		if (vflag) {
9080Sstevel@tonic-gate 			/*
9090Sstevel@tonic-gate 			 * If the verbose flag is set determine if this version
9100Sstevel@tonic-gate 			 * has a `weak' attribute, and print any version
9110Sstevel@tonic-gate 			 * dependencies this version inherits.
9120Sstevel@tonic-gate 			 */
9130Sstevel@tonic-gate 			if (oflag)
914*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_OFIL), file);
915*7682SAli.Bahrami@Sun.COM 			(void) printf(MSG_ORIG(MSG_FMT_VER_NAME), vdp->vd_name);
916*7682SAli.Bahrami@Sun.COM 			if ((vdp->vd_flags & MSK_VER_USER) != 0) {
917*7682SAli.Bahrami@Sun.COM 				Conv_ver_flags_buf_t	ver_flags_buf;
918*7682SAli.Bahrami@Sun.COM 
919*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_VER_FLG),
920*7682SAli.Bahrami@Sun.COM 				    conv_ver_flags(
921*7682SAli.Bahrami@Sun.COM 				    vdp->vd_flags & MSK_VER_USER,
922*7682SAli.Bahrami@Sun.COM 				    CONV_FMT_NOBKT, &ver_flags_buf));
923*7682SAli.Bahrami@Sun.COM 			}
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 			count = 1;
926*7682SAli.Bahrami@Sun.COM 			for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) {
9270Sstevel@tonic-gate 				const char	*_name = _vdp->vd_name;
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 				if (count++ == 1) {
930*7682SAli.Bahrami@Sun.COM 
9310Sstevel@tonic-gate 					if (oflag)
932*7682SAli.Bahrami@Sun.COM 						(void) printf(
933*7682SAli.Bahrami@Sun.COM 						    MSG_ORIG(MSG_FMT_IN_OFLG),
934*7682SAli.Bahrami@Sun.COM 						    _name);
9350Sstevel@tonic-gate 					else if (vdp->vd_flags & VER_FLG_WEAK)
936*7682SAli.Bahrami@Sun.COM 						(void) printf(
937*7682SAli.Bahrami@Sun.COM 						    MSG_ORIG(MSG_FMT_IN_WEAK),
938*7682SAli.Bahrami@Sun.COM 						    _name);
9390Sstevel@tonic-gate 					else
940*7682SAli.Bahrami@Sun.COM 						(void) printf(
941*7682SAli.Bahrami@Sun.COM 						    MSG_ORIG(MSG_FMT_IN),
9420Sstevel@tonic-gate 						    _name);
9430Sstevel@tonic-gate 				} else
944*7682SAli.Bahrami@Sun.COM 					(void) printf(
945*7682SAli.Bahrami@Sun.COM 					    MSG_ORIG(MSG_FMT_LIST_NEXT), _name);
9460Sstevel@tonic-gate 			}
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 			if (count != 1)
949*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_IN_END));
9500Sstevel@tonic-gate 
951*7682SAli.Bahrami@Sun.COM 			if (vsdata && !oflag)
952*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_COL_NL));
9530Sstevel@tonic-gate 			else
954*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_SEM_NL));
9550Sstevel@tonic-gate 		} else {
956*7682SAli.Bahrami@Sun.COM 			if (vsdata && !oflag)
957*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_TNCO),
958*7682SAli.Bahrami@Sun.COM 				    vdp->vd_name);
959*7682SAli.Bahrami@Sun.COM 			else if (!vsdata) {
9600Sstevel@tonic-gate 				if (oflag)
961*7682SAli.Bahrami@Sun.COM 					(void) printf(MSG_ORIG(MSG_FMT_OFIL),
962*7682SAli.Bahrami@Sun.COM 					    file);
963*7682SAli.Bahrami@Sun.COM 				(void) printf(MSG_ORIG(MSG_FMT_TNSE),
964*7682SAli.Bahrami@Sun.COM 				    vdp->vd_name);
9650Sstevel@tonic-gate 			}
9660Sstevel@tonic-gate 		}
9670Sstevel@tonic-gate 
968*7682SAli.Bahrami@Sun.COM 		/* If we are not printing symbols, we're done */
969*7682SAli.Bahrami@Sun.COM 		if (vsdata == NULL)
9700Sstevel@tonic-gate 			continue;
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 		/*
973*7682SAli.Bahrami@Sun.COM 		 * If a specific version to match has been specified then
974*7682SAli.Bahrami@Sun.COM 		 * display any of its own symbols plus any inherited from
975*7682SAli.Bahrami@Sun.COM 		 * other versions. Otherwise simply print out the symbols
976*7682SAli.Bahrami@Sun.COM 		 * for this version.
9770Sstevel@tonic-gate 		 */
978*7682SAli.Bahrami@Sun.COM 		gvers_syms(vsdata, vdp->vd_ndx, vdp->vd_name, NULL, file);
979*7682SAli.Bahrami@Sun.COM 		if (alist_nitems(match_list) != 0) {
980*7682SAli.Bahrami@Sun.COM 			recurse_syms(vsdata, vdp, file);
9810Sstevel@tonic-gate 
9820Sstevel@tonic-gate 			/*
983*7682SAli.Bahrami@Sun.COM 			 * If the verbose flag is set, and this is not
984*7682SAli.Bahrami@Sun.COM 			 * the base version, then add the base version as a
985*7682SAli.Bahrami@Sun.COM 			 * dependency.
9860Sstevel@tonic-gate 			 */
987*7682SAli.Bahrami@Sun.COM 			if (vflag && bvdp &&
988*7682SAli.Bahrami@Sun.COM 			    !match(NULL, bvdp->vd_name, bvdp->vd_ndx)) {
9890Sstevel@tonic-gate 				if (!oflag)
990*7682SAli.Bahrami@Sun.COM 					(void) printf(MSG_ORIG(MSG_FMT_TNCO),
991*7682SAli.Bahrami@Sun.COM 					    bvdp->vd_name);
992*7682SAli.Bahrami@Sun.COM 				gvers_syms(vsdata, bvdp->vd_ndx,
993*7682SAli.Bahrami@Sun.COM 				    bvdp->vd_name, NULL, file);
9940Sstevel@tonic-gate 			}
9950Sstevel@tonic-gate 		}
9960Sstevel@tonic-gate 	}
9970Sstevel@tonic-gate 	return (error);
9980Sstevel@tonic-gate }
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate int
10010Sstevel@tonic-gate main(int argc, char **argv, char **envp)
10020Sstevel@tonic-gate {
10030Sstevel@tonic-gate 	GElf_Shdr	shdr;
10040Sstevel@tonic-gate 	Elf		*elf;
10050Sstevel@tonic-gate 	Elf_Scn		*scn;
10060Sstevel@tonic-gate 	Elf_Data	*data;
10070Sstevel@tonic-gate 	GElf_Ehdr 	ehdr;
10080Sstevel@tonic-gate 	int		nfile, var;
10090Sstevel@tonic-gate 	char		*names;
10100Sstevel@tonic-gate 	Cache		*cache, *_cache;
10110Sstevel@tonic-gate 	Cache		*_cache_def, *_cache_need, *_cache_sym, *_cache_loc;
10120Sstevel@tonic-gate 	int		error = 0;
1013*7682SAli.Bahrami@Sun.COM 	Gver_sym_data 	vsdata_s;
1014*7682SAli.Bahrami@Sun.COM 	const Gver_sym_data	*vsdata = NULL;
10150Sstevel@tonic-gate 
10160Sstevel@tonic-gate 	/*
10170Sstevel@tonic-gate 	 * Check for a binary that better fits this architecture.
10180Sstevel@tonic-gate 	 */
10192647Srie 	(void) conv_check_native(argv, envp);
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 	/*
10220Sstevel@tonic-gate 	 * Establish locale.
10230Sstevel@tonic-gate 	 */
10240Sstevel@tonic-gate 	(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
10250Sstevel@tonic-gate 	(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 	cname = argv[0];
10280Sstevel@tonic-gate 	Cflag = dflag = lflag = nflag = oflag = rflag = sflag = vflag = 0;
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 	opterr = 0;
1031*7682SAli.Bahrami@Sun.COM 	while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
10320Sstevel@tonic-gate 		switch (var) {
10330Sstevel@tonic-gate 		case 'C':
10340Sstevel@tonic-gate 			Cflag = USR_DEFINED;
10350Sstevel@tonic-gate 			break;
10360Sstevel@tonic-gate 		case 'd':
10370Sstevel@tonic-gate 			dflag = USR_DEFINED;
10380Sstevel@tonic-gate 			break;
10390Sstevel@tonic-gate 		case 'l':
1040*7682SAli.Bahrami@Sun.COM 			lflag = sflag = USR_DEFINED;
10410Sstevel@tonic-gate 			break;
10420Sstevel@tonic-gate 		case 'n':
10430Sstevel@tonic-gate 			nflag = USR_DEFINED;
10440Sstevel@tonic-gate 			break;
10450Sstevel@tonic-gate 		case 'o':
10460Sstevel@tonic-gate 			oflag = USR_DEFINED;
10470Sstevel@tonic-gate 			break;
10480Sstevel@tonic-gate 		case 'r':
10490Sstevel@tonic-gate 			rflag = USR_DEFINED;
10500Sstevel@tonic-gate 			break;
10510Sstevel@tonic-gate 		case 's':
10520Sstevel@tonic-gate 			sflag = USR_DEFINED;
10530Sstevel@tonic-gate 			break;
10540Sstevel@tonic-gate 		case 'v':
10550Sstevel@tonic-gate 			vflag = USR_DEFINED;
10560Sstevel@tonic-gate 			break;
1057*7682SAli.Bahrami@Sun.COM 		case 'I':
10580Sstevel@tonic-gate 		case 'N':
1059*7682SAli.Bahrami@Sun.COM 			add_match_record(var, optarg);
10600Sstevel@tonic-gate 			break;
10610Sstevel@tonic-gate 		case '?':
10620Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
10630Sstevel@tonic-gate 			    cname);
10640Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL));
10650Sstevel@tonic-gate 			exit(1);
10660Sstevel@tonic-gate 		default:
10670Sstevel@tonic-gate 			break;
10680Sstevel@tonic-gate 		}
10690Sstevel@tonic-gate 	}
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate 	/*
10720Sstevel@tonic-gate 	 * No files specified on the command line?
10730Sstevel@tonic-gate 	 */
10740Sstevel@tonic-gate 	if ((nfile = argc - optind) == 0) {
10750Sstevel@tonic-gate 		(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), cname);
10760Sstevel@tonic-gate 		exit(1);
10770Sstevel@tonic-gate 	}
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate 	/*
10800Sstevel@tonic-gate 	 * By default print both version definitions and needed dependencies.
10810Sstevel@tonic-gate 	 */
1082*7682SAli.Bahrami@Sun.COM 	if ((dflag == 0) && (rflag == 0) && (lflag == 0))
10830Sstevel@tonic-gate 		dflag = rflag = DEF_DEFINED;
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 	/*
10860Sstevel@tonic-gate 	 * Open the input file and initialize the elf interface.
10870Sstevel@tonic-gate 	 */
10880Sstevel@tonic-gate 	for (; optind < argc; optind++) {
10890Sstevel@tonic-gate 		int		derror = 0, nerror = 0,	err;
10900Sstevel@tonic-gate 		const char	*file = argv[optind];
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 		if ((var = open(file, O_RDONLY)) == -1) {
10930Sstevel@tonic-gate 			err = errno;
10940Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
10950Sstevel@tonic-gate 			    cname, file, strerror(err));
10960Sstevel@tonic-gate 			error = 1;
10970Sstevel@tonic-gate 			continue;
10980Sstevel@tonic-gate 		}
10990Sstevel@tonic-gate 		(void) elf_version(EV_CURRENT);
11000Sstevel@tonic-gate 		if ((elf = elf_begin(var, ELF_C_READ, NULL)) == NULL) {
11010Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_ORIG(MSG_ELF_BEGIN), cname,
11020Sstevel@tonic-gate 			    file, elf_errmsg(elf_errno()));
11030Sstevel@tonic-gate 			error = 1;
11040Sstevel@tonic-gate 			(void) close(var);
11050Sstevel@tonic-gate 			continue;
11060Sstevel@tonic-gate 		}
11070Sstevel@tonic-gate 		if (elf_kind(elf) != ELF_K_ELF) {
11080Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_INTL(MSG_ELF_NOTELF), cname,
11090Sstevel@tonic-gate 			    file);
11100Sstevel@tonic-gate 			error = 1;
11110Sstevel@tonic-gate 			(void) close(var);
11120Sstevel@tonic-gate 			(void) elf_end(elf);
11130Sstevel@tonic-gate 			continue;
11140Sstevel@tonic-gate 		}
11150Sstevel@tonic-gate 		if (gelf_getehdr(elf, &ehdr) == NULL) {
11160Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETEHDR), cname,
11170Sstevel@tonic-gate 			    file, elf_errmsg(elf_errno()));
11180Sstevel@tonic-gate 			error = 1;
11190Sstevel@tonic-gate 			(void) close(var);
11200Sstevel@tonic-gate 			(void) elf_end(elf);
11210Sstevel@tonic-gate 			continue;
11220Sstevel@tonic-gate 		}
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 		/*
11250Sstevel@tonic-gate 		 *  Obtain the .shstrtab data buffer to provide the required
11260Sstevel@tonic-gate 		 * section name strings.
11270Sstevel@tonic-gate 		 */
11280Sstevel@tonic-gate 		if ((scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL) {
11290Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETSCN), cname,
11300Sstevel@tonic-gate 			    file, elf_errmsg(elf_errno()));
11310Sstevel@tonic-gate 			error = 1;
11320Sstevel@tonic-gate 			(void) close(var);
11330Sstevel@tonic-gate 			(void) elf_end(elf);
11340Sstevel@tonic-gate 			continue;
11350Sstevel@tonic-gate 		}
11360Sstevel@tonic-gate 		if ((data = elf_getdata(scn, NULL)) == NULL) {
11370Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETDATA), cname,
11380Sstevel@tonic-gate 			    file, elf_errmsg(elf_errno()));
11390Sstevel@tonic-gate 			error = 1;
11400Sstevel@tonic-gate 			(void) close(var);
11410Sstevel@tonic-gate 			(void) elf_end(elf);
11420Sstevel@tonic-gate 			continue;
11430Sstevel@tonic-gate 		}
11440Sstevel@tonic-gate 		names = data->d_buf;
11450Sstevel@tonic-gate 
11460Sstevel@tonic-gate 		/*
11470Sstevel@tonic-gate 		 * Fill in the cache descriptor with information for each
11480Sstevel@tonic-gate 		 * section we might need.   We probably only need to save
11490Sstevel@tonic-gate 		 * read-only allocable sections as this is where the version
11500Sstevel@tonic-gate 		 * structures and their associated symbols and strings live.
11510Sstevel@tonic-gate 		 * However, God knows what someone can do with a mapfile, and
11520Sstevel@tonic-gate 		 * as elf_begin has already gone through all the overhead we
11530Sstevel@tonic-gate 		 * might as well set up the cache for every section.
11540Sstevel@tonic-gate 		 */
1155*7682SAli.Bahrami@Sun.COM 		if ((cache = calloc(ehdr.e_shnum, sizeof (Cache))) == NULL) {
11560Sstevel@tonic-gate 			int err = errno;
11570Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
11580Sstevel@tonic-gate 			    file, strerror(err));
11590Sstevel@tonic-gate 			exit(1);
11600Sstevel@tonic-gate 		}
11610Sstevel@tonic-gate 
1162*7682SAli.Bahrami@Sun.COM 		_cache_def = _cache_need = _cache_sym = _cache_loc = NULL;
11630Sstevel@tonic-gate 		_cache = cache;
11640Sstevel@tonic-gate 		_cache++;
11650Sstevel@tonic-gate 		for (scn = NULL; scn = elf_nextscn(elf, scn); _cache++) {
11660Sstevel@tonic-gate 			if (gelf_getshdr(scn, &shdr) == NULL) {
11670Sstevel@tonic-gate 				(void) fprintf(stderr,
11680Sstevel@tonic-gate 				    MSG_ORIG(MSG_ELF_GETSHDR), cname, file,
11690Sstevel@tonic-gate 				    elf_errmsg(elf_errno()));
11700Sstevel@tonic-gate 				error = 1;
11710Sstevel@tonic-gate 				continue;
11720Sstevel@tonic-gate 			}
11730Sstevel@tonic-gate 			if ((_cache->c_data = elf_getdata(scn, NULL)) ==
11740Sstevel@tonic-gate 			    NULL) {
11750Sstevel@tonic-gate 				(void) fprintf(stderr,
11760Sstevel@tonic-gate 				    MSG_ORIG(MSG_ELF_GETDATA), cname, file,
11770Sstevel@tonic-gate 				    elf_errmsg(elf_errno()));
11780Sstevel@tonic-gate 				error = 1;
11790Sstevel@tonic-gate 				continue;
11800Sstevel@tonic-gate 			}
11810Sstevel@tonic-gate 			_cache->c_scn = scn;
11820Sstevel@tonic-gate 			_cache->c_name = names + shdr.sh_name;
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate 			/*
11850Sstevel@tonic-gate 			 * Remember the version sections and symbol table.
11860Sstevel@tonic-gate 			 */
11872766Sab196087 			switch (shdr.sh_type) {
11882766Sab196087 			case SHT_SUNW_verdef:
11892766Sab196087 				if (dflag)
11902766Sab196087 					_cache_def = _cache;
11912766Sab196087 				break;
11922766Sab196087 			case SHT_SUNW_verneed:
11932766Sab196087 				if (rflag)
11942766Sab196087 					_cache_need = _cache;
11952766Sab196087 				break;
11962766Sab196087 			case SHT_SUNW_versym:
11972766Sab196087 				if (sflag)
11982766Sab196087 					_cache_sym = _cache;
11992766Sab196087 				break;
12002766Sab196087 			case SHT_SYMTAB:
12012766Sab196087 				if (lflag)
12022766Sab196087 					_cache_loc = _cache;
12032766Sab196087 				break;
12042766Sab196087 			}
12050Sstevel@tonic-gate 		}
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 		/*
12080Sstevel@tonic-gate 		 * Before printing anything out determine if any warnings are
12090Sstevel@tonic-gate 		 * necessary.
12100Sstevel@tonic-gate 		 */
1211*7682SAli.Bahrami@Sun.COM 		if (lflag && (_cache_loc == NULL)) {
12120Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS),
12130Sstevel@tonic-gate 			    cname, file);
12140Sstevel@tonic-gate 			(void) fprintf(stderr, MSG_INTL(MSG_VER_NOSYMTAB));
12150Sstevel@tonic-gate 		}
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 		/*
12180Sstevel@tonic-gate 		 * If there is more than one input file, and we're not printing
12190Sstevel@tonic-gate 		 * one-line output, display the filename being processed.
12200Sstevel@tonic-gate 		 */
12210Sstevel@tonic-gate 		if ((nfile > 1) && !oflag)
1222*7682SAli.Bahrami@Sun.COM 			(void) printf(MSG_ORIG(MSG_FMT_FILE), file);
1223*7682SAli.Bahrami@Sun.COM 
1224*7682SAli.Bahrami@Sun.COM 		/*
1225*7682SAli.Bahrami@Sun.COM 		 * If we're printing symbols, then collect the data
1226*7682SAli.Bahrami@Sun.COM 		 * necessary to do that.
1227*7682SAli.Bahrami@Sun.COM 		 */
1228*7682SAli.Bahrami@Sun.COM 		if (_cache_sym != NULL) {
1229*7682SAli.Bahrami@Sun.COM 			vsdata = &vsdata_s;
1230*7682SAli.Bahrami@Sun.COM 			(void) gelf_getshdr(_cache_sym->c_scn, &shdr);
1231*7682SAli.Bahrami@Sun.COM 			vsdata_s.vsd_vsp =
1232*7682SAli.Bahrami@Sun.COM 			    (GElf_Versym *)_cache_sym->c_data->d_buf;
1233*7682SAli.Bahrami@Sun.COM 			vsdata_s.vsd_sym_data = cache[shdr.sh_link].c_data;
1234*7682SAli.Bahrami@Sun.COM 			(void) gelf_getshdr(cache[shdr.sh_link].c_scn, &shdr);
1235*7682SAli.Bahrami@Sun.COM 			vsdata_s.vsd_symn = shdr.sh_size / shdr.sh_entsize;
1236*7682SAli.Bahrami@Sun.COM 			vsdata_s.vsd_strs =
1237*7682SAli.Bahrami@Sun.COM 			    (const char *)cache[shdr.sh_link].c_data->d_buf;
1238*7682SAli.Bahrami@Sun.COM 		}
1239*7682SAli.Bahrami@Sun.COM 
12400Sstevel@tonic-gate 
12410Sstevel@tonic-gate 		/*
12420Sstevel@tonic-gate 		 * Print the files version needed sections.
12430Sstevel@tonic-gate 		 */
12440Sstevel@tonic-gate 		if (_cache_need)
1245*7682SAli.Bahrami@Sun.COM 			nerror = gvers_need(cache, _cache_need, vsdata, file);
12460Sstevel@tonic-gate 
12470Sstevel@tonic-gate 		/*
12480Sstevel@tonic-gate 		 * Print the files version definition sections.
12490Sstevel@tonic-gate 		 */
12500Sstevel@tonic-gate 		if (_cache_def)
1251*7682SAli.Bahrami@Sun.COM 			derror = gvers_def(cache, _cache_def, vsdata, file);
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 		/*
12540Sstevel@tonic-gate 		 * Print any local symbol reductions.
12550Sstevel@tonic-gate 		 */
12560Sstevel@tonic-gate 		if (_cache_loc)
12570Sstevel@tonic-gate 			sym_local(cache, _cache_loc, file);
12580Sstevel@tonic-gate 
12590Sstevel@tonic-gate 		/*
12600Sstevel@tonic-gate 		 * Determine the error return.  There are three conditions that
12610Sstevel@tonic-gate 		 * may produce an error (a non-zero return):
12620Sstevel@tonic-gate 		 *
12630Sstevel@tonic-gate 		 *  o	if the user specified -d and no version definitions
12640Sstevel@tonic-gate 		 *	were found.
12650Sstevel@tonic-gate 		 *
12660Sstevel@tonic-gate 		 *  o	if the user specified -r and no version requirements
12670Sstevel@tonic-gate 		 *	were found.
12680Sstevel@tonic-gate 		 *
12690Sstevel@tonic-gate 		 *  o	if the user specified neither -d or -r, (thus both are
12700Sstevel@tonic-gate 		 *	enabled by default), and no version definitions or
12710Sstevel@tonic-gate 		 *	version dependencies were found.
12720Sstevel@tonic-gate 		 */
12730Sstevel@tonic-gate 		if (((dflag == USR_DEFINED) && (derror == 0)) ||
12740Sstevel@tonic-gate 		    ((rflag == USR_DEFINED) && (nerror == 0)) ||
12750Sstevel@tonic-gate 		    (rflag && dflag && (derror == 0) && (nerror == 0)))
12760Sstevel@tonic-gate 			error = 1;
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate 		(void) close(var);
12790Sstevel@tonic-gate 		(void) elf_end(elf);
12800Sstevel@tonic-gate 		free(cache);
12810Sstevel@tonic-gate 	}
12820Sstevel@tonic-gate 	return (error);
12830Sstevel@tonic-gate }
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate const char *
12860Sstevel@tonic-gate _pvs_msg(Msg mid)
12870Sstevel@tonic-gate {
12880Sstevel@tonic-gate 	return (gettext(MSG_ORIG(mid)));
12890Sstevel@tonic-gate }
1290