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 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Analyze the versioning information within a file.
29 *
30 * -C demangle C++ symbol names.
31 *
32 * -d dump version definitions.
33 *
34 * -l print reduced (local) symbols. Implies -s.
35 *
36 * -n normalize any version definitions.
37 *
38 * -o dump output in one-line fashion (more suitable for grep'ing
39 * and diff'ing).
40 *
41 * -r dump the version requirements on library dependencies
42 *
43 * -s display the symbols associated with each version definition.
44 *
45 * -v verbose output. With the -r and -d options any WEAK attribute
46 * is displayed. With the -d option, any version inheritance,
47 * and the base version are displayed. With the -r option,
48 * WEAK and INFO attributes are displayed. With the -s option
49 * the version symbol is displayed.
50 *
51 * -I index only print the specifed version index, or index range.
52 *
53 * -N name only print the specifed `name'.
54 */
55 #include <fcntl.h>
56 #include <stdio.h>
57 #include <libelf.h>
58 #include <link.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <locale.h>
63 #include <errno.h>
64 #include <sgs.h>
65 #include <conv.h>
66 #include <gelf.h>
67 #include <debug.h>
68 #include <ctype.h>
69 #include <alist.h>
70 #include "msg.h"
71
72 /*
73 * Define Alist initialization sizes.
74 */
75 #define AL_CNT_MATCH_LIST 5 /* match_list initial alist count */
76 #define AL_CNT_GVER_DESC 25 /* version tracking descriptors */
77
78 typedef struct cache {
79 Elf_Scn *c_scn;
80 Elf_Data *c_data;
81 char *c_name;
82 } Cache;
83
84 typedef struct gver_desc {
85 const char *vd_name;
86 unsigned long vd_hash;
87 GElf_Half vd_ndx;
88 GElf_Half vd_flags;
89 APlist *vd_deps;
90 } GVer_desc;
91
92 /* Versym related data used by gvers_syms() */
93 typedef struct {
94 GElf_Versym *vsd_vsp; /* ptr to versym data */
95 Elf_Data *vsd_sym_data; /* ptr to symtab data */
96 Word vsd_symn; /* # of symbols in symtab */
97 const char *vsd_strs; /* string table data */
98 } Gver_sym_data;
99
100 /*
101 * Type used to manage -I and -N options:
102 *
103 * The -I option specifies a VERSYM index, or index range. The
104 * result is to select the VERDEF or VERNEED records with
105 * indexes that match those given.
106 *
107 * -N options come in two forms:
108 *
109 * 1) name
110 * 2) needobj (version)
111 *
112 * The meaning of the first case depends on the type of
113 * version record being matched:
114 *
115 * VERDEF - name is the name of a version defined
116 * by the object being processed (i.e. SUNW_1.1).
117 *
118 * VERNEED - name is the name of the object file
119 * on which the dependency exists (i.e. libc.so.1).
120 *
121 * -N options of the second form only apply to VERNEED records.
122 * They are used to specify a version from a needed object.
123 */
124 /* match_opt_t is used to note which match option was used */
125 typedef enum {
126 MATCH_OPT_NAME, /* Record contains a name */
127 MATCH_OPT_NEED_VER, /* Record contains needed object and version */
128 MATCH_OPT_NDX, /* Record contains a single index */
129 MATCH_OPT_RANGE, /* Record contains an index range */
130 } match_opt_t;
131
132 typedef struct {
133 match_opt_t opt_type;
134 union {
135 struct {
136 const char *version; /* MATCH_OPT_{NAME|NEED_VER} */
137 const char *needobj; /* MATCH_OPT_NEED_VER only */
138 } name;
139 struct {
140 int start; /* MATCH_OPT_{NDX|RANGE} */
141 int end; /* MATCH_OPT_RANGE only) */
142 } ndx;
143 } value;
144 } match_rec_t;
145
146
147
148 static const char *cname;
149 static int Cflag, dflag, lflag, nflag, oflag, rflag, sflag, vflag;
150 static Alist *match_list;
151
152 /* Used to track whether an option defaulted to on, or was explicitly set */
153 #define DEF_DEFINED 1
154 #define USR_DEFINED 2
155
156 /*
157 * Determine whether a symbol name should be demangled.
158 */
159 static const char *
demangle(const char * name)160 demangle(const char *name)
161 {
162 if (Cflag)
163 return (Elf_demangle_name(name));
164 else
165 return (name);
166 }
167
168 /*
169 * Append an item to the specified list, and return a pointer to the list
170 * node created.
171 *
172 * exit:
173 * On success, a new list node is created and the item is
174 * added to the list. On failure, a fatal error is issued
175 * and the process exits.
176 */
177 static void
pvs_aplist_append(APlist ** lst,const void * item,const char * file)178 pvs_aplist_append(APlist **lst, const void *item, const char *file)
179 {
180 if (aplist_append(lst, item, AL_CNT_GVER_DESC) == NULL) {
181 int err = errno;
182 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, file,
183 strerror(err));
184 exit(1);
185 }
186 }
187
188 /*
189 * Add an entry to match_list for use by match(). This routine is for
190 * use during getopt() processing.
191 *
192 * entry:
193 * opt - One of 'N' or 'I', indicating the option
194 * str - Value string corresponding to opt
195 *
196 * exit:
197 * The new match record has been added. On error, a fatal
198 * error is issued and and the process exits.
199 */
200 static void
add_match_record(int opt,const char * str)201 add_match_record(int opt, const char *str)
202 {
203 /*
204 * Macros for removing leading and trailing whitespace:
205 * WS_SKIP - Advance _str without passing the NULL termination,
206 * until the first character is not whitespace.
207 * WS_SKIP_LIMIT - Advance _str without passing _limit,
208 * until the first character is not whitespace.
209 * WS_RSKIP_LIMIT - Move _tail back without passing _str,
210 * until the character before it is not whitespace.
211 * Write a NULL termination at that point.
212 */
213 #define WS_SKIP(_str) for (; *(_str) && isspace(*(_str)); (_str)++)
214 #define WS_SKIP_LIMIT(_str, _limit) \
215 while (((_str) < s2) && isspace(*(_str))) \
216 (_str)++
217 #define WS_RSKIP_LIMIT(_str, _tail) \
218 while (((_tail) > (_str)) && isspace(*((_tail) - 1))) \
219 (_tail)--; \
220 *(_tail) = '\0'
221
222
223 match_rec_t *rec;
224 char *lstr, *s1, *s2;
225
226 rec = alist_append(&match_list, NULL, sizeof (match_rec_t),
227 AL_CNT_MATCH_LIST);
228 if (rec == NULL) {
229 int err = errno;
230 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
231 MSG_INTL(MSG_STR_MATCH_RECORD), strerror(err));
232 exit(1);
233 }
234
235 if (opt == 'N') {
236 if ((lstr = strdup(str)) == NULL) {
237 int err = errno;
238 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC),
239 cname, MSG_INTL(MSG_STR_MATCH_RECORD),
240 strerror(err));
241 exit(1);
242 }
243
244 /* Strip leading/trailing whitespace */
245 s2 = lstr + strlen(lstr);
246 WS_SKIP_LIMIT(lstr, s2);
247 WS_RSKIP_LIMIT(lstr, s2);
248
249 /* Assume this is a plain string */
250 rec->opt_type = MATCH_OPT_NAME;
251 rec->value.name.version = lstr;
252
253 /*
254 * If s2 points at a closing paren, then this might
255 * be a MATCH_OPT_NEED_VER case. Otherwise we're done.
256 */
257 if ((s2 == lstr) || (*(s2 - 1) != ')'))
258 return;
259
260 /* We have a closing paren. Locate the opening one. */
261 for (s1 = lstr; *s1 && (*s1 != '('); s1++)
262 ;
263 if (*s1 != '(')
264 return;
265
266 rec->opt_type = MATCH_OPT_NEED_VER;
267 rec->value.name.needobj = lstr;
268 rec->value.name.version = s1 + 1;
269 s2--; /* Points at closing paren */
270
271 /* Remove whitespace from head/tail of version */
272 WS_SKIP_LIMIT(rec->value.name.version, s2);
273 WS_RSKIP_LIMIT(rec->value.name.version, s2);
274
275 /* Terminate needobj, skipping trailing whitespace */
276 WS_RSKIP_LIMIT(rec->value.name.needobj, s1);
277
278 return;
279 }
280
281
282 /* If we get here, we are looking at a -I index option */
283 rec->value.ndx.start = strtol(str, &s2, 10);
284 /* Value must use some of the input, and be positive */
285 if ((str == s2) || (rec->value.ndx.start < 1))
286 goto syntax_error;
287 str = s2;
288
289 WS_SKIP(str);
290 if (*str != ':') {
291 rec->opt_type = MATCH_OPT_NDX;
292 } else {
293 str++; /* Skip the ':' */
294 rec->opt_type = MATCH_OPT_RANGE;
295 WS_SKIP(str);
296 if (*str == '\0') {
297 rec->value.ndx.end = -1; /* Indicates "to end" */
298 } else {
299 rec->value.ndx.end = strtol(str, &s2, 10);
300 if ((str == s2) || (rec->value.ndx.end < 0))
301 goto syntax_error;
302 str = s2;
303 WS_SKIP(str);
304 }
305 }
306
307 /* If we are successful, there is nothing left to parse */
308 if (*str == '\0')
309 return;
310
311 /*
312 * If we get here, there is leftover input. Fall through
313 * to issue a syntax error.
314 */
315 syntax_error:
316 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), cname);
317 exit(1);
318
319 #undef WS_SKIP
320 #undef WS_SKIP_LIMIT
321 #undef WS_RSKIP_LIMIT
322 }
323
324 /*
325 * Returns True (1) if the version with the given name or index should
326 * be displayed, and False (0) if it should not be.
327 *
328 * entry:
329 * needobj - NULL for VERDEF records, the name of the
330 * needed object for VERNEED.
331 * version - NULL, or needed version
332 * ndx - Versym index of version under consideration, or a value less
333 * than 1 to indicate that no valid index is given.
334 *
335 * exit:
336 * True will be returned if the given name/index matches those given
337 * by one of the -I or -N command line options, or if no such option
338 * was used in the command invocation.
339 */
340 int
match(const char * needobj,const char * version,int ndx)341 match(const char *needobj, const char *version, int ndx)
342 {
343 Aliste _idx;
344 match_rec_t *rec;
345 const char *str;
346
347 /* If there is no match list, then we approve everything */
348 if (alist_nitems(match_list) == 0)
349 return (1);
350
351 /* Run through the match records and check for a hit */
352 for (ALIST_TRAVERSE(match_list, _idx, rec)) {
353 switch (rec->opt_type) {
354 case MATCH_OPT_NAME:
355 if (needobj)
356 str = needobj;
357 else if (version)
358 str = version;
359 else
360 break;
361 if (strcmp(rec->value.name.version, str) == 0)
362 return (1);
363 break;
364 case MATCH_OPT_NEED_VER:
365 if (needobj && version &&
366 (strcmp(rec->value.name.needobj, needobj) == 0) &&
367 (strcmp(rec->value.name.version, version) == 0))
368 return (1);
369 break;
370 case MATCH_OPT_NDX:
371 if ((ndx > 0) && (ndx == rec->value.ndx.start))
372 return (1);
373 break;
374 case MATCH_OPT_RANGE:
375 /*
376 * A range end value less than 0 means that any value
377 * above the start is acceptible.
378 */
379 if ((ndx > 0) &&
380 (ndx >= rec->value.ndx.start) &&
381 ((rec->value.ndx.end < 0) ||
382 (ndx <= rec->value.ndx.end)))
383 return (1);
384 break;
385 }
386 }
387
388 /* Nothing matched */
389 return (0);
390 }
391
392 /*
393 * List the symbols that belong to a specified version
394 *
395 * entry:
396 * vsdata - VERSYM related data from the object
397 * vd_ndx - The VERSYM index for symbols to display
398 * vd_name - Version name
399 * needobj - NULL for symbols corresponding to a VERDEF
400 * record. Name of the needed object in the case
401 * of a VERNEED record.
402 * file - Object file
403 */
404 static void
gvers_syms(const Gver_sym_data * vsdata,GElf_Half vd_ndx,const char * vd_name,const char * needobj,const char * file)405 gvers_syms(const Gver_sym_data *vsdata, GElf_Half vd_ndx,
406 const char *vd_name, const char *needobj, const char *file)
407 {
408 GElf_Sym sym;
409 int _symn;
410
411 for (_symn = 0; _symn < vsdata->vsd_symn; _symn++) {
412 size_t size = 0;
413 const char *name;
414
415 if (vsdata->vsd_vsp[_symn] != vd_ndx)
416 continue;
417
418 (void) gelf_getsym(vsdata->vsd_sym_data, _symn, &sym);
419 name = demangle(vsdata->vsd_strs + sym.st_name);
420
421 /*
422 * Symbols that reference a VERDEF record
423 * have some extra details to handle.
424 */
425 if (needobj == NULL) {
426 /*
427 * For data symbols defined by this object,
428 * determine the size.
429 */
430 if ((GELF_ST_TYPE(sym.st_info) == STT_OBJECT) ||
431 (GELF_ST_TYPE(sym.st_info) == STT_COMMON) ||
432 (GELF_ST_TYPE(sym.st_info) == STT_TLS))
433 size = (size_t)sym.st_size;
434
435 /*
436 * Only output the version symbol when the verbose
437 * flag is used.
438 */
439 if (!vflag && (sym.st_shndx == SHN_ABS) &&
440 (strcmp(name, vd_name) == 0))
441 continue;
442 }
443
444 if (oflag) {
445 if (needobj == NULL)
446 (void) printf(MSG_ORIG(MSG_FMT_SYM_OFIL),
447 file, vd_name);
448 else
449 (void) printf(MSG_ORIG(MSG_FMT_SYM_NEED_OFIL),
450 file, needobj, vd_name);
451
452 if (size)
453 (void) printf(MSG_ORIG(MSG_FMT_SYM_SZ_OFLG),
454 name, (ulong_t)size);
455 else
456 (void) printf(MSG_ORIG(MSG_FMT_SYM_OFLG), name);
457 } else {
458 if (size)
459 (void) printf(MSG_ORIG(MSG_FMT_SYM_SZ), name,
460 (ulong_t)size);
461 else
462 (void) printf(MSG_ORIG(MSG_FMT_SYM), name);
463 }
464 }
465 }
466
467 /*
468 * Print any reduced symbols. The convention is that reduced symbols exist as
469 * LOCL entries in the .symtab, between the FILE symbol for the output file and
470 * the first FILE symbol for any input file used to build the output file.
471 */
472 static void
sym_local(Cache * cache,Cache * csym,const char * file)473 sym_local(Cache *cache, Cache *csym, const char *file)
474 {
475 int symn, _symn, found = 0;
476 GElf_Shdr shdr;
477 GElf_Sym sym;
478 char *strs;
479
480 (void) gelf_getshdr(csym->c_scn, &shdr);
481 strs = (char *)cache[shdr.sh_link].c_data->d_buf;
482 /* LINTED */
483 symn = shdr.sh_info;
484
485 /*
486 * Verify symtab[1] is the output file symbol.
487 */
488 (void) gelf_getsym(csym->c_data, 1, &sym);
489 if (GELF_ST_TYPE(sym.st_info) != STT_FILE) {
490 (void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS), cname,
491 file);
492 (void) fprintf(stderr, MSG_INTL(MSG_VER_NOTSTTFILE),
493 csym->c_name);
494 return;
495 }
496
497 /*
498 * Scan the remaining symbols until the next file symbol is found.
499 */
500 for (_symn = 2; _symn < symn; _symn++) {
501 const char *name;
502
503 (void) gelf_getsym(csym->c_data, _symn, &sym);
504 if (GELF_ST_TYPE(sym.st_info) == STT_SECTION)
505 continue;
506 if (GELF_ST_TYPE(sym.st_info) == STT_FILE)
507 break;
508
509 /*
510 * Its possible that section symbols are followed immediately
511 * by globals. This is the case if an object (filter) is
512 * generated exclusively from mapfile symbol definitions.
513 */
514 if (GELF_ST_BIND(sym.st_info) != STB_LOCAL)
515 break;
516
517 name = demangle(strs + sym.st_name);
518
519 if (oflag) {
520 (void) printf(MSG_ORIG(MSG_FMT_LOCSYM_OFLG),
521 file, name);
522 } else {
523 if (found == 0) {
524 found = 1;
525 (void) printf(MSG_ORIG(MSG_FMT_LOCSYM_HDR));
526 }
527 (void) printf(MSG_ORIG(MSG_FMT_LOCSYM), name);
528 }
529 }
530 }
531
532 /*
533 * Print data from the files VERNEED section.
534 *
535 * If we have been asked to display symbols, then the
536 * output format follows that used for verdef sections,
537 * with each version displayed separately. For instance:
538 *
539 * libc.so.1 (SUNW_1.7):
540 * sym1;
541 * sym2;
542 * libc.so.1 (SUNW_1.9):
543 * sym3;
544 *
545 * If we are not displaying symbols, then a terse format
546 * is used, which combines all the needed versions from
547 * a given object into a single line. In this case, the
548 * versions are shown whether or not they contribute symbols.
549 *
550 * libc.so.1 (SUNW_1.7, SUNW_1.9);
551 */
552 static int
gvers_need(Cache * cache,Cache * need,const Gver_sym_data * vsdata,const char * file)553 gvers_need(Cache *cache, Cache *need, const Gver_sym_data *vsdata,
554 const char *file)
555 {
556 unsigned int num, _num;
557 char *strs;
558 GElf_Verneed *vnd = need->c_data->d_buf;
559 GElf_Shdr shdr;
560 int error = 0;
561 int show = vflag || (vsdata == NULL) || !oflag;
562
563
564 (void) gelf_getshdr(need->c_scn, &shdr);
565
566 /*
567 * Verify the version revision. We only check the first version
568 * structure as it is assumed all other version structures in this
569 * data section will be of the same revision.
570 */
571 if (vnd->vn_version > VER_DEF_CURRENT)
572 (void) fprintf(stderr, MSG_INTL(MSG_VER_HIGHREV), cname, file,
573 vnd->vn_version, VER_DEF_CURRENT);
574
575 /*
576 * Get the data buffer for the associated string table.
577 */
578 strs = (char *)cache[shdr.sh_link].c_data->d_buf;
579 num = shdr.sh_info;
580
581 for (_num = 1; _num <= num; _num++,
582 vnd = (GElf_Verneed *)((uintptr_t)vnd + vnd->vn_next)) {
583 GElf_Vernaux *vnap;
584 Word ndx;
585 const char *needobj, *dep;
586 int started = 0, listcnt = 0;
587
588 vnap = (GElf_Vernaux *) ((uintptr_t)vnd + vnd->vn_aux);
589
590 /* Obtain the needed object file name */
591 needobj = (char *)(strs + vnd->vn_file);
592
593 error = 1;
594
595 /* Process the versions needed from this object */
596 for (ndx = 0; ndx < vnd->vn_cnt; ndx++,
597 vnap = (GElf_Vernaux *)((uintptr_t)vnap + vnap->vna_next)) {
598 Conv_ver_flags_buf_t ver_flags_buf;
599
600 dep = (char *)(strs + vnap->vna_name);
601
602 if (!match(needobj, dep, vnap->vna_other))
603 continue;
604
605 if (show) {
606 if ((started == 0) || (vsdata != NULL)) {
607 /*
608 * If one-line ouput is called for
609 * display the filename being processed.
610 */
611 if (oflag && show)
612 (void) printf(
613 MSG_ORIG(MSG_FMT_OFIL),
614 file);
615
616 (void) printf(
617 MSG_ORIG(MSG_FMT_LIST_BEGIN),
618 needobj);
619 started = 1;
620 }
621
622 /*
623 * If not showing symbols, only show INFO
624 * versions in verbose mode. They don't
625 * actually contribute to the version
626 * interface as seen by rtld, so listing them
627 * without qualification can be misleading.
628 */
629 if (vflag || (vsdata != NULL) ||
630 (alist_nitems(match_list) != 0) ||
631 !(vnap->vna_flags & VER_FLG_INFO)) {
632 const char *fmt = (listcnt == 0) ?
633 MSG_ORIG(MSG_FMT_LIST_FIRST) :
634 MSG_ORIG(MSG_FMT_LIST_NEXT);
635
636 if (vsdata == NULL)
637 listcnt++;
638 (void) printf(fmt, dep);
639
640 /* Show non-zero flags */
641 if (vflag && (vnap->vna_flags != 0))
642 (void) printf(
643 MSG_ORIG(MSG_FMT_VER_FLG),
644 conv_ver_flags(
645 vnap->vna_flags,
646 CONV_FMT_NOBKT,
647 &ver_flags_buf));
648 }
649 if (vsdata != NULL)
650 (void) printf(oflag ?
651 MSG_ORIG(MSG_FMT_LIST_END_SEM) :
652 MSG_ORIG(MSG_FMT_LIST_END_COL));
653 }
654
655 /*
656 * If we are showing symbols, and vna_other is
657 * non-zero, list them here.
658 *
659 * A value of 0 means that this object uses
660 * traditional Solaris versioning rules, under
661 * which VERSYM does not contain indexes to VERNEED
662 * records. In this case, there is nothing to show.
663 */
664 if (vsdata && (vnap->vna_other > 0))
665 gvers_syms(vsdata, vnap->vna_other,
666 dep, needobj, file);
667 }
668 if (show && started && (vsdata == NULL))
669 (void) printf(MSG_ORIG(MSG_FMT_LIST_END_SEM));
670 }
671 return (error);
672 }
673
674 /*
675 * Return a GVer_desc descriptor for the given version if one
676 * exists.
677 *
678 * entry:
679 * name - Version name
680 * hash - ELF hash of name
681 * lst - APlist of existing descriptors.
682 * file - Object file containing the version
683 *
684 * exit:
685 * Return the corresponding GVer_desc struct if it
686 * exists, and NULL otherwise.
687 */
688 static GVer_desc *
gvers_find(const char * name,unsigned long hash,APlist * lst)689 gvers_find(const char *name, unsigned long hash, APlist *lst)
690 {
691 Aliste idx;
692 GVer_desc *vdp;
693
694 for (APLIST_TRAVERSE(lst, idx, vdp))
695 if ((vdp->vd_hash == hash) &&
696 (strcmp(vdp->vd_name, name) == 0))
697 return (vdp);
698
699 return (NULL);
700 }
701
702 /*
703 * Return a GVer_desc descriptor for the given version.
704 *
705 * entry:
706 * name - Version name
707 * hash - ELF hash of name
708 * lst - List of existing descriptors.
709 * file - Object file containing the version
710 *
711 * exit:
712 * Return the corresponding GVer_desc struct. If the
713 * descriptor does not already exist, it is created.
714 * On error, a fatal error is issued and the process exits.
715 */
716 static GVer_desc *
gvers_desc(const char * name,unsigned long hash,APlist ** lst,const char * file)717 gvers_desc(const char *name, unsigned long hash, APlist **lst, const char *file)
718 {
719 GVer_desc *vdp;
720
721 if ((vdp = gvers_find(name, hash, *lst)) == NULL) {
722 if ((vdp = calloc(sizeof (GVer_desc), 1)) == NULL) {
723 int err = errno;
724 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
725 file, strerror(err));
726 exit(1);
727 }
728
729 vdp->vd_name = name;
730 vdp->vd_hash = hash;
731
732 pvs_aplist_append(lst, vdp, file);
733 }
734 return (vdp);
735 }
736
737 /*
738 * Insert a version dependency for the given GVer_desc descriptor.
739 *
740 * entry:
741 * name - Dependency version name
742 * hash - ELF hash of name
743 * lst - List of existing descriptors.
744 * vdp - Existing version descriptor to which the dependency
745 * is to be added.
746 * file - Object file containing the version
747 *
748 * exit:
749 * A descriptor for the dependency version is looked up
750 * (created if necessary), and then added to the dependency
751 * list for vdp. Returns the dependency descriptor. On error,
752 * a fatal error is issued and the process exits.
753 */
754 static GVer_desc *
gvers_depend(const char * name,unsigned long hash,GVer_desc * vdp,APlist ** lst,const char * file)755 gvers_depend(const char *name, unsigned long hash, GVer_desc *vdp, APlist **lst,
756 const char *file)
757 {
758 GVer_desc *_vdp;
759
760 _vdp = gvers_desc(name, hash, lst, file);
761 pvs_aplist_append(&vdp->vd_deps, _vdp, file);
762 return (vdp);
763 }
764
765 static void
gvers_derefer(GVer_desc * vdp,int weak)766 gvers_derefer(GVer_desc *vdp, int weak)
767 {
768 Aliste idx;
769 GVer_desc *_vdp;
770
771 /*
772 * If the head of the list was a weak then we only clear out
773 * weak dependencies, but if the head of the list was 'strong'
774 * we clear the REFER bit on all dependencies.
775 */
776 if ((weak && (vdp->vd_flags & VER_FLG_WEAK)) || (!weak))
777 vdp->vd_flags &= ~FLG_VER_AVAIL;
778
779 for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp))
780 gvers_derefer(_vdp, weak);
781 }
782
783
784 static void
recurse_syms(const Gver_sym_data * vsdata,GVer_desc * vdp,const char * file)785 recurse_syms(const Gver_sym_data *vsdata, GVer_desc *vdp, const char *file)
786 {
787 Aliste idx;
788 GVer_desc *_vdp;
789
790 for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp)) {
791 if (!oflag)
792 (void) printf(MSG_ORIG(MSG_FMT_TNCO), _vdp->vd_name);
793 gvers_syms(vsdata, _vdp->vd_ndx, _vdp->vd_name, NULL, file);
794 if (aplist_nitems(_vdp->vd_deps) != 0)
795 recurse_syms(vsdata, _vdp, file);
796 }
797 }
798
799
800 /*
801 * Print the files version definition sections.
802 */
803 static int
gvers_def(Cache * cache,Cache * def,const Gver_sym_data * vsdata,const char * file)804 gvers_def(Cache *cache, Cache *def, const Gver_sym_data *vsdata,
805 const char *file)
806 {
807 unsigned int num, _num;
808 char *strs;
809 GElf_Verdef *vdf = def->c_data->d_buf;
810 GElf_Shdr shdr;
811 GVer_desc *vdp, *bvdp = NULL;
812 Aliste idx1;
813 APlist *verdefs = NULL;
814 int error = 0;
815
816 /*
817 * Verify the version revision. We only check the first version
818 * structure as it is assumed all other version structures in this
819 * data section will be of the same revision.
820 */
821 if (vdf->vd_version > VER_DEF_CURRENT) {
822 (void) fprintf(stderr, MSG_INTL(MSG_VER_HIGHREV), cname, file,
823 vdf->vd_version, VER_DEF_CURRENT);
824 }
825
826 /*
827 * Get the data buffer for the associated string table.
828 */
829 (void) gelf_getshdr(def->c_scn, &shdr);
830 strs = (char *)cache[shdr.sh_link].c_data->d_buf;
831 num = shdr.sh_info;
832
833 /*
834 * Process the version definitions placing each on a version dependency
835 * list.
836 */
837 for (_num = 1; _num <= num; _num++,
838 vdf = (GElf_Verdef *)((uintptr_t)vdf + vdf->vd_next)) {
839 GElf_Half cnt = vdf->vd_cnt;
840 GElf_Half ndx = vdf->vd_ndx;
841 GElf_Verdaux *vdap;
842 const char *_name;
843
844 vdap = (GElf_Verdaux *)((uintptr_t)vdf + vdf->vd_aux);
845
846 /*
847 * Determine the version name and any dependencies.
848 */
849 _name = (char *)(strs + vdap->vda_name);
850
851 vdp = gvers_desc(_name, elf_hash(_name), &verdefs, file);
852 vdp->vd_ndx = ndx;
853 vdp->vd_flags = vdf->vd_flags | FLG_VER_AVAIL;
854
855 vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next);
856 for (cnt--; cnt; cnt--,
857 vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next)) {
858 _name = (char *)(strs + vdap->vda_name);
859 if (gvers_depend(_name, elf_hash(_name), vdp,
860 &verdefs, file) == NULL)
861 return (0);
862 }
863
864 /*
865 * Remember the base version for possible later use.
866 */
867 if (ndx == VER_NDX_GLOBAL)
868 bvdp = vdp;
869 }
870
871 /*
872 * Normalize the dependency list if required.
873 */
874 if (nflag) {
875 for (APLIST_TRAVERSE(verdefs, idx1, vdp)) {
876 Aliste idx2;
877 GVer_desc *_vdp;
878 int type = vdp->vd_flags & VER_FLG_WEAK;
879
880 for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp))
881 gvers_derefer(_vdp, type);
882 }
883
884 /*
885 * Always dereference the base version.
886 */
887 if (bvdp)
888 bvdp->vd_flags &= ~FLG_VER_AVAIL;
889 }
890
891
892 /*
893 * Traverse the dependency list and print out the appropriate
894 * information.
895 */
896 for (APLIST_TRAVERSE(verdefs, idx1, vdp)) {
897 Aliste idx2;
898 GVer_desc *_vdp;
899 int count;
900
901 if (!match(NULL, vdp->vd_name, vdp->vd_ndx))
902 continue;
903 if ((alist_nitems(match_list) == 0) &&
904 !(vdp->vd_flags & FLG_VER_AVAIL))
905 continue;
906
907 error = 1;
908
909 if (vflag) {
910 /*
911 * If the verbose flag is set determine if this version
912 * has a `weak' attribute, and print any version
913 * dependencies this version inherits.
914 */
915 if (oflag)
916 (void) printf(MSG_ORIG(MSG_FMT_OFIL), file);
917 (void) printf(MSG_ORIG(MSG_FMT_VER_NAME), vdp->vd_name);
918 if ((vdp->vd_flags & MSK_VER_USER) != 0) {
919 Conv_ver_flags_buf_t ver_flags_buf;
920
921 (void) printf(MSG_ORIG(MSG_FMT_VER_FLG),
922 conv_ver_flags(
923 vdp->vd_flags & MSK_VER_USER,
924 CONV_FMT_NOBKT, &ver_flags_buf));
925 }
926
927 count = 1;
928 for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) {
929 const char *_name = _vdp->vd_name;
930
931 if (count++ == 1) {
932
933 if (oflag)
934 (void) printf(
935 MSG_ORIG(MSG_FMT_IN_OFLG),
936 _name);
937 else if (vdp->vd_flags & VER_FLG_WEAK)
938 (void) printf(
939 MSG_ORIG(MSG_FMT_IN_WEAK),
940 _name);
941 else
942 (void) printf(
943 MSG_ORIG(MSG_FMT_IN),
944 _name);
945 } else
946 (void) printf(
947 MSG_ORIG(MSG_FMT_LIST_NEXT), _name);
948 }
949
950 if (count != 1)
951 (void) printf(MSG_ORIG(MSG_FMT_IN_END));
952
953 if (vsdata && !oflag)
954 (void) printf(MSG_ORIG(MSG_FMT_COL_NL));
955 else
956 (void) printf(MSG_ORIG(MSG_FMT_SEM_NL));
957 } else {
958 if (vsdata && !oflag)
959 (void) printf(MSG_ORIG(MSG_FMT_TNCO),
960 vdp->vd_name);
961 else if (!vsdata) {
962 if (oflag)
963 (void) printf(MSG_ORIG(MSG_FMT_OFIL),
964 file);
965 (void) printf(MSG_ORIG(MSG_FMT_TNSE),
966 vdp->vd_name);
967 }
968 }
969
970 /* If we are not printing symbols, we're done */
971 if (vsdata == NULL)
972 continue;
973
974 /*
975 * If a specific version to match has been specified then
976 * display any of its own symbols plus any inherited from
977 * other versions. Otherwise simply print out the symbols
978 * for this version.
979 */
980 gvers_syms(vsdata, vdp->vd_ndx, vdp->vd_name, NULL, file);
981 if (alist_nitems(match_list) != 0) {
982 recurse_syms(vsdata, vdp, file);
983
984 /*
985 * If the verbose flag is set, and this is not
986 * the base version, then add the base version as a
987 * dependency.
988 */
989 if (vflag && bvdp &&
990 !match(NULL, bvdp->vd_name, bvdp->vd_ndx)) {
991 if (!oflag)
992 (void) printf(MSG_ORIG(MSG_FMT_TNCO),
993 bvdp->vd_name);
994 gvers_syms(vsdata, bvdp->vd_ndx,
995 bvdp->vd_name, NULL, file);
996 }
997 }
998 }
999 return (error);
1000 }
1001
1002 int
main(int argc,char ** argv,char ** envp)1003 main(int argc, char **argv, char **envp)
1004 {
1005 GElf_Shdr shdr;
1006 Elf *elf;
1007 Elf_Scn *scn;
1008 Elf_Data *data;
1009 GElf_Ehdr ehdr;
1010 int nfile, var;
1011 char *names;
1012 Cache *cache, *_cache;
1013 Cache *_cache_def, *_cache_need, *_cache_sym, *_cache_loc;
1014 int error = 0;
1015 Gver_sym_data vsdata_s;
1016 const Gver_sym_data *vsdata = NULL;
1017
1018 /*
1019 * Check for a binary that better fits this architecture.
1020 */
1021 (void) conv_check_native(argv, envp);
1022
1023 /*
1024 * Establish locale.
1025 */
1026 (void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
1027 (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
1028
1029 cname = argv[0];
1030 Cflag = dflag = lflag = nflag = oflag = rflag = sflag = vflag = 0;
1031
1032 opterr = 0;
1033 while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
1034 switch (var) {
1035 case 'C':
1036 Cflag = USR_DEFINED;
1037 break;
1038 case 'd':
1039 dflag = USR_DEFINED;
1040 break;
1041 case 'l':
1042 lflag = sflag = USR_DEFINED;
1043 break;
1044 case 'n':
1045 nflag = USR_DEFINED;
1046 break;
1047 case 'o':
1048 oflag = USR_DEFINED;
1049 break;
1050 case 'r':
1051 rflag = USR_DEFINED;
1052 break;
1053 case 's':
1054 sflag = USR_DEFINED;
1055 break;
1056 case 'v':
1057 vflag = USR_DEFINED;
1058 break;
1059 case 'I':
1060 case 'N':
1061 add_match_record(var, optarg);
1062 break;
1063 case '?':
1064 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
1065 cname);
1066 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL));
1067 exit(1);
1068 default:
1069 break;
1070 }
1071 }
1072
1073 /*
1074 * No files specified on the command line?
1075 */
1076 if ((nfile = argc - optind) == 0) {
1077 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), cname);
1078 exit(1);
1079 }
1080
1081 /*
1082 * By default print both version definitions and needed dependencies.
1083 */
1084 if ((dflag == 0) && (rflag == 0) && (lflag == 0))
1085 dflag = rflag = DEF_DEFINED;
1086
1087 /*
1088 * Open the input file and initialize the elf interface.
1089 */
1090 for (; optind < argc; optind++) {
1091 int derror = 0, nerror = 0, err;
1092 const char *file = argv[optind];
1093 size_t shnum = 0;
1094
1095 if ((var = open(file, O_RDONLY)) == -1) {
1096 err = errno;
1097 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
1098 cname, file, strerror(err));
1099 error = 1;
1100 continue;
1101 }
1102 (void) elf_version(EV_CURRENT);
1103 if ((elf = elf_begin(var, ELF_C_READ, NULL)) == NULL) {
1104 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_BEGIN), cname,
1105 file, elf_errmsg(elf_errno()));
1106 error = 1;
1107 (void) close(var);
1108 continue;
1109 }
1110 if (elf_kind(elf) != ELF_K_ELF) {
1111 (void) fprintf(stderr, MSG_INTL(MSG_ELF_NOTELF), cname,
1112 file);
1113 error = 1;
1114 (void) close(var);
1115 (void) elf_end(elf);
1116 continue;
1117 }
1118 if (gelf_getehdr(elf, &ehdr) == NULL) {
1119 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETEHDR), cname,
1120 file, elf_errmsg(elf_errno()));
1121 error = 1;
1122 (void) close(var);
1123 (void) elf_end(elf);
1124 continue;
1125 }
1126
1127 /*
1128 * Obtain the .shstrtab data buffer to provide the required
1129 * section name strings.
1130 */
1131 if ((scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL) {
1132 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETSCN), cname,
1133 file, elf_errmsg(elf_errno()));
1134 error = 1;
1135 (void) close(var);
1136 (void) elf_end(elf);
1137 continue;
1138 }
1139 if ((data = elf_getdata(scn, NULL)) == NULL) {
1140 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETDATA), cname,
1141 file, elf_errmsg(elf_errno()));
1142 error = 1;
1143 (void) close(var);
1144 (void) elf_end(elf);
1145 continue;
1146 }
1147 names = data->d_buf;
1148
1149 /*
1150 * Fill in the cache descriptor with information for each
1151 * section we might need. We probably only need to save
1152 * read-only allocable sections as this is where the version
1153 * structures and their associated symbols and strings live.
1154 * However, God knows what someone can do with a mapfile, and
1155 * as elf_begin has already gone through all the overhead we
1156 * might as well set up the cache for every section.
1157 */
1158 if (elf_getshdrnum(elf, &shnum) == -1) {
1159 (void) fprintf(stderr, MSG_ORIG(MSG_ELF_GETSHDRNUM),
1160 cname, file, elf_errmsg(elf_errno()));
1161 exit(1);
1162 }
1163
1164 if ((cache = calloc(shnum, sizeof (Cache))) == NULL) {
1165 int err = errno;
1166 (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
1167 file, strerror(err));
1168 exit(1);
1169 }
1170
1171 _cache_def = _cache_need = _cache_sym = _cache_loc = NULL;
1172 _cache = cache;
1173 _cache++;
1174 for (scn = NULL; scn = elf_nextscn(elf, scn); _cache++) {
1175 if (gelf_getshdr(scn, &shdr) == NULL) {
1176 (void) fprintf(stderr,
1177 MSG_ORIG(MSG_ELF_GETSHDR), cname, file,
1178 elf_errmsg(elf_errno()));
1179 error = 1;
1180 continue;
1181 }
1182 if ((_cache->c_data = elf_getdata(scn, NULL)) ==
1183 NULL) {
1184 (void) fprintf(stderr,
1185 MSG_ORIG(MSG_ELF_GETDATA), cname, file,
1186 elf_errmsg(elf_errno()));
1187 error = 1;
1188 continue;
1189 }
1190 _cache->c_scn = scn;
1191 _cache->c_name = names + shdr.sh_name;
1192
1193 /*
1194 * Remember the version sections and symbol table.
1195 */
1196 switch (shdr.sh_type) {
1197 case SHT_SUNW_verdef:
1198 if (dflag)
1199 _cache_def = _cache;
1200 break;
1201 case SHT_SUNW_verneed:
1202 if (rflag)
1203 _cache_need = _cache;
1204 break;
1205 case SHT_SUNW_versym:
1206 if (sflag)
1207 _cache_sym = _cache;
1208 break;
1209 case SHT_SYMTAB:
1210 if (lflag)
1211 _cache_loc = _cache;
1212 break;
1213 }
1214 }
1215
1216 /*
1217 * Before printing anything out determine if any warnings are
1218 * necessary.
1219 */
1220 if (lflag && (_cache_loc == NULL)) {
1221 (void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS),
1222 cname, file);
1223 (void) fprintf(stderr, MSG_INTL(MSG_VER_NOSYMTAB));
1224 }
1225
1226 /*
1227 * If there is more than one input file, and we're not printing
1228 * one-line output, display the filename being processed.
1229 */
1230 if ((nfile > 1) && !oflag)
1231 (void) printf(MSG_ORIG(MSG_FMT_FILE), file);
1232
1233 /*
1234 * If we're printing symbols, then collect the data
1235 * necessary to do that.
1236 */
1237 if (_cache_sym != NULL) {
1238 vsdata = &vsdata_s;
1239 (void) gelf_getshdr(_cache_sym->c_scn, &shdr);
1240 vsdata_s.vsd_vsp =
1241 (GElf_Versym *)_cache_sym->c_data->d_buf;
1242 vsdata_s.vsd_sym_data = cache[shdr.sh_link].c_data;
1243 (void) gelf_getshdr(cache[shdr.sh_link].c_scn, &shdr);
1244 vsdata_s.vsd_symn = shdr.sh_size / shdr.sh_entsize;
1245 vsdata_s.vsd_strs =
1246 (const char *)cache[shdr.sh_link].c_data->d_buf;
1247 }
1248
1249
1250 /*
1251 * Print the files version needed sections.
1252 */
1253 if (_cache_need)
1254 nerror = gvers_need(cache, _cache_need, vsdata, file);
1255
1256 /*
1257 * Print the files version definition sections.
1258 */
1259 if (_cache_def)
1260 derror = gvers_def(cache, _cache_def, vsdata, file);
1261
1262 /*
1263 * Print any local symbol reductions.
1264 */
1265 if (_cache_loc)
1266 sym_local(cache, _cache_loc, file);
1267
1268 /*
1269 * Determine the error return. There are three conditions that
1270 * may produce an error (a non-zero return):
1271 *
1272 * o if the user specified -d and no version definitions
1273 * were found.
1274 *
1275 * o if the user specified -r and no version requirements
1276 * were found.
1277 *
1278 * o if the user specified neither -d or -r, (thus both are
1279 * enabled by default), and no version definitions or
1280 * version dependencies were found.
1281 */
1282 if (((dflag == USR_DEFINED) && (derror == 0)) ||
1283 ((rflag == USR_DEFINED) && (nerror == 0)) ||
1284 (rflag && dflag && (derror == 0) && (nerror == 0)))
1285 error = 1;
1286
1287 (void) close(var);
1288 (void) elf_end(elf);
1289 free(cache);
1290 }
1291 return (error);
1292 }
1293
1294 const char *
_pvs_msg(Msg mid)1295 _pvs_msg(Msg mid)
1296 {
1297 return (gettext(MSG_ORIG(mid)));
1298 }
1299