summaryrefslogtreecommitdiff
path: root/usr/src/cmd/sgs/pvs/common/pvs.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/sgs/pvs/common/pvs.c')
-rw-r--r--usr/src/cmd/sgs/pvs/common/pvs.c896
1 files changed, 645 insertions, 251 deletions
diff --git a/usr/src/cmd/sgs/pvs/common/pvs.c b/usr/src/cmd/sgs/pvs/common/pvs.c
index cc8cb42a8d..2ef648fc14 100644
--- a/usr/src/cmd/sgs/pvs/common/pvs.c
+++ b/usr/src/cmd/sgs/pvs/common/pvs.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Analyze the versioning information within a file.
*
@@ -33,7 +31,7 @@
*
* -d dump version definitions.
*
- * -l print reduced (local) symbols.
+ * -l print reduced (local) symbols. Implies -s.
*
* -n normalize any version definitions.
*
@@ -46,8 +44,11 @@
*
* -v verbose output. With the -r and -d options any WEAK attribute
* is displayed. With the -d option, any version inheritance,
- * and the base version are displayed. With the -s option the
- * version symbol is displayed.
+ * and the base version are displayed. With the -r option,
+ * WEAK and INFO attributes are displayed. With the -s option
+ * the version symbol is displayed.
+ *
+ * -I index only print the specifed version index, or index range.
*
* -N name only print the specifed `name'.
*/
@@ -64,9 +65,15 @@
#include <conv.h>
#include <gelf.h>
#include <debug.h>
+#include <ctype.h>
+#include <alist.h>
#include "msg.h"
-#define FLG_VER_AVAIL 0x10
+/*
+ * Define Alist initialization sizes.
+ */
+#define AL_CNT_MATCH_LIST 5 /* match_list initial alist count */
+#define AL_CNT_GVER_DESC 25 /* version tracking descriptors */
typedef struct cache {
Elf_Scn *c_scn;
@@ -79,21 +86,70 @@ typedef struct gver_desc {
unsigned long vd_hash;
GElf_Half vd_ndx;
GElf_Half vd_flags;
- List vd_deps;
+ APlist *vd_deps;
} GVer_desc;
+/* Versym related data used by gvers_syms() */
+typedef struct {
+ GElf_Versym *vsd_vsp; /* ptr to versym data */
+ Elf_Data *vsd_sym_data; /* ptr to symtab data */
+ Word vsd_symn; /* # of symbols in symtab */
+ const char *vsd_strs; /* string table data */
+} Gver_sym_data;
+
+/*
+ * Type used to manage -I and -N options:
+ *
+ * The -I option specifies a VERSYM index, or index range. The
+ * result is to select the VERDEF or VERNEED records with
+ * indexes that match those given.
+ *
+ * -N options come in two forms:
+ *
+ * 1) name
+ * 2) needobj (version)
+ *
+ * The meaning of the first case depends on the type of
+ * version record being matched:
+ *
+ * VERDEF - name is the name of a version defined
+ * by the object being processed (i.e. SUNW_1.1).
+ *
+ * VERNEED - name is the name of the object file
+ * on which the dependency exists (i.e. libc.so.1).
+ *
+ * -N options of the second form only apply to VERNEED records.
+ * They are used to specify a version from a needed object.
+ */
+/* match_opt_t is used to note which match option was used */
+typedef enum {
+ MATCH_OPT_NAME, /* Record contains a name */
+ MATCH_OPT_NEED_VER, /* Record contains needed object and version */
+ MATCH_OPT_NDX, /* Record contains a single index */
+ MATCH_OPT_RANGE, /* Record contains an index range */
+} match_opt_t;
+
+typedef struct {
+ match_opt_t opt_type;
+ union {
+ struct {
+ const char *version; /* MATCH_OPT_{NAME|NEED_VER} */
+ const char *needobj; /* MATCH_OPT_NEED_VER only */
+ } name;
+ struct {
+ int start; /* MATCH_OPT_{NDX|RANGE} */
+ int end; /* MATCH_OPT_RANGE only) */
+ } ndx;
+ } value;
+} match_rec_t;
+
+
+
static const char *cname;
static int Cflag, dflag, lflag, nflag, oflag, rflag, sflag, vflag;
+static Alist *match_list;
-static const char
- * Format_ofil = "%s -",
- * Format_tnco = "\t%s:\n",
- * Format_tnse = "\t%s;\n",
- * Format_bgnl = "\t%s (%s",
- * Format_next = ", %s",
- * Format_weak = " [WEAK]",
- * Format_endl = ");\n";
-
+/* Used to track whether an option defaulted to on, or was explicitly set */
#define DEF_DEFINED 1
#define USR_DEFINED 2
@@ -110,6 +166,305 @@ demangle(const char *name)
}
/*
+ * Append an item to the specified list, and return a pointer to the list
+ * node created.
+ *
+ * exit:
+ * On success, a new list node is created and the item is
+ * added to the list. On failure, a fatal error is issued
+ * and the process exits.
+ */
+static void
+pvs_aplist_append(APlist **lst, const void *item, const char *file)
+{
+ if (aplist_append(lst, item, AL_CNT_GVER_DESC) == NULL) {
+ int err = errno;
+ (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, file,
+ strerror(err));
+ exit(1);
+ }
+}
+
+/*
+ * Add an entry to match_list for use by match(). This routine is for
+ * use during getopt() processing.
+ *
+ * entry:
+ * opt - One of 'N' or 'I', indicating the option
+ * str - Value string corresponding to opt
+ *
+ * exit:
+ * The new match record has been added. On error, a fatal
+ * error is issued and and the process exits.
+ */
+static void
+add_match_record(int opt, const char *str)
+{
+ /*
+ * Macros for removing leading and trailing whitespace:
+ * WS_SKIP - Advance _str without passing the NULL termination,
+ * until the first character is not whitespace.
+ * WS_SKIP_LIMIT - Advance _str without passing _limit,
+ * until the first character is not whitespace.
+ * WS_RSKIP_LIMIT - Move _tail back without passing _str,
+ * until the character before it is not whitespace.
+ * Write a NULL termination at that point.
+ */
+#define WS_SKIP(_str) for (; *(_str) && isspace(*(_str)); (_str)++)
+#define WS_SKIP_LIMIT(_str, _limit) \
+ while (((_str) < s2) && isspace(*(_str))) \
+ (_str)++
+#define WS_RSKIP_LIMIT(_str, _tail) \
+ while (((_tail) > (_str)) && isspace(*((_tail) - 1))) \
+ (_tail)--; \
+ *(_tail) = '\0'
+
+
+ match_rec_t *rec;
+ char *lstr, *s1, *s2;
+
+ rec = alist_append(&match_list, NULL, sizeof (match_rec_t),
+ AL_CNT_MATCH_LIST);
+ if (rec == NULL) {
+ int err = errno;
+ (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
+ MSG_INTL(MSG_STR_MATCH_RECORD), strerror(err));
+ exit(1);
+ }
+
+ if (opt == 'N') {
+ if ((lstr = strdup(str)) == NULL) {
+ int err = errno;
+ (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC),
+ cname, MSG_INTL(MSG_STR_MATCH_RECORD),
+ strerror(err));
+ exit(1);
+ }
+
+ /* Strip leading/trailing whitespace */
+ s2 = lstr + strlen(lstr);
+ WS_SKIP_LIMIT(lstr, s2);
+ WS_RSKIP_LIMIT(lstr, s2);
+
+ /* Assume this is a plain string */
+ rec->opt_type = MATCH_OPT_NAME;
+ rec->value.name.version = lstr;
+
+ /*
+ * If s2 points at a closing paren, then this might
+ * be a MATCH_OPT_NEED_VER case. Otherwise we're done.
+ */
+ if ((s2 == lstr) || (*(s2 - 1) != ')'))
+ return;
+
+ /* We have a closing paren. Locate the opening one. */
+ for (s1 = lstr; *s1 && (*s1 != '('); s1++)
+ ;
+ if (*s1 != '(')
+ return;
+
+ rec->opt_type = MATCH_OPT_NEED_VER;
+ rec->value.name.needobj = lstr;
+ rec->value.name.version = s1 + 1;
+ s2--; /* Points at closing paren */
+
+ /* Remove whitespace from head/tail of version */
+ WS_SKIP_LIMIT(rec->value.name.version, s2);
+ WS_RSKIP_LIMIT(rec->value.name.version, s2);
+
+ /* Terminate needobj, skipping trailing whitespace */
+ WS_RSKIP_LIMIT(rec->value.name.needobj, s1);
+
+ return;
+ }
+
+
+ /* If we get here, we are looking at a -I index option */
+ rec->value.ndx.start = strtol(str, &s2, 10);
+ /* Value must use some of the input, and be positive */
+ if ((str == s2) || (rec->value.ndx.start < 1))
+ goto syntax_error;
+ str = s2;
+
+ WS_SKIP(str);
+ if (*str != ':') {
+ rec->opt_type = MATCH_OPT_NDX;
+ } else {
+ str++; /* Skip the ':' */
+ rec->opt_type = MATCH_OPT_RANGE;
+ WS_SKIP(str);
+ if (*str == '\0') {
+ rec->value.ndx.end = -1; /* Indicates "to end" */
+ } else {
+ rec->value.ndx.end = strtol(str, &s2, 10);
+ if ((str == s2) || (rec->value.ndx.end < 0))
+ goto syntax_error;
+ str = s2;
+ WS_SKIP(str);
+ }
+ }
+
+ /* If we are successful, there is nothing left to parse */
+ if (*str == '\0')
+ return;
+
+ /*
+ * If we get here, there is leftover input. Fall through
+ * to issue a syntax error.
+ */
+syntax_error:
+ (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), cname);
+ exit(1);
+
+#undef WS_SKIP
+#undef WS_SKIP_LIMIT
+#undef WS_RSKIP_LIMIT
+}
+
+/*
+ * Returns True (1) if the version with the given name or index should
+ * be displayed, and False (0) if it should not be.
+ *
+ * entry:
+ * needobj - NULL for VERDEF records, the name of the
+ * needed object for VERNEED.
+ * version - NULL, or needed version
+ * ndx - Versym index of version under consideration, or a value less
+ * than 1 to indicate that no valid index is given.
+ *
+ * exit:
+ * True will be returned if the given name/index matches those given
+ * by one of the -I or -N command line options, or if no such option
+ * was used in the command invocation.
+ */
+int
+match(const char *needobj, const char *version, int ndx)
+{
+ Aliste _idx;
+ match_rec_t *rec;
+ const char *str;
+
+ /* If there is no match list, then we approve everything */
+ if (alist_nitems(match_list) == 0)
+ return (1);
+
+ /* Run through the match records and check for a hit */
+ for (ALIST_TRAVERSE(match_list, _idx, rec)) {
+ switch (rec->opt_type) {
+ case MATCH_OPT_NAME:
+ if (needobj)
+ str = needobj;
+ else if (version)
+ str = version;
+ else
+ break;
+ if (strcmp(rec->value.name.version, str) == 0)
+ return (1);
+ break;
+ case MATCH_OPT_NEED_VER:
+ if (needobj && version &&
+ (strcmp(rec->value.name.needobj, needobj) == 0) &&
+ (strcmp(rec->value.name.version, version) == 0))
+ return (1);
+ break;
+ case MATCH_OPT_NDX:
+ if ((ndx > 0) && (ndx == rec->value.ndx.start))
+ return (1);
+ break;
+ case MATCH_OPT_RANGE:
+ /*
+ * A range end value less than 0 means that any value
+ * above the start is acceptible.
+ */
+ if ((ndx > 0) &&
+ (ndx >= rec->value.ndx.start) &&
+ ((rec->value.ndx.end < 0) ||
+ (ndx <= rec->value.ndx.end)))
+ return (1);
+ break;
+ }
+ }
+
+ /* Nothing matched */
+ return (0);
+}
+
+/*
+ * List the symbols that belong to a specified version
+ *
+ * entry:
+ * vsdata - VERSYM related data from the object
+ * vd_ndx - The VERSYM index for symbols to display
+ * vd_name - Version name
+ * needobj - NULL for symbols corresponding to a VERDEF
+ * record. Name of the needed object in the case
+ * of a VERNEED record.
+ * file - Object file
+ */
+static void
+gvers_syms(const Gver_sym_data *vsdata, GElf_Half vd_ndx,
+ const char *vd_name, const char *needobj, const char *file)
+{
+ GElf_Sym sym;
+ int _symn;
+
+ for (_symn = 0; _symn < vsdata->vsd_symn; _symn++) {
+ size_t size = 0;
+ const char *name;
+
+ if (vsdata->vsd_vsp[_symn] != vd_ndx)
+ continue;
+
+ (void) gelf_getsym(vsdata->vsd_sym_data, _symn, &sym);
+ name = demangle(vsdata->vsd_strs + sym.st_name);
+
+ /*
+ * Symbols that reference a VERDEF record
+ * have some extra details to handle.
+ */
+ if (needobj == NULL) {
+ /*
+ * For data symbols defined by this object,
+ * determine the size.
+ */
+ if ((GELF_ST_TYPE(sym.st_info) == STT_OBJECT) ||
+ (GELF_ST_TYPE(sym.st_info) == STT_COMMON) ||
+ (GELF_ST_TYPE(sym.st_info) == STT_TLS))
+ size = (size_t)sym.st_size;
+
+ /*
+ * Only output the version symbol when the verbose
+ * flag is used.
+ */
+ if (!vflag && (sym.st_shndx == SHN_ABS) &&
+ (strcmp(name, vd_name) == 0))
+ continue;
+ }
+
+ if (oflag) {
+ if (needobj == NULL)
+ (void) printf(MSG_ORIG(MSG_FMT_SYM_OFIL),
+ file, vd_name);
+ else
+ (void) printf(MSG_ORIG(MSG_FMT_SYM_NEED_OFIL),
+ file, needobj, vd_name);
+
+ if (size)
+ (void) printf(MSG_ORIG(MSG_FMT_SYM_SZ_OFLG),
+ name, (ulong_t)size);
+ else
+ (void) printf(MSG_ORIG(MSG_FMT_SYM_OFLG), name);
+ } else {
+ if (size)
+ (void) printf(MSG_ORIG(MSG_FMT_SYM_SZ), name,
+ (ulong_t)size);
+ else
+ (void) printf(MSG_ORIG(MSG_FMT_SYM), name);
+ }
+ }
+}
+
+/*
* Print any reduced symbols. The convention is that reduced symbols exist as
* LOCL entries in the .symtab, between the FILE symbol for the output file and
* the first FILE symbol for any input file used to build the output file.
@@ -120,7 +475,7 @@ sym_local(Cache *cache, Cache *csym, const char *file)
int symn, _symn, found = 0;
GElf_Shdr shdr;
GElf_Sym sym;
- char *strs, *local = "_LOCAL_";
+ char *strs;
(void) gelf_getshdr(csym->c_scn, &shdr);
strs = (char *)cache[shdr.sh_link].c_data->d_buf;
@@ -162,29 +517,49 @@ sym_local(Cache *cache, Cache *csym, const char *file)
name = demangle(strs + sym.st_name);
if (oflag) {
- (void) printf(Format_ofil, file);
- (void) printf("\t%s: %s\n", local, name);
+ (void) printf(MSG_ORIG(MSG_FMT_LOCSYM_OFLG),
+ file, name);
} else {
if (found == 0) {
found = 1;
- (void) printf(Format_tnco, local);
+ (void) printf(MSG_ORIG(MSG_FMT_LOCSYM_HDR));
}
- (void) printf("\t\t%s;\n", name);
+ (void) printf(MSG_ORIG(MSG_FMT_LOCSYM), name);
}
}
}
/*
- * Print the files version needed sections.
+ * Print data from the files VERNEED section.
+ *
+ * If we have been asked to display symbols, then the
+ * output format follows that used for verdef sections,
+ * with each version displayed separately. For instance:
+ *
+ * libc.so.1 (SUNW_1.7):
+ * sym1;
+ * sym2;
+ * libc.so.1 (SUNW_1.9):
+ * sym3;
+ *
+ * If we are not displaying symbols, then a terse format
+ * is used, which combines all the needed versions from
+ * a given object into a single line. In this case, the
+ * versions are shown whether or not they contribute symbols.
+ *
+ * libc.so.1 (SUNW_1.7, SUNW_1.9);
*/
static int
-gvers_need(Cache *cache, Cache *need, const char *file, const char *name)
+gvers_need(Cache *cache, Cache *need, const Gver_sym_data *vsdata,
+ const char *file)
{
unsigned int num, _num;
char *strs;
GElf_Verneed *vnd = need->c_data->d_buf;
GElf_Shdr shdr;
int error = 0;
+ int show = vflag || (vsdata == NULL) || !oflag;
+
(void) gelf_getshdr(need->c_scn, &shdr);
@@ -205,107 +580,144 @@ gvers_need(Cache *cache, Cache *need, const char *file, const char *name)
for (_num = 1; _num <= num; _num++,
vnd = (GElf_Verneed *)((uintptr_t)vnd + vnd->vn_next)) {
- GElf_Vernaux *vnap = (GElf_Vernaux *)
- ((uintptr_t)vnd + vnd->vn_aux);
- GElf_Half cnt = vnd->vn_cnt;
- const char *_name, * dep;
+ GElf_Vernaux *vnap;
+ Word ndx;
+ const char *needobj, *dep, *fmt;
+ int started = 0;
- /*
- * Obtain the version name and determine if we need to process
- * it further.
- */
- _name = (char *)(strs + vnd->vn_file);
- if (name && (strcmp(name, _name) == 0))
- continue;
+ vnap = (GElf_Vernaux *) ((uintptr_t)vnd + vnd->vn_aux);
+
+ /* Obtain the needed object file name */
+ needobj = (char *)(strs + vnd->vn_file);
error = 1;
- /*
- * If one-line ouput is called for display the filename being
- * processed.
- */
- if (oflag)
- (void) printf(Format_ofil, file);
+ /* Process the versions needed from this object */
+ for (ndx = 0; ndx < vnd->vn_cnt; ndx++,
+ vnap = (GElf_Vernaux *)((uintptr_t)vnap + vnap->vna_next)) {
+ Conv_ver_flags_buf_t ver_flags_buf;
- /*
- * Determine the version name required from this file.
- */
- if (cnt--)
dep = (char *)(strs + vnap->vna_name);
- else
- dep = MSG_ORIG(MSG_STR_EMPTY);
- (void) printf(Format_bgnl, _name, dep);
- if (vflag && (vnap->vna_flags == VER_FLG_WEAK))
- (void) printf(Format_weak);
+ if (!match(needobj, dep, vnap->vna_other))
+ continue;
- /*
- * Extract any other version dependencies for this file
- */
- /* CSTYLED */
- for (vnap = (GElf_Vernaux *)((uintptr_t)vnap + vnap->vna_next);
- cnt; cnt--,
- vnap = (GElf_Vernaux *)((uintptr_t)vnap + vnap->vna_next)) {
- dep = (char *)(strs + vnap->vna_name);
- (void) printf(Format_next, dep);
- if (vflag && (vnap->vna_flags == VER_FLG_WEAK))
- (void) printf(Format_weak);
+ if (show) {
+ if ((started == 0) || (vsdata != NULL)) {
+ /*
+ * If one-line ouput is called for
+ * display the filename being processed.
+ */
+ if (oflag && show)
+ (void) printf(
+ MSG_ORIG(MSG_FMT_OFIL),
+ file);
+
+ (void) printf(
+ MSG_ORIG(MSG_FMT_LIST_BEGIN),
+ needobj);
+
+ fmt = MSG_ORIG(MSG_FMT_LIST_FIRST);
+ started = 1;
+ } else {
+ fmt = MSG_ORIG(MSG_FMT_LIST_NEXT);
+ }
+
+ /*
+ * If not showing symbols, only show INFO
+ * versions in verbose mode. They don't
+ * actually contribute to the version
+ * interface as seen by rtld, so listing them
+ * without qualification can be misleading.
+ */
+ if (vflag || (vsdata != NULL) ||
+ (alist_nitems(match_list) != 0) ||
+ !(vnap->vna_flags & VER_FLG_INFO)) {
+ (void) printf(fmt, dep);
+
+ /* Show non-zero flags */
+ if (vflag && (vnap->vna_flags != 0))
+ (void) printf(
+ MSG_ORIG(MSG_FMT_VER_FLG),
+ conv_ver_flags(
+ vnap->vna_flags,
+ CONV_FMT_NOBKT,
+ &ver_flags_buf));
+ }
+ if (vsdata != NULL)
+ (void) printf(oflag ?
+ MSG_ORIG(MSG_FMT_LIST_END_SEM) :
+ MSG_ORIG(MSG_FMT_LIST_END_COL));
+ }
+
+ /*
+ * If we are showing symbols, and vna_other is
+ * non-zero, list them here.
+ *
+ * A value of 0 means that this object uses
+ * traditional Solaris versioning rules, under
+ * which VERSYM does not contain indexes to VERNEED
+ * records. In this case, there is nothing to show.
+ */
+ if (vsdata && (vnap->vna_other > 0))
+ gvers_syms(vsdata, vnap->vna_other,
+ dep, needobj, file);
}
- (void) printf(Format_endl);
+ if (show && started && (vsdata == NULL))
+ (void) printf(MSG_ORIG(MSG_FMT_LIST_END_SEM));
}
return (error);
}
/*
- * Append an item to the specified list, and return a pointer to the list
- * node created.
+ * Return a GVer_desc descriptor for the given version if one
+ * exists.
+ *
+ * entry:
+ * name - Version name
+ * hash - ELF hash of name
+ * lst - APlist of existing descriptors.
+ * file - Object file containing the version
+ *
+ * exit:
+ * Return the corresponding GVer_desc struct if it
+ * exists, and NULL otherwise.
*/
-static Listnode *
-list_append(List *lst, const void *item, const char *file)
-{
- Listnode *_lnp;
-
- if ((_lnp = malloc(sizeof (Listnode))) == 0) {
- int err = errno;
- (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname, file,
- strerror(err));
- exit(1);
- }
-
- _lnp->data = (void *)item;
- _lnp->next = NULL;
-
- if (lst->head == NULL)
- lst->tail = lst->head = _lnp;
- else {
- lst->tail->next = _lnp;
- lst->tail = lst->tail->next;
- }
- return (_lnp);
-}
-
static GVer_desc *
-gvers_find(const char *name, unsigned long hash, List *lst)
+gvers_find(const char *name, unsigned long hash, APlist *lst)
{
- Listnode *lnp;
+ Aliste idx;
GVer_desc *vdp;
- for (LIST_TRAVERSE(lst, lnp, vdp)) {
- if (vdp->vd_hash != hash)
- continue;
- if (strcmp(vdp->vd_name, name) == 0)
+ for (APLIST_TRAVERSE(lst, idx, vdp))
+ if ((vdp->vd_hash == hash) &&
+ (strcmp(vdp->vd_name, name) == 0))
return (vdp);
- }
- return (0);
+
+ return (NULL);
}
+/*
+ * Return a GVer_desc descriptor for the given version.
+ *
+ * entry:
+ * name - Version name
+ * hash - ELF hash of name
+ * lst - List of existing descriptors.
+ * file - Object file containing the version
+ *
+ * exit:
+ * Return the corresponding GVer_desc struct. If the
+ * descriptor does not already exist, it is created.
+ * On error, a fatal error is issued and the process exits.
+ */
static GVer_desc *
-gvers_desc(const char *name, unsigned long hash, List *lst, const char *file)
+gvers_desc(const char *name, unsigned long hash, APlist **lst, const char *file)
{
GVer_desc *vdp;
- if ((vdp = gvers_find(name, hash, lst)) == 0) {
- if ((vdp = calloc(sizeof (GVer_desc), 1)) == 0) {
+ if ((vdp = gvers_find(name, hash, *lst)) == NULL) {
+ if ((vdp = calloc(sizeof (GVer_desc), 1)) == NULL) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
file, strerror(err));
@@ -315,83 +727,44 @@ gvers_desc(const char *name, unsigned long hash, List *lst, const char *file)
vdp->vd_name = name;
vdp->vd_hash = hash;
- if (list_append(lst, vdp, file) == 0)
- return (0);
+ pvs_aplist_append(lst, vdp, file);
}
return (vdp);
}
+/*
+ * Insert a version dependency for the given GVer_desc descriptor.
+ *
+ * entry:
+ * name - Dependency version name
+ * hash - ELF hash of name
+ * lst - List of existing descriptors.
+ * vdp - Existing version descriptor to which the dependency
+ * is to be added.
+ * file - Object file containing the version
+ *
+ * exit:
+ * A descriptor for the dependency version is looked up
+ * (created if necessary), and then added to the dependency
+ * list for vdp. Returns the dependency descriptor. On error,
+ * a fatal error is issued and the process exits.
+ */
static GVer_desc *
-gvers_depend(const char *name, unsigned long hash, GVer_desc *vdp, List *lst,
+gvers_depend(const char *name, unsigned long hash, GVer_desc *vdp, APlist **lst,
const char *file)
{
GVer_desc *_vdp;
- if ((_vdp = gvers_desc(name, hash, lst, file)) == 0)
- return (0);
-
- if (list_append(&vdp->vd_deps, _vdp, file) == 0)
- return (0);
-
+ _vdp = gvers_desc(name, hash, lst, file);
+ pvs_aplist_append(&vdp->vd_deps, _vdp, file);
return (vdp);
}
static void
-gvers_syms(GElf_Versym *vsp, Elf_Data *sym_data, int symn, char *strs,
- GVer_desc *vdp, const char *file)
-{
- GElf_Sym sym;
- int _symn;
-
- for (_symn = 0; _symn < symn; _symn++) {
- size_t size = 0;
- const char *name;
-
- if (vsp[_symn] != vdp->vd_ndx)
- continue;
-
- /*
- * For data symbols determine the size.
- */
- (void) gelf_getsym(sym_data, _symn, &sym);
- if ((GELF_ST_TYPE(sym.st_info) == STT_OBJECT) ||
- (GELF_ST_TYPE(sym.st_info) == STT_COMMON) ||
- (GELF_ST_TYPE(sym.st_info) == STT_TLS))
- size = (size_t)sym.st_size;
-
- name = demangle(strs + sym.st_name);
-
- /*
- * Only output the version symbol when the verbose flag is used.
- */
- if (!vflag && (sym.st_shndx == SHN_ABS)) {
- if (strcmp(name, vdp->vd_name) == 0)
- continue;
- }
-
- if (oflag) {
- (void) printf(Format_ofil, file);
- (void) printf("\t%s: ", vdp->vd_name);
- if (size)
- (void) printf("%s (%ld);\n", name,
- (ulong_t)size);
- else
- (void) printf("%s;\n", name);
- } else {
- if (size)
- (void) printf("\t\t%s (%ld);\n", name,
- (ulong_t)size);
- else
- (void) printf("\t\t%s;\n", name);
- }
- }
-}
-
-static void
-gvers_derefer(GVer_desc * vdp, int weak)
+gvers_derefer(GVer_desc *vdp, int weak)
{
- Listnode * _lnp;
- GVer_desc * _vdp;
+ Aliste idx;
+ GVer_desc *_vdp;
/*
* If the head of the list was a weak then we only clear out
@@ -401,24 +774,23 @@ gvers_derefer(GVer_desc * vdp, int weak)
if ((weak && (vdp->vd_flags & VER_FLG_WEAK)) || (!weak))
vdp->vd_flags &= ~FLG_VER_AVAIL;
- for (LIST_TRAVERSE(&vdp->vd_deps, _lnp, _vdp))
+ for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp))
gvers_derefer(_vdp, weak);
}
static void
-recurse_syms(GElf_Versym *vsp, Elf_Data *sym_data, int symn, char *strs,
- GVer_desc *vdp, const char *file)
+recurse_syms(const Gver_sym_data *vsdata, GVer_desc *vdp, const char *file)
{
- Listnode *_lnp;
+ Aliste idx;
GVer_desc *_vdp;
- for (LIST_TRAVERSE(&vdp->vd_deps, _lnp, _vdp)) {
+ for (APLIST_TRAVERSE(vdp->vd_deps, idx, _vdp)) {
if (!oflag)
- (void) printf(Format_tnco, _vdp->vd_name);
- gvers_syms(vsp, sym_data, symn, strs, _vdp, file);
- if (_vdp->vd_deps.head)
- recurse_syms(vsp, sym_data, symn, strs, _vdp, file);
+ (void) printf(MSG_ORIG(MSG_FMT_TNCO), _vdp->vd_name);
+ gvers_syms(vsdata, _vdp->vd_ndx, _vdp->vd_name, NULL, file);
+ if (aplist_nitems(_vdp->vd_deps) != 0)
+ recurse_syms(vsdata, _vdp, file);
}
}
@@ -427,19 +799,16 @@ recurse_syms(GElf_Versym *vsp, Elf_Data *sym_data, int symn, char *strs,
* Print the files version definition sections.
*/
static int
-gvers_def(Cache *cache, Cache *def, Cache *csym, const char *file,
- const char *name)
+gvers_def(Cache *cache, Cache *def, const Gver_sym_data *vsdata,
+ const char *file)
{
unsigned int num, _num;
char *strs;
- GElf_Versym *vsp;
GElf_Verdef *vdf = def->c_data->d_buf;
GElf_Shdr shdr;
- Elf_Data *sym_data;
- int symn;
- GVer_desc *vdp, *bvdp = 0;
- Listnode *lnp;
- List verdefs = {0, 0};
+ GVer_desc *vdp, *bvdp = NULL;
+ Aliste idx1;
+ APlist *verdefs = NULL;
int error = 0;
/*
@@ -467,18 +836,17 @@ gvers_def(Cache *cache, Cache *def, Cache *csym, const char *file,
vdf = (GElf_Verdef *)((uintptr_t)vdf + vdf->vd_next)) {
GElf_Half cnt = vdf->vd_cnt;
GElf_Half ndx = vdf->vd_ndx;
- GElf_Verdaux *vdap = (GElf_Verdaux *)((uintptr_t)vdf +
- vdf->vd_aux);
+ GElf_Verdaux *vdap;
const char *_name;
+ vdap = (GElf_Verdaux *)((uintptr_t)vdf + vdf->vd_aux);
+
/*
* Determine the version name and any dependencies.
*/
_name = (char *)(strs + vdap->vda_name);
- if ((vdp = gvers_desc(_name, elf_hash(_name), &verdefs,
- file)) == 0)
- return (0);
+ vdp = gvers_desc(_name, elf_hash(_name), &verdefs, file);
vdp->vd_ndx = ndx;
vdp->vd_flags = vdf->vd_flags | FLG_VER_AVAIL;
@@ -487,7 +855,7 @@ gvers_def(Cache *cache, Cache *def, Cache *csym, const char *file,
vdap = (GElf_Verdaux *)((uintptr_t)vdap + vdap->vda_next)) {
_name = (char *)(strs + vdap->vda_name);
if (gvers_depend(_name, elf_hash(_name), vdp,
- &verdefs, file) == 0)
+ &verdefs, file) == NULL)
return (0);
}
@@ -502,12 +870,12 @@ gvers_def(Cache *cache, Cache *def, Cache *csym, const char *file,
* Normalize the dependency list if required.
*/
if (nflag) {
- for (LIST_TRAVERSE(&verdefs, lnp, vdp)) {
- Listnode * _lnp;
- GVer_desc * _vdp;
+ for (APLIST_TRAVERSE(verdefs, idx1, vdp)) {
+ Aliste idx2;
+ GVer_desc *_vdp;
int type = vdp->vd_flags & VER_FLG_WEAK;
- for (LIST_TRAVERSE(&vdp->vd_deps, _lnp, _vdp))
+ for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp))
gvers_derefer(_vdp, type);
}
@@ -523,15 +891,15 @@ gvers_def(Cache *cache, Cache *def, Cache *csym, const char *file,
* Traverse the dependency list and print out the appropriate
* information.
*/
- for (LIST_TRAVERSE(&verdefs, lnp, vdp)) {
- Listnode * _lnp;
- GVer_desc * _vdp;
+ for (APLIST_TRAVERSE(verdefs, idx1, vdp)) {
+ Aliste idx2;
+ GVer_desc *_vdp;
int count;
- if (name && (strcmp(name, vdp->vd_name) != 0))
+ if (!match(NULL, vdp->vd_name, vdp->vd_ndx))
continue;
-
- if (!name && !(vdp->vd_flags & FLG_VER_AVAIL))
+ if ((alist_nitems(match_list) == 0) &&
+ !(vdp->vd_flags & FLG_VER_AVAIL))
continue;
error = 1;
@@ -543,77 +911,86 @@ gvers_def(Cache *cache, Cache *def, Cache *csym, const char *file,
* dependencies this version inherits.
*/
if (oflag)
- (void) printf(Format_ofil, file);
- (void) printf("\t%s", vdp->vd_name);
- if (vdp->vd_flags & VER_FLG_WEAK)
- (void) printf(Format_weak);
+ (void) printf(MSG_ORIG(MSG_FMT_OFIL), file);
+ (void) printf(MSG_ORIG(MSG_FMT_VER_NAME), vdp->vd_name);
+ if ((vdp->vd_flags & MSK_VER_USER) != 0) {
+ Conv_ver_flags_buf_t ver_flags_buf;
+
+ (void) printf(MSG_ORIG(MSG_FMT_VER_FLG),
+ conv_ver_flags(
+ vdp->vd_flags & MSK_VER_USER,
+ CONV_FMT_NOBKT, &ver_flags_buf));
+ }
count = 1;
- for (LIST_TRAVERSE(&vdp->vd_deps, _lnp, _vdp)) {
+ for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) {
const char *_name = _vdp->vd_name;
if (count++ == 1) {
+
if (oflag)
- (void) printf(": {%s", _name);
+ (void) printf(
+ MSG_ORIG(MSG_FMT_IN_OFLG),
+ _name);
else if (vdp->vd_flags & VER_FLG_WEAK)
- (void) printf(":\t{%s", _name);
+ (void) printf(
+ MSG_ORIG(MSG_FMT_IN_WEAK),
+ _name);
else
- (void) printf(": \t{%s",
+ (void) printf(
+ MSG_ORIG(MSG_FMT_IN),
_name);
} else
- (void) printf(Format_next, _name);
+ (void) printf(
+ MSG_ORIG(MSG_FMT_LIST_NEXT), _name);
}
if (count != 1)
- (void) printf("}");
+ (void) printf(MSG_ORIG(MSG_FMT_IN_END));
- if (csym && !oflag)
- (void) printf(":\n");
+ if (vsdata && !oflag)
+ (void) printf(MSG_ORIG(MSG_FMT_COL_NL));
else
- (void) printf(";\n");
+ (void) printf(MSG_ORIG(MSG_FMT_SEM_NL));
} else {
- if (csym && !oflag)
- (void) printf(Format_tnco, vdp->vd_name);
- else if (!csym) {
+ if (vsdata && !oflag)
+ (void) printf(MSG_ORIG(MSG_FMT_TNCO),
+ vdp->vd_name);
+ else if (!vsdata) {
if (oflag)
- (void) printf(Format_ofil, file);
- (void) printf(Format_tnse, vdp->vd_name);
+ (void) printf(MSG_ORIG(MSG_FMT_OFIL),
+ file);
+ (void) printf(MSG_ORIG(MSG_FMT_TNSE),
+ vdp->vd_name);
}
}
- /*
- * If we need to print symbols get the associated symbol table.
- */
- if (csym) {
- (void) gelf_getshdr(csym->c_scn, &shdr);
- vsp = (GElf_Versym *)csym->c_data->d_buf;
- sym_data = cache[shdr.sh_link].c_data;
- (void) gelf_getshdr(cache[shdr.sh_link].c_scn, &shdr);
- /* LINTED */
- symn = (int)(shdr.sh_size / shdr.sh_entsize);
- } else
+ /* If we are not printing symbols, we're done */
+ if (vsdata == NULL)
continue;
/*
- * If a specific version name has been specified then display
- * any of its own symbols plus any inherited from other
- * versions. Otherwise simply print out the symbols for this
- * version.
+ * If a specific version to match has been specified then
+ * display any of its own symbols plus any inherited from
+ * other versions. Otherwise simply print out the symbols
+ * for this version.
*/
- gvers_syms(vsp, sym_data, symn, strs, vdp, file);
- if (name) {
- recurse_syms(vsp, sym_data, symn, strs, vdp, file);
+ gvers_syms(vsdata, vdp->vd_ndx, vdp->vd_name, NULL, file);
+ if (alist_nitems(match_list) != 0) {
+ recurse_syms(vsdata, vdp, file);
/*
- * If the verbose flag is set add the base version as a
- * dependency (unless it's the list we were asked to
- * print in the first place).
+ * If the verbose flag is set, and this is not
+ * the base version, then add the base version as a
+ * dependency.
*/
- if (vflag && bvdp && strcmp(name, bvdp->vd_name)) {
+ if (vflag && bvdp &&
+ !match(NULL, bvdp->vd_name, bvdp->vd_ndx)) {
if (!oflag)
- (void) printf(Format_tnco, bvdp->vd_name);
- gvers_syms(vsp, sym_data, symn, strs, bvdp,
- file);
+ (void) printf(MSG_ORIG(MSG_FMT_TNCO),
+ bvdp->vd_name);
+ gvers_syms(vsdata, bvdp->vd_ndx,
+ bvdp->vd_name, NULL, file);
}
}
}
@@ -629,11 +1006,12 @@ main(int argc, char **argv, char **envp)
Elf_Data *data;
GElf_Ehdr ehdr;
int nfile, var;
- const char *name;
char *names;
Cache *cache, *_cache;
Cache *_cache_def, *_cache_need, *_cache_sym, *_cache_loc;
int error = 0;
+ Gver_sym_data vsdata_s;
+ const Gver_sym_data *vsdata = NULL;
/*
* Check for a binary that better fits this architecture.
@@ -647,11 +1025,10 @@ main(int argc, char **argv, char **envp)
(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
cname = argv[0];
- name = NULL;
Cflag = dflag = lflag = nflag = oflag = rflag = sflag = vflag = 0;
opterr = 0;
- while ((var = getopt(argc, argv, "CdlnorsvN:")) != EOF) {
+ while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
switch (var) {
case 'C':
Cflag = USR_DEFINED;
@@ -660,7 +1037,7 @@ main(int argc, char **argv, char **envp)
dflag = USR_DEFINED;
break;
case 'l':
- lflag = USR_DEFINED;
+ lflag = sflag = USR_DEFINED;
break;
case 'n':
nflag = USR_DEFINED;
@@ -677,8 +1054,9 @@ main(int argc, char **argv, char **envp)
case 'v':
vflag = USR_DEFINED;
break;
+ case 'I':
case 'N':
- name = optarg;
+ add_match_record(var, optarg);
break;
case '?':
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
@@ -701,7 +1079,7 @@ main(int argc, char **argv, char **envp)
/*
* By default print both version definitions and needed dependencies.
*/
- if ((dflag == 0) && (rflag == 0))
+ if ((dflag == 0) && (rflag == 0) && (lflag == 0))
dflag = rflag = DEF_DEFINED;
/*
@@ -774,14 +1152,14 @@ main(int argc, char **argv, char **envp)
* as elf_begin has already gone through all the overhead we
* might as well set up the cache for every section.
*/
- if ((cache = calloc(ehdr.e_shnum, sizeof (Cache))) == 0) {
+ if ((cache = calloc(ehdr.e_shnum, sizeof (Cache))) == NULL) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), cname,
file, strerror(err));
exit(1);
}
- _cache_def = _cache_need = _cache_sym = _cache_loc = 0;
+ _cache_def = _cache_need = _cache_sym = _cache_loc = NULL;
_cache = cache;
_cache++;
for (scn = NULL; scn = elf_nextscn(elf, scn); _cache++) {
@@ -830,7 +1208,7 @@ main(int argc, char **argv, char **envp)
* Before printing anything out determine if any warnings are
* necessary.
*/
- if (lflag && (_cache_loc == 0)) {
+ if (lflag && (_cache_loc == NULL)) {
(void) fprintf(stderr, MSG_INTL(MSG_VER_UNREDSYMS),
cname, file);
(void) fprintf(stderr, MSG_INTL(MSG_VER_NOSYMTAB));
@@ -841,20 +1219,36 @@ main(int argc, char **argv, char **envp)
* one-line output, display the filename being processed.
*/
if ((nfile > 1) && !oflag)
- (void) printf("%s:\n", file);
+ (void) printf(MSG_ORIG(MSG_FMT_FILE), file);
+
+ /*
+ * If we're printing symbols, then collect the data
+ * necessary to do that.
+ */
+ if (_cache_sym != NULL) {
+ vsdata = &vsdata_s;
+ (void) gelf_getshdr(_cache_sym->c_scn, &shdr);
+ vsdata_s.vsd_vsp =
+ (GElf_Versym *)_cache_sym->c_data->d_buf;
+ vsdata_s.vsd_sym_data = cache[shdr.sh_link].c_data;
+ (void) gelf_getshdr(cache[shdr.sh_link].c_scn, &shdr);
+ vsdata_s.vsd_symn = shdr.sh_size / shdr.sh_entsize;
+ vsdata_s.vsd_strs =
+ (const char *)cache[shdr.sh_link].c_data->d_buf;
+ }
+
/*
* Print the files version needed sections.
*/
if (_cache_need)
- nerror = gvers_need(cache, _cache_need, file, name);
+ nerror = gvers_need(cache, _cache_need, vsdata, file);
/*
* Print the files version definition sections.
*/
if (_cache_def)
- derror = gvers_def(cache, _cache_def, _cache_sym,
- file, name);
+ derror = gvers_def(cache, _cache_def, vsdata, file);
/*
* Print any local symbol reductions.