xref: /illumos-gate/usr/src/cmd/sgs/pvs/common/pvs.c (revision bcd524b5c10222cf2a1ef37ac7ea8bf1baa3a2ee)
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 
71 typedef struct cache {
72 	Elf_Scn		*c_scn;
73 	Elf_Data	*c_data;
74 	char		*c_name;
75 } Cache;
76 
77 typedef 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 
85 static const char	*cname;
86 static int		Cflag, dflag, lflag, nflag, oflag, rflag, sflag, vflag;
87 
88 static 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  */
103 static const char *
104 demangle(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  */
117 static void
118 sym_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  */
180 static int
181 gvers_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  */
263 static Listnode *
264 list_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 
287 static GVer_desc *
288 gvers_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 
302 static GVer_desc *
303 gvers_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 
324 static GVer_desc *
325 gvers_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 
339 static void
340 gvers_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 
390 static void
391 gvers_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 
409 static void
410 recurse_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  */
429 static int
430 gvers_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 
623 int
624 main(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 
891 const char *
892 _pvs_msg(Msg mid)
893 {
894 	return (gettext(MSG_ORIG(mid)));
895 }
896