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