pvs.c revision 2766:897bcb036a29
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 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Analyze the versioning information within a file.
31 *
32 *   -C		demangle C++ symbol names.
33 *
34 *   -d		dump version definitions.
35 *
36 *   -l		print reduced (local) symbols.
37 *
38 *   -n		normalize any version definitions.
39 *
40 *   -o		dump output in one-line fashion	(more suitable for grep'ing
41 *		and diff'ing).
42 *
43 *   -r		dump the version requirements on library dependencies
44 *
45 *   -s		display the symbols associated with each version definition.
46 *
47 *   -v		verbose output.  With the -r and -d options any WEAK attribute
48 *		is displayed.  With the -d option, any version inheritance,
49 *		and the base version are displayed.  With the -s option the
50 *		version symbol is displayed.
51 *
52 *   -N name	only print the specifed `name'.
53 */
54#include	<fcntl.h>
55#include	<stdio.h>
56#include	<libelf.h>
57#include	<link.h>
58#include	<stdlib.h>
59#include	<string.h>
60#include	<unistd.h>
61#include	<locale.h>
62#include	<errno.h>
63#include	<sgs.h>
64#include	<conv.h>
65#include	<gelf.h>
66#include	<debug.h>
67#include	"msg.h"
68
69#define		FLG_VER_AVAIL	0x10
70
71typedef struct cache {
72	Elf_Scn		*c_scn;
73	Elf_Data	*c_data;
74	char		*c_name;
75} Cache;
76
77typedef struct gver_desc {
78	const char	*vd_name;
79	unsigned long	vd_hash;
80	GElf_Half	vd_ndx;
81	GElf_Half	vd_flags;
82	List		vd_deps;
83} GVer_desc;
84
85static const char	*cname;
86static int		Cflag, dflag, lflag, nflag, oflag, rflag, sflag, vflag;
87
88static const char
89	* Format_ofil = "%s -",
90	* Format_tnco =	"\t%s:\n",
91	* Format_tnse =	"\t%s;\n",
92	* Format_bgnl = "\t%s (%s",
93	* Format_next = ", %s",
94	* Format_weak = " [WEAK]",
95	* Format_endl = ");\n";
96
97#define	DEF_DEFINED	1
98#define	USR_DEFINED	2
99
100/*
101 * Determine whether a symbol name should be demangled.
102 */
103static const char *
104demangle(const char *name)
105{
106	if (Cflag)
107		return (Elf_demangle_name(name));
108	else
109		return (name);
110}
111
112/*
113 * Print any reduced symbols.  The convention is that reduced symbols exist as
114 * LOCL entries in the .symtab, between the FILE symbol for the output file and
115 * the first FILE symbol for any input file used to build the output file.
116 */
117static void
118sym_local(Cache *cache, Cache *csym, const char *file)
119{
120	int		symn, _symn, found = 0;
121	GElf_Shdr	shdr;
122	GElf_Sym	sym;
123	char		*strs, *local = "_LOCAL_";
124
125	(void) gelf_getshdr(csym->c_scn, &shdr);
126	strs = (char *)cache[shdr.sh_link].c_data->d_buf;
127	/* LINTED */
128	symn = shdr.sh_info;
129
130	/*
131	 * Verify symtab[1] is the output file symbol.
132	 */
133	(void) gelf_getsym(csym->c_data, 1, &sym);
134	if (GELF_ST_TYPE(sym.st_info) != STT_FILE) {
135		(void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS), cname,
136		    file);
137		(void) fprintf(stderr, MSG_INTL(MSG_VER_NOTSTTFILE),
138		    csym->c_name);
139		return;
140	}
141
142	/*
143	 * Scan the remaining symbols until the next file symbol is found.
144	 */
145	for (_symn = 2; _symn < symn; _symn++) {
146		const char	*name;
147
148		(void) gelf_getsym(csym->c_data, _symn, &sym);
149		if (GELF_ST_TYPE(sym.st_info) == STT_SECTION)
150			continue;
151		if (GELF_ST_TYPE(sym.st_info) == STT_FILE)
152			break;
153
154		/*
155		 * Its possible that section symbols are followed immediately
156		 * by globals.  This is the case if an object (filter) is
157		 * generated exclusively from mapfile symbol definitions.
158		 */
159		if (GELF_ST_BIND(sym.st_info) != STB_LOCAL)
160			break;
161
162		name = demangle(strs + sym.st_name);
163
164		if (oflag) {
165			(void) printf(Format_ofil, file);
166			(void) printf("\t%s: %s\n", local, name);
167		} else {
168			if (found == 0) {
169				found = 1;
170				(void) printf(Format_tnco, local);
171			}
172			(void) printf("\t\t%s;\n", name);
173		}
174	}
175}
176
177/*
178 * Print the files version needed sections.
179 */
180static int
181gvers_need(Cache *cache, Cache *need, const char *file, const char *name)
182{
183	unsigned int	num, _num;
184	char		*strs;
185	GElf_Verneed	*vnd = need->c_data->d_buf;
186	GElf_Shdr	shdr;
187	int		error = 0;
188
189	(void) gelf_getshdr(need->c_scn, &shdr);
190
191	/*
192	 * Verify the version revision.  We only check the first version
193	 * structure as it is assumed all other version structures in this
194	 * data section will be of the same revision.
195	 */
196	if (vnd->vn_version > VER_DEF_CURRENT)
197		(void) fprintf(stderr, MSG_INTL(MSG_VER_HIGHREV), cname, file,
198		    vnd->vn_version, VER_DEF_CURRENT);
199
200	/*
201	 * Get the data buffer for the associated string table.
202	 */
203	strs = (char *)cache[shdr.sh_link].c_data->d_buf;
204	num = shdr.sh_info;
205
206	for (_num = 1; _num <= num; _num++,
207	    vnd = (GElf_Verneed *)((uintptr_t)vnd + vnd->vn_next)) {
208		GElf_Vernaux	*vnap = (GElf_Vernaux *)
209					((uintptr_t)vnd + vnd->vn_aux);
210		GElf_Half	cnt = vnd->vn_cnt;
211		const char	*_name, * dep;
212
213		/*
214		 * Obtain the version name and determine if we need to process
215		 * it further.
216		 */
217		_name = (char *)(strs + vnd->vn_file);
218		if (name && (strcmp(name, _name) == 0))
219			continue;
220
221		error = 1;
222
223		/*
224		 * If one-line ouput is called for display the filename being
225		 * processed.
226		 */
227		if (oflag)
228			(void) printf(Format_ofil, file);
229
230		/*
231		 * Determine the version name required from this file.
232		 */
233		if (cnt--)
234			dep = (char *)(strs + vnap->vna_name);
235		else
236			dep = MSG_ORIG(MSG_STR_EMPTY);
237
238		(void) printf(Format_bgnl, _name, dep);
239		if (vflag && (vnap->vna_flags == VER_FLG_WEAK))
240			(void) printf(Format_weak);
241
242		/*
243		 * Extract any other version dependencies for this file
244		 */
245		/* CSTYLED */
246		for (vnap = (GElf_Vernaux *)((uintptr_t)vnap + vnap->vna_next);
247		    cnt; cnt--,
248		    vnap = (GElf_Vernaux *)((uintptr_t)vnap + vnap->vna_next)) {
249			dep = (char *)(strs + vnap->vna_name);
250			(void) printf(Format_next, dep);
251			if (vflag && (vnap->vna_flags == VER_FLG_WEAK))
252				(void) printf(Format_weak);
253		}
254		(void) printf(Format_endl);
255	}
256	return (error);
257}
258
259/*
260 * Append an item to the specified list, and return a pointer to the list
261 * node created.
262 */
263static Listnode *
264list_append(List *lst, const void *item, const char *file)
265{
266	Listnode	*_lnp;
267
268	if ((_lnp = malloc(sizeof (Listnode))) == 0) {
269		int err = errno;
270		(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, file,
271		    strerror(err));
272		exit(1);
273	}
274
275	_lnp->data = (void *)item;
276	_lnp->next = NULL;
277
278	if (lst->head == NULL)
279		lst->tail = lst->head = _lnp;
280	else {
281		lst->tail->next = _lnp;
282		lst->tail = lst->tail->next;
283	}
284	return (_lnp);
285}
286
287static GVer_desc *
288gvers_find(const char *name, unsigned long hash, List *lst)
289{
290	Listnode	*lnp;
291	GVer_desc	*vdp;
292
293	for (LIST_TRAVERSE(lst, lnp, vdp)) {
294		if (vdp->vd_hash != hash)
295			continue;
296		if (strcmp(vdp->vd_name, name) == 0)
297			return (vdp);
298	}
299	return (0);
300}
301
302static GVer_desc *
303gvers_desc(const char *name, unsigned long hash, List *lst, const char *file)
304{
305	GVer_desc	*vdp;
306
307	if ((vdp = gvers_find(name, hash, lst)) == 0) {
308		if ((vdp = calloc(sizeof (GVer_desc), 1)) == 0) {
309			int err = errno;
310			(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
311			    file, strerror(err));
312			exit(1);
313		}
314
315		vdp->vd_name = name;
316		vdp->vd_hash = hash;
317
318		if (list_append(lst, vdp, file) == 0)
319			return (0);
320	}
321	return (vdp);
322}
323
324static GVer_desc *
325gvers_depend(const char *name, unsigned long hash, GVer_desc *vdp, List *lst,
326    const char *file)
327{
328	GVer_desc	*_vdp;
329
330	if ((_vdp = gvers_desc(name, hash, lst, file)) == 0)
331		return (0);
332
333	if (list_append(&vdp->vd_deps, _vdp, file) == 0)
334		return (0);
335
336	return (vdp);
337}
338
339static void
340gvers_syms(GElf_Versym *vsp, Elf_Data *sym_data, int symn, char *strs,
341    GVer_desc *vdp, const char *file)
342{
343	GElf_Sym	sym;
344	int		_symn;
345
346	for (_symn = 0; _symn < symn; _symn++) {
347		size_t		size =	0;
348		const char	*name;
349
350		if (vsp[_symn] != vdp->vd_ndx)
351			continue;
352
353		/*
354		 * For data symbols determine the size.
355		 */
356		(void) gelf_getsym(sym_data, _symn, &sym);
357		if ((GELF_ST_TYPE(sym.st_info) == STT_OBJECT) ||
358		    (GELF_ST_TYPE(sym.st_info) == STT_COMMON) ||
359		    (GELF_ST_TYPE(sym.st_info) == STT_TLS))
360			size = (size_t)sym.st_size;
361
362		name = demangle(strs + sym.st_name);
363
364		/*
365		 * Only output the version symbol when the verbose flag is used.
366		 */
367		if (!vflag && (sym.st_shndx == SHN_ABS)) {
368			if (strcmp(name, vdp->vd_name) == 0)
369				continue;
370		}
371
372		if (oflag) {
373			(void) printf(Format_ofil, file);
374			(void) printf("\t%s: ", vdp->vd_name);
375			if (size)
376				(void) printf("%s (%ld);\n", name,
377				    (ulong_t)size);
378			else
379				(void) printf("%s;\n", name);
380		} else {
381			if (size)
382				(void) printf("\t\t%s (%ld);\n", name,
383				    (ulong_t)size);
384			else
385				(void) printf("\t\t%s;\n", name);
386		}
387	}
388}
389
390static void
391gvers_derefer(GVer_desc * vdp, int weak)
392{
393	Listnode *	_lnp;
394	GVer_desc *	_vdp;
395
396	/*
397	 * If the head of the list was a weak then we only clear out
398	 * weak dependencies, but if the head of the list was 'strong'
399	 * we clear the REFER bit on all dependencies.
400	 */
401	if ((weak && (vdp->vd_flags & VER_FLG_WEAK)) || (!weak))
402		vdp->vd_flags &= ~FLG_VER_AVAIL;
403
404	for (LIST_TRAVERSE(&vdp->vd_deps, _lnp, _vdp))
405		gvers_derefer(_vdp, weak);
406}
407
408
409static void
410recurse_syms(GElf_Versym *vsp, Elf_Data *sym_data, int symn, char *strs,
411    GVer_desc *vdp, const char *file)
412{
413	Listnode	*_lnp;
414	GVer_desc	*_vdp;
415
416	for (LIST_TRAVERSE(&vdp->vd_deps, _lnp, _vdp)) {
417		if (!oflag)
418			(void) printf(Format_tnco, _vdp->vd_name);
419		gvers_syms(vsp, sym_data, symn, strs, _vdp, file);
420		if (_vdp->vd_deps.head)
421			recurse_syms(vsp, sym_data, symn, strs, _vdp, file);
422	}
423}
424
425
426/*
427 * Print the files version definition sections.
428 */
429static int
430gvers_def(Cache *cache, Cache *def, Cache *csym, const char *file,
431    const char *name)
432{
433	unsigned int	num, _num;
434	char		*strs;
435	GElf_Versym	*vsp;
436	GElf_Verdef	*vdf = def->c_data->d_buf;
437	GElf_Shdr	shdr;
438	Elf_Data	*sym_data;
439	int		symn;
440	GVer_desc	*vdp, *bvdp = 0;
441	Listnode	*lnp;
442	List		verdefs = {0, 0};
443	int		error = 0;
444
445	/*
446	 * Verify the version revision.  We only check the first version
447	 * structure as it is assumed all other version structures in this
448	 * data section will be of the same revision.
449	 */
450	if (vdf->vd_version > VER_DEF_CURRENT) {
451		(void) fprintf(stderr, MSG_INTL(MSG_VER_HIGHREV), cname, file,
452		    vdf->vd_version, VER_DEF_CURRENT);
453	}
454
455	/*
456	 * Get the data buffer for the associated string table.
457	 */
458	(void) gelf_getshdr(def->c_scn, &shdr);
459	strs = (char *)cache[shdr.sh_link].c_data->d_buf;
460	num = shdr.sh_info;
461
462	/*
463	 * Process the version definitions placing each on a version dependency
464	 * list.
465	 */
466	for (_num = 1; _num <= num; _num++,
467	    vdf = (GElf_Verdef *)((uintptr_t)vdf + vdf->vd_next)) {
468		GElf_Half	cnt = vdf->vd_cnt;
469		GElf_Half	ndx = vdf->vd_ndx;
470		GElf_Verdaux	*vdap = (GElf_Verdaux *)((uintptr_t)vdf +
471				    vdf->vd_aux);
472		const char	*_name;
473
474		/*
475		 * Determine the version name and any dependencies.
476		 */
477		_name = (char *)(strs + vdap->vda_name);
478
479		if ((vdp = gvers_desc(_name, elf_hash(_name), &verdefs,
480		    file)) == 0)
481			return (0);
482		vdp->vd_ndx = ndx;
483		vdp->vd_flags = vdf->vd_flags | FLG_VER_AVAIL;
484
485		vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next);
486		for (cnt--; cnt; cnt--,
487		    vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next)) {
488			_name = (char *)(strs + vdap->vda_name);
489			if (gvers_depend(_name, elf_hash(_name), vdp,
490			    &verdefs, file) == 0)
491				return (0);
492		}
493
494		/*
495		 * Remember the base version for possible later use.
496		 */
497		if (ndx == VER_NDX_GLOBAL)
498			bvdp = vdp;
499	}
500
501	/*
502	 * Normalize the dependency list if required.
503	 */
504	if (nflag) {
505		for (LIST_TRAVERSE(&verdefs, lnp, vdp)) {
506			Listnode *	_lnp;
507			GVer_desc *	_vdp;
508			int		type = vdp->vd_flags & VER_FLG_WEAK;
509
510			for (LIST_TRAVERSE(&vdp->vd_deps, _lnp, _vdp))
511				gvers_derefer(_vdp, type);
512		}
513
514		/*
515		 * Always dereference the base version.
516		 */
517		if (bvdp)
518			bvdp->vd_flags &= ~FLG_VER_AVAIL;
519	}
520
521
522	/*
523	 * Traverse the dependency list and print out the appropriate
524	 * information.
525	 */
526	for (LIST_TRAVERSE(&verdefs, lnp, vdp)) {
527		Listnode *	_lnp;
528		GVer_desc *	_vdp;
529		int		count;
530
531		if (name && (strcmp(name, vdp->vd_name) != 0))
532			continue;
533
534		if (!name && !(vdp->vd_flags & FLG_VER_AVAIL))
535			continue;
536
537		error = 1;
538
539		if (vflag) {
540			/*
541			 * If the verbose flag is set determine if this version
542			 * has a `weak' attribute, and print any version
543			 * dependencies this version inherits.
544			 */
545			if (oflag)
546				(void) printf(Format_ofil, file);
547			(void) printf("\t%s", vdp->vd_name);
548			if (vdp->vd_flags & VER_FLG_WEAK)
549				(void) printf(Format_weak);
550
551			count = 1;
552			for (LIST_TRAVERSE(&vdp->vd_deps, _lnp, _vdp)) {
553				const char	*_name = _vdp->vd_name;
554
555				if (count++ == 1) {
556					if (oflag)
557						(void) printf(": {%s", _name);
558					else if (vdp->vd_flags & VER_FLG_WEAK)
559						(void) printf(":\t{%s", _name);
560					else
561						(void) printf(":       \t{%s",
562						    _name);
563				} else
564					(void) printf(Format_next, _name);
565			}
566
567			if (count != 1)
568				(void) printf("}");
569
570			if (csym && !oflag)
571				(void) printf(":\n");
572			else
573				(void) printf(";\n");
574		} else {
575			if (csym && !oflag)
576				(void) printf(Format_tnco, vdp->vd_name);
577			else if (!csym) {
578				if (oflag)
579					(void) printf(Format_ofil, file);
580				(void) printf(Format_tnse, vdp->vd_name);
581			}
582		}
583
584		/*
585		 * If we need to print symbols get the associated symbol table.
586		 */
587		if (csym) {
588			(void) gelf_getshdr(csym->c_scn, &shdr);
589			vsp = (GElf_Versym *)csym->c_data->d_buf;
590			sym_data = cache[shdr.sh_link].c_data;
591			(void) gelf_getshdr(cache[shdr.sh_link].c_scn, &shdr);
592			/* LINTED */
593			symn = (int)(shdr.sh_size / shdr.sh_entsize);
594		} else
595			continue;
596
597		/*
598		 * If a specific version name has been specified then display
599		 * any of its own symbols plus any inherited from other
600		 * versions.  Otherwise simply print out the symbols for this
601		 * version.
602		 */
603		gvers_syms(vsp, sym_data, symn, strs, vdp, file);
604		if (name) {
605			recurse_syms(vsp, sym_data, symn, strs, vdp, file);
606
607			/*
608			 * If the verbose flag is set add the base version as a
609			 * dependency (unless it's the list we were asked to
610			 * print in the first place).
611			 */
612			if (vflag && bvdp && strcmp(name, bvdp->vd_name)) {
613				if (!oflag)
614				    (void) printf(Format_tnco, bvdp->vd_name);
615				gvers_syms(vsp, sym_data, symn, strs, bvdp,
616				    file);
617			}
618		}
619	}
620	return (error);
621}
622
623int
624main(int argc, char **argv, char **envp)
625{
626	GElf_Shdr	shdr;
627	Elf		*elf;
628	Elf_Scn		*scn;
629	Elf_Data	*data;
630	GElf_Ehdr 	ehdr;
631	int		nfile, var;
632	const char	*name;
633	char		*names;
634	Cache		*cache, *_cache;
635	Cache		*_cache_def, *_cache_need, *_cache_sym, *_cache_loc;
636	int		error = 0;
637
638	/*
639	 * Check for a binary that better fits this architecture.
640	 */
641	(void) conv_check_native(argv, envp);
642
643	/*
644	 * Establish locale.
645	 */
646	(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
647	(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
648
649	cname = argv[0];
650	name = NULL;
651	Cflag = dflag = lflag = nflag = oflag = rflag = sflag = vflag = 0;
652
653	opterr = 0;
654	while ((var = getopt(argc, argv, "CdlnorsvN:")) != EOF) {
655		switch (var) {
656		case 'C':
657			Cflag = USR_DEFINED;
658			break;
659		case 'd':
660			dflag = USR_DEFINED;
661			break;
662		case 'l':
663			lflag = USR_DEFINED;
664			break;
665		case 'n':
666			nflag = USR_DEFINED;
667			break;
668		case 'o':
669			oflag = USR_DEFINED;
670			break;
671		case 'r':
672			rflag = USR_DEFINED;
673			break;
674		case 's':
675			sflag = USR_DEFINED;
676			break;
677		case 'v':
678			vflag = USR_DEFINED;
679			break;
680		case 'N':
681			name = optarg;
682			break;
683		case '?':
684			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
685			    cname);
686			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL));
687			exit(1);
688		default:
689			break;
690		}
691	}
692
693	/*
694	 * No files specified on the command line?
695	 */
696	if ((nfile = argc - optind) == 0) {
697		(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), cname);
698		exit(1);
699	}
700
701	/*
702	 * By default print both version definitions and needed dependencies.
703	 */
704	if ((dflag == 0) && (rflag == 0))
705		dflag = rflag = DEF_DEFINED;
706
707	/*
708	 * Open the input file and initialize the elf interface.
709	 */
710	for (; optind < argc; optind++) {
711		int		derror = 0, nerror = 0,	err;
712		const char	*file = argv[optind];
713
714		if ((var = open(file, O_RDONLY)) == -1) {
715			err = errno;
716			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
717			    cname, file, strerror(err));
718			error = 1;
719			continue;
720		}
721		(void) elf_version(EV_CURRENT);
722		if ((elf = elf_begin(var, ELF_C_READ, NULL)) == NULL) {
723			(void) fprintf(stderr, MSG_ORIG(MSG_ELF_BEGIN), cname,
724			    file, elf_errmsg(elf_errno()));
725			error = 1;
726			(void) close(var);
727			continue;
728		}
729		if (elf_kind(elf) != ELF_K_ELF) {
730			(void) fprintf(stderr, MSG_INTL(MSG_ELF_NOTELF), cname,
731			    file);
732			error = 1;
733			(void) close(var);
734			(void) elf_end(elf);
735			continue;
736		}
737		if (gelf_getehdr(elf, &ehdr) == NULL) {
738			(void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETEHDR), cname,
739			    file, elf_errmsg(elf_errno()));
740			error = 1;
741			(void) close(var);
742			(void) elf_end(elf);
743			continue;
744		}
745
746		/*
747		 *  Obtain the .shstrtab data buffer to provide the required
748		 * section name strings.
749		 */
750		if ((scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL) {
751			(void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETSCN), cname,
752			    file, elf_errmsg(elf_errno()));
753			error = 1;
754			(void) close(var);
755			(void) elf_end(elf);
756			continue;
757		}
758		if ((data = elf_getdata(scn, NULL)) == NULL) {
759			(void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETDATA), cname,
760			    file, elf_errmsg(elf_errno()));
761			error = 1;
762			(void) close(var);
763			(void) elf_end(elf);
764			continue;
765		}
766		names = data->d_buf;
767
768		/*
769		 * Fill in the cache descriptor with information for each
770		 * section we might need.   We probably only need to save
771		 * read-only allocable sections as this is where the version
772		 * structures and their associated symbols and strings live.
773		 * However, God knows what someone can do with a mapfile, and
774		 * as elf_begin has already gone through all the overhead we
775		 * might as well set up the cache for every section.
776		 */
777		if ((cache = calloc(ehdr.e_shnum, sizeof (Cache))) == 0) {
778			int err = errno;
779			(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
780			    file, strerror(err));
781			exit(1);
782		}
783
784		_cache_def = _cache_need = _cache_sym = _cache_loc = 0;
785		_cache = cache;
786		_cache++;
787		for (scn = NULL; scn = elf_nextscn(elf, scn); _cache++) {
788			if (gelf_getshdr(scn, &shdr) == NULL) {
789				(void) fprintf(stderr,
790				    MSG_ORIG(MSG_ELF_GETSHDR), cname, file,
791				    elf_errmsg(elf_errno()));
792				error = 1;
793				continue;
794			}
795			if ((_cache->c_data = elf_getdata(scn, NULL)) ==
796			    NULL) {
797				(void) fprintf(stderr,
798				    MSG_ORIG(MSG_ELF_GETDATA), cname, file,
799				    elf_errmsg(elf_errno()));
800				error = 1;
801				continue;
802			}
803			_cache->c_scn = scn;
804			_cache->c_name = names + shdr.sh_name;
805
806			/*
807			 * Remember the version sections and symbol table.
808			 */
809			switch (shdr.sh_type) {
810			case SHT_SUNW_verdef:
811				if (dflag)
812					_cache_def = _cache;
813				break;
814			case SHT_SUNW_verneed:
815				if (rflag)
816					_cache_need = _cache;
817				break;
818			case SHT_SUNW_versym:
819				if (sflag)
820					_cache_sym = _cache;
821				break;
822			case SHT_SYMTAB:
823				if (lflag)
824					_cache_loc = _cache;
825				break;
826			}
827		}
828
829		/*
830		 * Before printing anything out determine if any warnings are
831		 * necessary.
832		 */
833		if (lflag && (_cache_loc == 0)) {
834			(void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS),
835			    cname, file);
836			(void) fprintf(stderr, MSG_INTL(MSG_VER_NOSYMTAB));
837		}
838
839		/*
840		 * If there is more than one input file, and we're not printing
841		 * one-line output, display the filename being processed.
842		 */
843		if ((nfile > 1) && !oflag)
844			(void) printf("%s:\n", file);
845
846		/*
847		 * Print the files version needed sections.
848		 */
849		if (_cache_need)
850			nerror = gvers_need(cache, _cache_need, file, name);
851
852		/*
853		 * Print the files version definition sections.
854		 */
855		if (_cache_def)
856			derror = gvers_def(cache, _cache_def, _cache_sym,
857			    file, name);
858
859		/*
860		 * Print any local symbol reductions.
861		 */
862		if (_cache_loc)
863			sym_local(cache, _cache_loc, file);
864
865		/*
866		 * Determine the error return.  There are three conditions that
867		 * may produce an error (a non-zero return):
868		 *
869		 *  o	if the user specified -d and no version definitions
870		 *	were found.
871		 *
872		 *  o	if the user specified -r and no version requirements
873		 *	were found.
874		 *
875		 *  o	if the user specified neither -d or -r, (thus both are
876		 *	enabled by default), and no version definitions or
877		 *	version dependencies were found.
878		 */
879		if (((dflag == USR_DEFINED) && (derror == 0)) ||
880		    ((rflag == USR_DEFINED) && (nerror == 0)) ||
881		    (rflag && dflag && (derror == 0) && (nerror == 0)))
882			error = 1;
883
884		(void) close(var);
885		(void) elf_end(elf);
886		free(cache);
887	}
888	return (error);
889}
890
891const char *
892_pvs_msg(Msg mid)
893{
894	return (gettext(MSG_ORIG(mid)));
895}
896