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