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.
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.
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]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Analyze the versioning information within a file.
30 * -C demangle C++ symbol names.
32 * -d dump version definitions.
34 * -l print reduced (local) symbols. Implies -s.
36 * -n normalize any version definitions.
38 * -o dump output in one-line fashion (more suitable for grep'ing
41 * -r dump the version requirements on library dependencies
43 * -s display the symbols associated with each version definition.
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.
51 * -I index only print the specifed version index, or index range.
53 * -N name only print the specifed `name'.
73 * Define Alist initialization sizes.
75 #define AL_CNT_MATCH_LIST 5 /* match_list initial alist count */
76 #define AL_CNT_GVER_DESC 25 /* version tracking descriptors */
78 typedef struct cache
{
84 typedef struct gver_desc
{
86 unsigned long vd_hash
;
92 /* Versym related data used by gvers_syms() */
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 */
101 * Type used to manage -I and -N options:
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.
107 * -N options come in two forms:
110 * 2) needobj (version)
112 * The meaning of the first case depends on the type of
113 * version record being matched:
115 * VERDEF - name is the name of a version defined
116 * by the object being processed (i.e. SUNW_1.1).
118 * VERNEED - name is the name of the object file
119 * on which the dependency exists (i.e. libc.so.1).
121 * -N options of the second form only apply to VERNEED records.
122 * They are used to specify a version from a needed object.
124 /* match_opt_t is used to note which match option was used */
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 */
133 match_opt_t opt_type
;
136 const char *version
; /* MATCH_OPT_{NAME|NEED_VER} */
137 const char *needobj
; /* MATCH_OPT_NEED_VER only */
140 int start
; /* MATCH_OPT_{NDX|RANGE} */
141 int end
; /* MATCH_OPT_RANGE only) */
148 static const char *cname
;
149 static int Cflag
, dflag
, lflag
, nflag
, oflag
, rflag
, sflag
, vflag
;
150 static Alist
*match_list
;
152 /* Used to track whether an option defaulted to on, or was explicitly set */
153 #define DEF_DEFINED 1
154 #define USR_DEFINED 2
157 * Determine whether a symbol name should be demangled.
160 demangle(const char *name
)
163 return (Elf_demangle_name(name
));
169 * Append an item to the specified list, and return a pointer to the list
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.
178 pvs_aplist_append(APlist
**lst
, const void *item
, const char *file
)
180 if (aplist_append(lst
, item
, AL_CNT_GVER_DESC
) == NULL
) {
182 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_MALLOC
), cname
, file
,
189 * Add an entry to match_list for use by match(). This routine is for
190 * use during getopt() processing.
193 * opt - One of 'N' or 'I', indicating the option
194 * str - Value string corresponding to opt
197 * The new match record has been added. On error, a fatal
198 * error is issued and and the process exits.
201 add_match_record(int opt
, const char *str
)
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.
213 #define WS_SKIP(_str) for (; *(_str) && isspace(*(_str)); (_str)++)
214 #define WS_SKIP_LIMIT(_str, _limit) \
215 while (((_str) < s2) && isspace(*(_str))) \
217 #define WS_RSKIP_LIMIT(_str, _tail) \
218 while (((_tail) > (_str)) && isspace(*((_tail) - 1))) \
224 char *lstr
, *s1
, *s2
;
226 rec
= alist_append(&match_list
, NULL
, sizeof (match_rec_t
),
230 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_MALLOC
), cname
,
231 MSG_INTL(MSG_STR_MATCH_RECORD
), strerror(err
));
236 if ((lstr
= strdup(str
)) == NULL
) {
238 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_MALLOC
),
239 cname
, MSG_INTL(MSG_STR_MATCH_RECORD
),
244 /* Strip leading/trailing whitespace */
245 s2
= lstr
+ strlen(lstr
);
246 WS_SKIP_LIMIT(lstr
, s2
);
247 WS_RSKIP_LIMIT(lstr
, s2
);
249 /* Assume this is a plain string */
250 rec
->opt_type
= MATCH_OPT_NAME
;
251 rec
->value
.name
.version
= lstr
;
254 * If s2 points at a closing paren, then this might
255 * be a MATCH_OPT_NEED_VER case. Otherwise we're done.
257 if ((s2
== lstr
) || (*(s2
- 1) != ')'))
260 /* We have a closing paren. Locate the opening one. */
261 for (s1
= lstr
; *s1
&& (*s1
!= '('); s1
++)
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 */
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
);
275 /* Terminate needobj, skipping trailing whitespace */
276 WS_RSKIP_LIMIT(rec
->value
.name
.needobj
, s1
);
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))
291 rec
->opt_type
= MATCH_OPT_NDX
;
293 str
++; /* Skip the ':' */
294 rec
->opt_type
= MATCH_OPT_RANGE
;
297 rec
->value
.ndx
.end
= -1; /* Indicates "to end" */
299 rec
->value
.ndx
.end
= strtol(str
, &s2
, 10);
300 if ((str
== s2
) || (rec
->value
.ndx
.end
< 0))
307 /* If we are successful, there is nothing left to parse */
312 * If we get here, there is leftover input. Fall through
313 * to issue a syntax error.
316 (void) fprintf(stderr
, MSG_INTL(MSG_USAGE_BRIEF
), cname
);
321 #undef WS_RSKIP_LIMIT
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.
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.
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.
341 match(const char *needobj
, const char *version
, int ndx
)
347 /* If there is no match list, then we approve everything */
348 if (alist_nitems(match_list
) == 0)
351 /* Run through the match records and check for a hit */
352 for (ALIST_TRAVERSE(match_list
, _idx
, rec
)) {
353 switch (rec
->opt_type
) {
361 if (strcmp(rec
->value
.name
.version
, str
) == 0)
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))
371 if ((ndx
> 0) && (ndx
== rec
->value
.ndx
.start
))
374 case MATCH_OPT_RANGE
:
376 * A range end value less than 0 means that any value
377 * above the start is acceptible.
380 (ndx
>= rec
->value
.ndx
.start
) &&
381 ((rec
->value
.ndx
.end
< 0) ||
382 (ndx
<= rec
->value
.ndx
.end
)))
388 /* Nothing matched */
393 * List the symbols that belong to a specified version
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.
405 gvers_syms(const Gver_sym_data
*vsdata
, GElf_Half vd_ndx
,
406 const char *vd_name
, const char *needobj
, const char *file
)
411 for (_symn
= 0; _symn
< vsdata
->vsd_symn
; _symn
++) {
415 if (vsdata
->vsd_vsp
[_symn
] != vd_ndx
)
418 (void) gelf_getsym(vsdata
->vsd_sym_data
, _symn
, &sym
);
419 name
= demangle(vsdata
->vsd_strs
+ sym
.st_name
);
422 * Symbols that reference a VERDEF record
423 * have some extra details to handle.
425 if (needobj
== NULL
) {
427 * For data symbols defined by this object,
428 * determine the size.
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
;
436 * Only output the version symbol when the verbose
439 if (!vflag
&& (sym
.st_shndx
== SHN_ABS
) &&
440 (strcmp(name
, vd_name
) == 0))
446 (void) printf(MSG_ORIG(MSG_FMT_SYM_OFIL
),
449 (void) printf(MSG_ORIG(MSG_FMT_SYM_NEED_OFIL
),
450 file
, needobj
, vd_name
);
453 (void) printf(MSG_ORIG(MSG_FMT_SYM_SZ_OFLG
),
454 name
, (ulong_t
)size
);
456 (void) printf(MSG_ORIG(MSG_FMT_SYM_OFLG
), name
);
459 (void) printf(MSG_ORIG(MSG_FMT_SYM_SZ
), name
,
462 (void) printf(MSG_ORIG(MSG_FMT_SYM
), name
);
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.
473 sym_local(Cache
*cache
, Cache
*csym
, const char *file
)
475 int symn
, _symn
, found
= 0;
480 (void) gelf_getshdr(csym
->c_scn
, &shdr
);
481 strs
= (char *)cache
[shdr
.sh_link
].c_data
->d_buf
;
486 * Verify symtab[1] is the output file symbol.
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
,
492 (void) fprintf(stderr
, MSG_INTL(MSG_VER_NOTSTTFILE
),
498 * Scan the remaining symbols until the next file symbol is found.
500 for (_symn
= 2; _symn
< symn
; _symn
++) {
503 (void) gelf_getsym(csym
->c_data
, _symn
, &sym
);
504 if (GELF_ST_TYPE(sym
.st_info
) == STT_SECTION
)
506 if (GELF_ST_TYPE(sym
.st_info
) == STT_FILE
)
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.
514 if (GELF_ST_BIND(sym
.st_info
) != STB_LOCAL
)
517 name
= demangle(strs
+ sym
.st_name
);
520 (void) printf(MSG_ORIG(MSG_FMT_LOCSYM_OFLG
),
525 (void) printf(MSG_ORIG(MSG_FMT_LOCSYM_HDR
));
527 (void) printf(MSG_ORIG(MSG_FMT_LOCSYM
), name
);
533 * Print data from the files VERNEED section.
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:
539 * libc.so.1 (SUNW_1.7):
542 * libc.so.1 (SUNW_1.9):
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.
550 * libc.so.1 (SUNW_1.7, SUNW_1.9);
553 gvers_need(Cache
*cache
, Cache
*need
, const Gver_sym_data
*vsdata
,
556 unsigned int num
, _num
;
558 GElf_Verneed
*vnd
= need
->c_data
->d_buf
;
561 int show
= vflag
|| (vsdata
== NULL
) || !oflag
;
564 (void) gelf_getshdr(need
->c_scn
, &shdr
);
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.
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
);
576 * Get the data buffer for the associated string table.
578 strs
= (char *)cache
[shdr
.sh_link
].c_data
->d_buf
;
581 for (_num
= 1; _num
<= num
; _num
++,
582 vnd
= (GElf_Verneed
*)((uintptr_t)vnd
+ vnd
->vn_next
)) {
585 const char *needobj
, *dep
;
586 int started
= 0, listcnt
= 0;
588 vnap
= (GElf_Vernaux
*) ((uintptr_t)vnd
+ vnd
->vn_aux
);
590 /* Obtain the needed object file name */
591 needobj
= (char *)(strs
+ vnd
->vn_file
);
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
;
600 dep
= (char *)(strs
+ vnap
->vna_name
);
602 if (!match(needobj
, dep
, vnap
->vna_other
))
606 if ((started
== 0) || (vsdata
!= NULL
)) {
608 * If one-line ouput is called for
609 * display the filename being processed.
613 MSG_ORIG(MSG_FMT_OFIL
),
617 MSG_ORIG(MSG_FMT_LIST_BEGIN
),
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.
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
);
638 (void) printf(fmt
, dep
);
640 /* Show non-zero flags */
641 if (vflag
&& (vnap
->vna_flags
!= 0))
643 MSG_ORIG(MSG_FMT_VER_FLG
),
650 (void) printf(oflag
?
651 MSG_ORIG(MSG_FMT_LIST_END_SEM
) :
652 MSG_ORIG(MSG_FMT_LIST_END_COL
));
656 * If we are showing symbols, and vna_other is
657 * non-zero, list them here.
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.
664 if (vsdata
&& (vnap
->vna_other
> 0))
665 gvers_syms(vsdata
, vnap
->vna_other
,
668 if (show
&& started
&& (vsdata
== NULL
))
669 (void) printf(MSG_ORIG(MSG_FMT_LIST_END_SEM
));
675 * Return a GVer_desc descriptor for the given version if one
679 * name - Version name
680 * hash - ELF hash of name
681 * lst - APlist of existing descriptors.
682 * file - Object file containing the version
685 * Return the corresponding GVer_desc struct if it
686 * exists, and NULL otherwise.
689 gvers_find(const char *name
, unsigned long hash
, APlist
*lst
)
694 for (APLIST_TRAVERSE(lst
, idx
, vdp
))
695 if ((vdp
->vd_hash
== hash
) &&
696 (strcmp(vdp
->vd_name
, name
) == 0))
703 * Return a GVer_desc descriptor for the given version.
706 * name - Version name
707 * hash - ELF hash of name
708 * lst - List of existing descriptors.
709 * file - Object file containing the version
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.
717 gvers_desc(const char *name
, unsigned long hash
, APlist
**lst
, const char *file
)
721 if ((vdp
= gvers_find(name
, hash
, *lst
)) == NULL
) {
722 if ((vdp
= calloc(sizeof (GVer_desc
), 1)) == NULL
) {
724 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_MALLOC
), cname
,
725 file
, strerror(err
));
732 pvs_aplist_append(lst
, vdp
, file
);
738 * Insert a version dependency for the given GVer_desc descriptor.
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
746 * file - Object file containing the version
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.
755 gvers_depend(const char *name
, unsigned long hash
, GVer_desc
*vdp
, APlist
**lst
,
760 _vdp
= gvers_desc(name
, hash
, lst
, file
);
761 pvs_aplist_append(&vdp
->vd_deps
, _vdp
, file
);
766 gvers_derefer(GVer_desc
*vdp
, int weak
)
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.
776 if ((weak
&& (vdp
->vd_flags
& VER_FLG_WEAK
)) || (!weak
))
777 vdp
->vd_flags
&= ~FLG_VER_AVAIL
;
779 for (APLIST_TRAVERSE(vdp
->vd_deps
, idx
, _vdp
))
780 gvers_derefer(_vdp
, weak
);
785 recurse_syms(const Gver_sym_data
*vsdata
, GVer_desc
*vdp
, const char *file
)
790 for (APLIST_TRAVERSE(vdp
->vd_deps
, idx
, _vdp
)) {
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
);
801 * Print the files version definition sections.
804 gvers_def(Cache
*cache
, Cache
*def
, const Gver_sym_data
*vsdata
,
807 unsigned int num
, _num
;
809 GElf_Verdef
*vdf
= def
->c_data
->d_buf
;
811 GVer_desc
*vdp
, *bvdp
= NULL
;
813 APlist
*verdefs
= NULL
;
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.
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
);
827 * Get the data buffer for the associated string table.
829 (void) gelf_getshdr(def
->c_scn
, &shdr
);
830 strs
= (char *)cache
[shdr
.sh_link
].c_data
->d_buf
;
834 * Process the version definitions placing each on a version dependency
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
;
844 vdap
= (GElf_Verdaux
*)((uintptr_t)vdf
+ vdf
->vd_aux
);
847 * Determine the version name and any dependencies.
849 _name
= (char *)(strs
+ vdap
->vda_name
);
851 vdp
= gvers_desc(_name
, elf_hash(_name
), &verdefs
, file
);
853 vdp
->vd_flags
= vdf
->vd_flags
| FLG_VER_AVAIL
;
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
)
865 * Remember the base version for possible later use.
867 if (ndx
== VER_NDX_GLOBAL
)
872 * Normalize the dependency list if required.
875 for (APLIST_TRAVERSE(verdefs
, idx1
, vdp
)) {
878 int type
= vdp
->vd_flags
& VER_FLG_WEAK
;
880 for (APLIST_TRAVERSE(vdp
->vd_deps
, idx2
, _vdp
))
881 gvers_derefer(_vdp
, type
);
885 * Always dereference the base version.
888 bvdp
->vd_flags
&= ~FLG_VER_AVAIL
;
893 * Traverse the dependency list and print out the appropriate
896 for (APLIST_TRAVERSE(verdefs
, idx1
, vdp
)) {
901 if (!match(NULL
, vdp
->vd_name
, vdp
->vd_ndx
))
903 if ((alist_nitems(match_list
) == 0) &&
904 !(vdp
->vd_flags
& FLG_VER_AVAIL
))
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.
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
;
921 (void) printf(MSG_ORIG(MSG_FMT_VER_FLG
),
923 vdp
->vd_flags
& MSK_VER_USER
,
924 CONV_FMT_NOBKT
, &ver_flags_buf
));
928 for (APLIST_TRAVERSE(vdp
->vd_deps
, idx2
, _vdp
)) {
929 const char *_name
= _vdp
->vd_name
;
935 MSG_ORIG(MSG_FMT_IN_OFLG
),
937 else if (vdp
->vd_flags
& VER_FLG_WEAK
)
939 MSG_ORIG(MSG_FMT_IN_WEAK
),
943 MSG_ORIG(MSG_FMT_IN
),
947 MSG_ORIG(MSG_FMT_LIST_NEXT
), _name
);
951 (void) printf(MSG_ORIG(MSG_FMT_IN_END
));
953 if (vsdata
&& !oflag
)
954 (void) printf(MSG_ORIG(MSG_FMT_COL_NL
));
956 (void) printf(MSG_ORIG(MSG_FMT_SEM_NL
));
958 if (vsdata
&& !oflag
)
959 (void) printf(MSG_ORIG(MSG_FMT_TNCO
),
963 (void) printf(MSG_ORIG(MSG_FMT_OFIL
),
965 (void) printf(MSG_ORIG(MSG_FMT_TNSE
),
970 /* If we are not printing symbols, we're done */
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
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
);
985 * If the verbose flag is set, and this is not
986 * the base version, then add the base version as a
990 !match(NULL
, bvdp
->vd_name
, bvdp
->vd_ndx
)) {
992 (void) printf(MSG_ORIG(MSG_FMT_TNCO
),
994 gvers_syms(vsdata
, bvdp
->vd_ndx
,
995 bvdp
->vd_name
, NULL
, file
);
1003 main(int argc
, char **argv
, char **envp
)
1012 Cache
*cache
, *_cache
;
1013 Cache
*_cache_def
, *_cache_need
, *_cache_sym
, *_cache_loc
;
1015 Gver_sym_data vsdata_s
;
1016 const Gver_sym_data
*vsdata
= NULL
;
1019 * Check for a binary that better fits this architecture.
1021 (void) conv_check_native(argv
, envp
);
1026 (void) setlocale(LC_MESSAGES
, MSG_ORIG(MSG_STR_EMPTY
));
1027 (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS
));
1030 Cflag
= dflag
= lflag
= nflag
= oflag
= rflag
= sflag
= vflag
= 0;
1033 while ((var
= getopt(argc
, argv
, MSG_ORIG(MSG_STR_OPTIONS
))) != EOF
) {
1036 Cflag
= USR_DEFINED
;
1039 dflag
= USR_DEFINED
;
1042 lflag
= sflag
= USR_DEFINED
;
1045 nflag
= USR_DEFINED
;
1048 oflag
= USR_DEFINED
;
1051 rflag
= USR_DEFINED
;
1054 sflag
= USR_DEFINED
;
1057 vflag
= USR_DEFINED
;
1061 add_match_record(var
, optarg
);
1064 (void) fprintf(stderr
, MSG_INTL(MSG_USAGE_BRIEF
),
1066 (void) fprintf(stderr
, MSG_INTL(MSG_USAGE_DETAIL
));
1074 * No files specified on the command line?
1076 if ((nfile
= argc
- optind
) == 0) {
1077 (void) fprintf(stderr
, MSG_INTL(MSG_USAGE_BRIEF
), cname
);
1082 * By default print both version definitions and needed dependencies.
1084 if ((dflag
== 0) && (rflag
== 0) && (lflag
== 0))
1085 dflag
= rflag
= DEF_DEFINED
;
1088 * Open the input file and initialize the elf interface.
1090 for (; optind
< argc
; optind
++) {
1091 int derror
= 0, nerror
= 0, err
;
1092 const char *file
= argv
[optind
];
1095 if ((var
= open(file
, O_RDONLY
)) == -1) {
1097 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_OPEN
),
1098 cname
, file
, strerror(err
));
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()));
1110 if (elf_kind(elf
) != ELF_K_ELF
) {
1111 (void) fprintf(stderr
, MSG_INTL(MSG_ELF_NOTELF
), cname
,
1115 (void) elf_end(elf
);
1118 if (gelf_getehdr(elf
, &ehdr
) == NULL
) {
1119 (void) fprintf(stderr
, MSG_ORIG(MSG_ELF_GETEHDR
), cname
,
1120 file
, elf_errmsg(elf_errno()));
1123 (void) elf_end(elf
);
1128 * Obtain the .shstrtab data buffer to provide the required
1129 * section name strings.
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()));
1136 (void) elf_end(elf
);
1139 if ((data
= elf_getdata(scn
, NULL
)) == NULL
) {
1140 (void) fprintf(stderr
, MSG_ORIG(MSG_ELF_GETDATA
), cname
,
1141 file
, elf_errmsg(elf_errno()));
1144 (void) elf_end(elf
);
1147 names
= data
->d_buf
;
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.
1158 if (elf_getshdrnum(elf
, &shnum
) == -1) {
1159 (void) fprintf(stderr
, MSG_ORIG(MSG_ELF_GETSHDRNUM
),
1160 cname
, file
, elf_errmsg(elf_errno()));
1164 if ((cache
= calloc(shnum
, sizeof (Cache
))) == NULL
) {
1166 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_MALLOC
), cname
,
1167 file
, strerror(err
));
1171 _cache_def
= _cache_need
= _cache_sym
= _cache_loc
= NULL
;
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()));
1182 if ((_cache
->c_data
= elf_getdata(scn
, NULL
)) ==
1184 (void) fprintf(stderr
,
1185 MSG_ORIG(MSG_ELF_GETDATA
), cname
, file
,
1186 elf_errmsg(elf_errno()));
1190 _cache
->c_scn
= scn
;
1191 _cache
->c_name
= names
+ shdr
.sh_name
;
1194 * Remember the version sections and symbol table.
1196 switch (shdr
.sh_type
) {
1197 case SHT_SUNW_verdef
:
1199 _cache_def
= _cache
;
1201 case SHT_SUNW_verneed
:
1203 _cache_need
= _cache
;
1205 case SHT_SUNW_versym
:
1207 _cache_sym
= _cache
;
1211 _cache_loc
= _cache
;
1217 * Before printing anything out determine if any warnings are
1220 if (lflag
&& (_cache_loc
== NULL
)) {
1221 (void) fprintf(stderr
, MSG_INTL(MSG_VER_UNREDSYMS
),
1223 (void) fprintf(stderr
, MSG_INTL(MSG_VER_NOSYMTAB
));
1227 * If there is more than one input file, and we're not printing
1228 * one-line output, display the filename being processed.
1230 if ((nfile
> 1) && !oflag
)
1231 (void) printf(MSG_ORIG(MSG_FMT_FILE
), file
);
1234 * If we're printing symbols, then collect the data
1235 * necessary to do that.
1237 if (_cache_sym
!= NULL
) {
1239 (void) gelf_getshdr(_cache_sym
->c_scn
, &shdr
);
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
;
1246 (const char *)cache
[shdr
.sh_link
].c_data
->d_buf
;
1251 * Print the files version needed sections.
1254 nerror
= gvers_need(cache
, _cache_need
, vsdata
, file
);
1257 * Print the files version definition sections.
1260 derror
= gvers_def(cache
, _cache_def
, vsdata
, file
);
1263 * Print any local symbol reductions.
1266 sym_local(cache
, _cache_loc
, file
);
1269 * Determine the error return. There are three conditions that
1270 * may produce an error (a non-zero return):
1272 * o if the user specified -d and no version definitions
1275 * o if the user specified -r and no version requirements
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.
1282 if (((dflag
== USR_DEFINED
) && (derror
== 0)) ||
1283 ((rflag
== USR_DEFINED
) && (nerror
== 0)) ||
1284 (rflag
&& dflag
&& (derror
== 0) && (nerror
== 0)))
1288 (void) elf_end(elf
);
1297 return (gettext(MSG_ORIG(mid
)));