diff options
| author | Ali Bahrami <Ali.Bahrami@Sun.COM> | 2008-09-24 11:26:37 -0600 |
|---|---|---|
| committer | Ali Bahrami <Ali.Bahrami@Sun.COM> | 2008-09-24 11:26:37 -0600 |
| commit | 090a8d9e70b0696e7d9bc114c6e021757c9f04fe (patch) | |
| tree | 65417695add89e61d46abf32dca758a050680bfe /usr/src/cmd | |
| parent | 5548c527e7ec2109f4f9ee52056afd33ada35516 (diff) | |
| download | illumos-joyent-090a8d9e70b0696e7d9bc114c6e021757c9f04fe.tar.gz | |
6749055 ld should generate GNU style VERSYM indexes for VERNEED records
PSARC/2008/603 ELF objects to adopt GNU-style Versym indexes
Diffstat (limited to 'usr/src/cmd')
| -rw-r--r-- | usr/src/cmd/sgs/elfdump/common/elfdump.c | 194 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/include/conv.h | 19 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/include/libld.h | 6 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/lari/lari.pl | 21 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/libconv/common/llib-lconv | 4 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/libconv/common/version.c | 48 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/libconv/common/version.msg | 9 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/libld/common/sections.c | 15 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/libld/common/update.c | 158 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/libld/common/version.c | 64 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/liblddbg/common/version.c | 14 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/packages/common/SUNWonld-README | 2 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/pvs/Makefile.com | 11 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/pvs/Makefile.targ | 16 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/pvs/common/lintsup.c | 11 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/pvs/common/pvs.c | 896 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/pvs/common/pvs.msg | 39 | ||||
| -rw-r--r-- | usr/src/cmd/sgs/rtld/common/elf.c | 15 |
18 files changed, 1091 insertions, 451 deletions
diff --git a/usr/src/cmd/sgs/elfdump/common/elfdump.c b/usr/src/cmd/sgs/elfdump/common/elfdump.c index ea51519c27..69d2d9b727 100644 --- a/usr/src/cmd/sgs/elfdump/common/elfdump.c +++ b/usr/src/cmd/sgs/elfdump/common/elfdump.c @@ -47,27 +47,63 @@ * in the object being analyzed. It is filled in by versions(), and used * by init_symtbl_state() when displaying symbol information. * + * There are three forms of symbol versioning known to us: + * + * 1) The original form, introduced with Solaris 2.5, in which + * the Versym contains indexes to Verdef records, and the + * Versym values for UNDEF symbols resolved by other objects + * are all set to 0. + * 2) The GNU form, which is backward compatible with the original + * Solaris form, but which adds several extensions: + * - The Versym also contains indexes to Verneed records, recording + * which object/version contributed the external symbol at + * link time. These indexes start with the next value following + * the final Verdef index. The index is written to the previously + * reserved vna_other field of the ELF Vernaux structure. + * - The top bit of the Versym value is no longer part of the index, + * but is used as a "hidden bit" to prevent binding to the symbol. + * - Multiple implementations of a given symbol, contained in varying + * versions are allowed, using special assembler pseudo ops, + * and encoded in the symbol name using '@' characters. + * 3) Modified Solaris form, in which we adopt the first GNU extension + * (Versym indexes to Verneed records), but not the others. + * + * elfdump can handle any of these cases. The presence of a DT_VERSYM + * dynamic element indicates a full GNU object. An object that lacks + * a DT_VERSYM entry, but which has non-zero vna_other fields in the Vernaux + * structures is a modified Solaris object. An object that has neither of + * these uses the original form. + * * max_verndx contains the largest version index that can appear * in a Versym entry. This can never be less than 1: In the case where * there is no verdef/verneed sections, the [0] index is reserved - * for local symbols, and the [1] index for globals. If Solaris versioning - * rules are in effect and there is a verdef section, then the number - * of defined versions provides this number. If GNU versioning is in effect, - * then: - * - If there is no verneed section, it is the same as for - * Solaris versioning. - * - If there is a verneed section, the vna_other field of the + * for local symbols, and the [1] index for globals. If the original + * Solaris versioning rules are in effect and there is a verdef section, + * then max_verndex is the number of defined versions. If one of the + * other versioning forms is in effect, then: + * 1) If there is no verneed section, it is the same as for + * original Solaris versioning. + * 2) If there is a verneed section, the vna_other field of the * Vernaux structs contain versions, and max_verndx is the * largest such index. * - * The value of the gnu field is based on the presence of + * If gnu_full is True, the object uses the full GNU form of versioning. + * The value of the gnu_full field is based on the presence of * a DT_VERSYM entry in the dynamic section: GNU ld produces these, and * Solaris ld does not. + * + * The gnu_needed field is True if the Versym contains indexes to + * Verneed records, as indicated by non-zero vna_other fields in the Verneed + * section. If gnu_full is True, then gnu_needed will always be true. + * However, gnu_needed can be true without gnu_full. This is the modified + * Solaris form. */ typedef struct { Cache *cache; /* Pointer to cache entry for VERSYM */ Versym *data; /* Pointer to versym array */ - int gnu; /* True if object uses GNU versioning rules */ + int gnu_full; /* True if object uses GNU versioning rules */ + int gnu_needed; /* True if object uses VERSYM indexes for */ + /* VERNEED (subset of gnu_full) */ int max_verndx; /* largest versym index value */ } VERSYM_STATE; @@ -982,10 +1018,11 @@ version_def(Verdef *vdf, Word vdf_num, Cache *vcache, Cache *scache, for (cnt = 1; cnt <= vdf_num; cnt++, vdf = (Verdef *)((uintptr_t)vdf + vdf->vd_next)) { - const char *name, *dep; - Half vcnt = vdf->vd_cnt - 1; - Half ndx = vdf->vd_ndx; - Verdaux *vdap = (Verdaux *)((uintptr_t)vdf + vdf->vd_aux); + Conv_ver_flags_buf_t ver_flags_buf; + const char *name, *dep; + Half vcnt = vdf->vd_cnt - 1; + Half ndx = vdf->vd_ndx; + Verdaux *vdap = (Verdaux *)((uintptr_t)vdf + vdf->vd_aux); /* * Obtain the name and first dependency (if any). @@ -1000,7 +1037,7 @@ version_def(Verdef *vdf, Word vdf_num, Cache *vcache, Cache *scache, (void) snprintf(index, MAXNDXSIZE, MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(ndx)); Elf_ver_line_1(0, index, name, dep, - conv_ver_flags(vdf->vd_flags)); + conv_ver_flags(vdf->vd_flags, 0, &ver_flags_buf)); /* * Print any additional dependencies. @@ -1033,6 +1070,26 @@ version_def(Verdef *vdf, Word vdf_num, Cache *vcache, Cache *scache, * The versions have been printed. If GNU style versioning * is in effect, versym->max_verndx has been updated to * contain the largest version index seen. + * + * note: + * The versym section of an object that follows the original + * Solaris versioning rules only contains indexes into the verdef + * section. Symbols defined in other objects (UNDEF) are given + * a version of 0, indicating that they are not defined by + * this file, and the Verneed entries do not have associated version + * indexes. For these reasons, we do not display a version index + * for original-style Verneed sections. + * + * The GNU versioning extensions alter this: Symbols defined in other + * objects receive a version index in the range above those defined + * by the Verdef section, and the vna_other field of the Vernaux + * structs inside the Verneed section contain the version index for + * that item. We therefore display the index when showing the + * contents of a GNU style Verneed section. You should not + * necessarily expect these indexes to appear in sorted + * order --- it seems that the GNU ld assigns the versions as + * symbols are encountered during linking, and then the results + * are assembled into the Verneed section afterwards. */ static void version_need(Verneed *vnd, Word vnd_num, Cache *vcache, Cache *scache, @@ -1042,38 +1099,13 @@ version_need(Verneed *vnd, Word vnd_num, Cache *vcache, Cache *scache, char index[MAXNDXSIZE]; const char *index_str; - Elf_ver_need_title(0, versym->gnu); - - /* - * The versym section in an object that follows Solaris versioning - * rules contains indexes into the verdef section. Symbols defined - * in other objects (UNDEF) are given a version of 0, indicating that - * they are not defined by this file, and the Verneed entries do not - * have associated version indexes. For these reasons, we do not - * display a version index for Solaris Verneed sections. - * - * The GNU versioning rules are different: Symbols defined in other - * objects receive a version index in the range above those defined - * by the Verdef section, and the vna_other field of the Vernaux - * structs inside the Verneed section contain the version index for - * that item. We therefore display the index when showing the - * contents of a GNU Verneed section. You should not expect these - * indexes to appear in sorted order --- it seems that the GNU ld - * assigns the versions as symbols are encountered during linking, - * and then the results are assembled into the Verneed section - * afterwards. - */ - if (versym->gnu) { - index_str = index; - } else { - /* For Solaris versioning, display a NULL string */ - index_str = MSG_ORIG(MSG_STR_EMPTY); - } + Elf_ver_need_title(0, versym->gnu_needed); for (cnt = 1; cnt <= vnd_num; cnt++, vnd = (Verneed *)((uintptr_t)vnd + vnd->vn_next)) { - const char *name, *dep; - Half vcnt = vnd->vn_cnt; + Conv_ver_flags_buf_t ver_flags_buf; + const char *name, *dep; + Half vcnt = vnd->vn_cnt; Vernaux *vnap = (Vernaux *)((uintptr_t)vnd + vnd->vn_aux); /* @@ -1088,7 +1120,10 @@ version_need(Verneed *vnd, Word vnd_num, Cache *vcache, Cache *scache, else dep = MSG_INTL(MSG_STR_NULL); - if (versym->gnu) { + if (vnap->vna_other == 0) { /* Traditional form */ + index_str = MSG_ORIG(MSG_STR_EMPTY); + } else { /* GNU form */ + index_str = index; /* Format the version index value */ (void) snprintf(index, MAXNDXSIZE, MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(vnap->vna_other)); @@ -1096,7 +1131,7 @@ version_need(Verneed *vnd, Word vnd_num, Cache *vcache, Cache *scache, versym->max_verndx = vnap->vna_other; } Elf_ver_line_1(0, index_str, name, dep, - conv_ver_flags(vnap->vna_flags)); + conv_ver_flags(vnap->vna_flags, 0, &ver_flags_buf)); /* * Print any additional version dependencies. @@ -1108,14 +1143,15 @@ version_need(Verneed *vnd, Word vnd_num, Cache *vcache, Cache *scache, vnap->vna_next)) { dep = string(vcache, cnt, scache, file, vnap->vna_name); - if (versym->gnu) { + if (vnap->vna_other > 0) { /* Format the next index value */ (void) snprintf(index, MAXNDXSIZE, MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(vnap->vna_other)); - Elf_ver_line_1(0, index_str, + Elf_ver_line_1(0, index, MSG_ORIG(MSG_STR_EMPTY), dep, - conv_ver_flags(vnap->vna_flags)); + conv_ver_flags(vnap->vna_flags, + 0, &ver_flags_buf)); if (vnap->vna_other > versym->max_verndx) versym->max_verndx = @@ -1123,7 +1159,8 @@ version_need(Verneed *vnd, Word vnd_num, Cache *vcache, Cache *scache, } else { Elf_ver_line_3(0, MSG_ORIG(MSG_STR_EMPTY), dep, - conv_ver_flags(vnap->vna_flags)); + conv_ver_flags(vnap->vna_flags, + 0, &ver_flags_buf)); } } } @@ -1131,9 +1168,12 @@ version_need(Verneed *vnd, Word vnd_num, Cache *vcache, Cache *scache, } /* - * Compute the max_verndx value for a GNU style object with - * a Verneed section. This is only needed if version_need() is not - * called. + * Examine the Verneed section for information related to GNU + * style Versym indexing: + * - A non-zero vna_other field indicates that Versym indexes can + * reference Verneed records. + * - If the object uses GNU style Versym indexing, the + * maximum index value is needed to detect bad Versym entries. * * entry: * vnd - Address of verneed data @@ -1141,11 +1181,13 @@ version_need(Verneed *vnd, Word vnd_num, Cache *vcache, Cache *scache, * versym - Information about versym section * * exit: + * If a non-zero vna_other field is seen, versym->gnu_needed is set. + * * versym->max_verndx has been updated to contain the largest * version index seen. */ static void -update_gnu_max_verndx(Verneed *vnd, Word vnd_num, VERSYM_STATE *versym) +update_gnu_verndx(Verneed *vnd, Word vnd_num, VERSYM_STATE *versym) { Word cnt; @@ -1154,8 +1196,16 @@ update_gnu_max_verndx(Verneed *vnd, Word vnd_num, VERSYM_STATE *versym) Half vcnt = vnd->vn_cnt; Vernaux *vnap = (Vernaux *)((uintptr_t)vnd + vnd->vn_aux); - if (vnap->vna_other > versym->max_verndx) - versym->max_verndx = vnap->vna_other; + /* + * A non-zero value of vna_other indicates that this + * object references VERNEED items from the VERSYM + * array. + */ + if (vnap->vna_other != 0) { + versym->gnu_needed = 1; + if (vnap->vna_other > versym->max_verndx) + versym->max_verndx = vnap->vna_other; + } /* * Check any additional version dependencies. @@ -1165,6 +1215,10 @@ update_gnu_max_verndx(Verneed *vnd, Word vnd_num, VERSYM_STATE *versym) for (vcnt--; vcnt; vcnt--, vnap = (Vernaux *)((uintptr_t)vnap + vnap->vna_next)) { + if (vnap->vna_other == 0) + continue; + + versym->gnu_needed = 1; if (vnap->vna_other > versym->max_verndx) versym->max_verndx = vnap->vna_other; } @@ -1220,7 +1274,8 @@ versions(Cache *cache, Word shnum, const char *file, uint_t flags, dyn = (Dyn *)_cache->c_data->d_buf; for (; numdyn-- > 0; dyn++) if (dyn->d_tag == DT_VERSYM) { - versym->gnu = 1; + versym->gnu_full = + versym->gnu_needed = 1; break; } break; @@ -1281,23 +1336,20 @@ versions(Cache *cache, Word shnum, const char *file, uint_t flags, } } - if ((flags & FLG_SHOW_VERSIONS) == 0) { - /* - * If GNU versioning applies to this object, and there - * is a Verneed section, then examine it to determine - * the maximum Versym version index for this file. - */ - if ((versym->gnu) && (verneed_cache != NULL)) - update_gnu_max_verndx( - (Verneed *)verneed_cache->c_data->d_buf, - verneed_cache->c_shdr->sh_info, versym); - return; - } + /* + * If there is a Verneed section, examine it for information + * related to GNU style versioning. + */ + if (verneed_cache != NULL) + update_gnu_verndx((Verneed *)verneed_cache->c_data->d_buf, + verneed_cache->c_shdr->sh_info, versym); /* * Now that all the information is available, display the - * Verdef and Verneed section contents. + * Verdef and Verneed section contents, if requested. */ + if ((flags & FLG_SHOW_VERSIONS) == 0) + return; if (verdef_cache != NULL) { dbg_print(0, MSG_ORIG(MSG_STR_EMPTY)); dbg_print(0, MSG_INTL(MSG_ELF_SCN_VERDEF), @@ -1313,7 +1365,7 @@ versions(Cache *cache, Word shnum, const char *file, uint_t flags, /* * If GNU versioning applies to this object, version_need() * will update versym->max_verndx, and it is not - * necessary to call update_gnu_max_verndx(). + * necessary to call update_gnu_verndx(). */ version_need((Verneed *)verneed_cache->c_data->d_buf, verneed_cache->c_shdr->sh_info, verneed_cache, @@ -1545,7 +1597,7 @@ output_symbol(SYMTBL_STATE *state, Word symndx, Word info, Word disp_symndx, Versym test_verndx; verndx = test_verndx = state->versym->data[symndx]; - gnuver = state->versym->gnu; + gnuver = state->versym->gnu_full; /* * Check to see if this is a defined symbol with a diff --git a/usr/src/cmd/sgs/include/conv.h b/usr/src/cmd/sgs/include/conv.h index 1a0e10fe8d..d9fccb6a62 100644 --- a/usr/src/cmd/sgs/include/conv.h +++ b/usr/src/cmd/sgs/include/conv.h @@ -30,8 +30,6 @@ #ifndef _CONV_H #define _CONV_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Global include file for conversion library. */ @@ -571,6 +569,18 @@ typedef union { } Conv64_cnote_auxv_af_buf_t; +/* conv_ver_flags() */ +#define CONV_VER_FLAGS_BUFSIZE 31 +typedef union { + Conv32_inv_buf_t inv_buf; + char buf[CONV_VER_FLAGS_BUFSIZE]; +} Conv32_ver_flags_buf_t; +typedef union { + Conv64_inv_buf_t inv_buf; + char buf[CONV_VER_FLAGS_BUFSIZE]; +} Conv64_ver_flags_buf_t; + + /* * Generic names for class specific buffer types above @@ -621,6 +631,7 @@ typedef union { #define Conv_cnote_ss_flags_buf_t Conv64_cnote_ss_flags_buf_t #define Conv_cnote_cc_content_buf_t Conv64_cnote_cc_content_buf_t #define Conv_cnote_auxv_af_buf_t Conv64_cnote_auxv_af_buf_t +#define Conv_ver_flags_buf_t Conv64_ver_flags_buf_t #else #define CONV_INV_BUFSIZE CONV32_INV_BUFSIZE #define CONV_EHDR_FLAGS_BUFSIZE CONV32_EHDR_FLAGS_BUFSIZE @@ -667,6 +678,7 @@ typedef union { #define Conv_cnote_ss_flags_buf_t Conv32_cnote_ss_flags_buf_t #define Conv_cnote_cc_content_buf_t Conv32_cnote_cc_content_buf_t #define Conv_cnote_auxv_af_buf_t Conv32_cnote_auxv_af_buf_t +#define Conv_ver_flags_buf_t Conv32_ver_flags_buf_t #endif @@ -860,7 +872,8 @@ extern void conv_str_to_c_literal(const char *buf, size_t n, Conv_str_to_c_literal_func_t *cb_func, void *uvalue); extern Uts_desc *conv_uts(void); -extern const char *conv_ver_flags(Half); +extern const char *conv_ver_flags(Half, Conv_fmt_flags_t, + Conv_ver_flags_buf_t *); extern const char *conv_ver_index(Versym, int, Conv_inv_buf_t *); diff --git a/usr/src/cmd/sgs/include/libld.h b/usr/src/cmd/sgs/include/libld.h index bc0ed3a9bc..334ff1e3b3 100644 --- a/usr/src/cmd/sgs/include/libld.h +++ b/usr/src/cmd/sgs/include/libld.h @@ -965,6 +965,8 @@ struct ver_desc { struct ver_index { const char *vi_name; /* dependency version name */ Half vi_flags; /* communicates availability */ + Half vi_overndx; /* Index asssigned to this version in */ + /* output object Verneed section */ Ver_desc *vi_desc; /* cross reference to descriptor */ }; @@ -976,10 +978,10 @@ struct ver_index { #define FLG_VER_AVAIL 0x10 /* version is available for binding */ #define FLG_VER_REFER 0x20 /* version has been referenced */ -#define FLG_VER_SELECT 0x40 /* version has been selected by user */ +#define FLG_VER_SPECVER 0x40 /* via $SPECVERS in mapfile. */ + /* Cannot be normalized away */ #define FLG_VER_CYCLIC 0x80 /* a member of cyclic dependency */ - /* * isalist(1) descriptor - used to break an isalist string into its component * options. diff --git a/usr/src/cmd/sgs/lari/lari.pl b/usr/src/cmd/sgs/lari/lari.pl index fff5d3f045..c398bd3590 100644 --- a/usr/src/cmd/sgs/lari/lari.pl +++ b/usr/src/cmd/sgs/lari/lari.pl @@ -21,11 +21,9 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # Link Analysis of Runtime Interfaces. # @@ -1201,7 +1199,6 @@ sub GetAllSymbols { my ($Type, $FileHandle); my (%AddrToName, %NameToAddr); my ($Exec) = 0; - my ($Vers) = 0; my ($Symb) = 0; my ($Copy) = 0; my ($Interpose) = 0; @@ -1233,12 +1230,13 @@ sub GetAllSymbols { # -e ELF header provides the file type # -d dynamic information provides filter names # -r relocations provide for copy relocations + # -v object versioning # -y symbol information section provide pre-symbol filters # and direct binding information # # As this information can be quite large, process the elfdump(1) output # through a pipe. - open($FileHandle, "LC_ALL=C elfdump -edry '$Obj' 2> /dev/null |"); + open($FileHandle, "LC_ALL=C elfdump -edrvy '$Obj' 2> /dev/null |"); while (defined(my $Line = <$FileHandle>)) { my (@Fields); @@ -1261,6 +1259,12 @@ sub GetAllSymbols { } elsif ($Line =~ /^Syminfo Section:/) { $Info = 1; $Ehdr = $Dyn = $Rel = 0; + } elsif ($Line =~ /^Version Definition Section:/) { + # The existance of a VERDEF section is all we + # are looking for. There is no need to parse + # the specific version definitions. + $Versioned{$Obj} = 1; + $Ehdr = $Dyn = $Rel = $Info = 0; } else { $Ehdr = $Dyn = $Rel = $Info = 0; } @@ -1509,13 +1513,6 @@ sub GetAllSymbols { $Symbols{$Fields[8]}{$Obj}[$ObjFlag] |= $Flags; $Symbols{$Fields[8]}{$Obj}[$ObjVis] = $Fields[5]; $Objects{$Obj}{$Fields[8]} |= $Flags; - - # If the version field is non-null this object has already been - # versioned. - if (($Vers == 0) && ($Fields[6] ne '0')) { - $Versioned{$Obj} = 1; - $Vers = 1; - } } close($FileHandle); diff --git a/usr/src/cmd/sgs/libconv/common/llib-lconv b/usr/src/cmd/sgs/libconv/common/llib-lconv index 64fb7017a2..0ba1cbe236 100644 --- a/usr/src/cmd/sgs/libconv/common/llib-lconv +++ b/usr/src/cmd/sgs/libconv/common/llib-lconv @@ -26,8 +26,6 @@ /* LINTLIBRARY */ /* PROTOLIB1 */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdlib.h> #include <libelf.h> #include <_machelf.h> @@ -57,7 +55,7 @@ const char *conv_lddstub(int); const char *conv_seg_flags(Half, Conv_seg_flags_buf_t *); int conv_sys_eclass(void); Uts_desc *conv_uts(void); -const char *conv_ver_flags(Half); +const char *conv_ver_flags(Half, Conv_fmt_flags_t, Conv_ver_flags_buf_t *); const char *conv_ver_index(Versym, int, Conv_inv_buf_t *); diff --git a/usr/src/cmd/sgs/libconv/common/version.c b/usr/src/cmd/sgs/libconv/common/version.c index 6b3fe924d3..087bfe6951 100644 --- a/usr/src/cmd/sgs/libconv/common/version.c +++ b/usr/src/cmd/sgs/libconv/common/version.c @@ -20,10 +20,9 @@ */ /* - * Copyright 2007 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" /* * String conversion routine for version flag entries. @@ -32,15 +31,48 @@ #include "_conv.h" #include "version_msg.h" +#define VERFLAGSZ CONV_EXPN_FIELD_DEF_PREFIX_SIZE + \ + MSG_VER_FLG_WEAK_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_VER_FLG_BASE_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_VER_FLG_INFO_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + CONV_INV_BUFSIZE + CONV_EXPN_FIELD_DEF_SUFFIX_SIZE + +/* + * Ensure that Conv_ver_flags_buf_t is large enough: + * + * VERFLAGSZ is the real minimum size of the buffer required by + * conv_ver_flags(). However, Conv_ver_flags_buf_t uses CONV_VER_FLAGS_BUFSIZE + * to set the buffer size. We do things this way because the definition of + * VERFLAGSZ uses information that is not available in the environment of + * other programs that include the conv.h header file. + */ +#if (CONV_VER_FLAGS_BUFSIZE != VERFLAGSZ) && !defined(__lint) +#define REPORT_BUFSIZE VERFLAGSZ +#include "report_bufsize.h" +#error "CONV_VER_FLAGS_BUFSIZE does not match VERFLAGSZ" +#endif + const char * -conv_ver_flags(Half flags) +conv_ver_flags(Half flags, Conv_fmt_flags_t fmt_flags, + Conv_ver_flags_buf_t *ver_flags_buf) { - if (flags & VER_FLG_WEAK) - return (MSG_ORIG(MSG_VER_FLG_WEAK)); - else if (flags & VER_FLG_BASE) - return (MSG_ORIG(MSG_VER_FLG_BASE)); - else + static Val_desc vda[] = { + { VER_FLG_WEAK, MSG_ORIG(MSG_VER_FLG_WEAK) }, + { VER_FLG_BASE, MSG_ORIG(MSG_VER_FLG_BASE) }, + { VER_FLG_INFO, MSG_ORIG(MSG_VER_FLG_INFO) }, + { 0, 0 } + }; + static CONV_EXPN_FIELD_ARG conv_arg = { + NULL, sizeof (ver_flags_buf->buf), vda }; + + if (flags == 0) return (MSG_ORIG(MSG_GBL_NULL)); + + conv_arg.buf = ver_flags_buf->buf; + conv_arg.oflags = conv_arg.rflags = flags; + (void) conv_expn_field(&conv_arg, fmt_flags); + + return ((const char *)ver_flags_buf->buf); } diff --git a/usr/src/cmd/sgs/libconv/common/version.msg b/usr/src/cmd/sgs/libconv/common/version.msg index 465fac840d..f04ee75c2d 100644 --- a/usr/src/cmd/sgs/libconv/common/version.msg +++ b/usr/src/cmd/sgs/libconv/common/version.msg @@ -1,5 +1,5 @@ # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # CDDL HEADER START @@ -21,12 +21,11 @@ # # CDDL HEADER END # -# ident "%Z%%M% %I% %E% SMI" -# # Message file for cmd/sgs/libconv/common/versions.c -@ MSG_VER_FLG_WEAK "[ WEAK ]" -@ MSG_VER_FLG_BASE "[ BASE ]" +@ MSG_VER_FLG_WEAK "WEAK" +@ MSG_VER_FLG_BASE "BASE" +@ MSG_VER_FLG_INFO "INFO" @ MSG_VERSYM_ELIMINATE "ELIM" diff --git a/usr/src/cmd/sgs/libld/common/sections.c b/usr/src/cmd/sgs/libld/common/sections.c index 0472ceadec..46d1c81d18 100644 --- a/usr/src/cmd/sgs/libld/common/sections.c +++ b/usr/src/cmd/sgs/libld/common/sections.c @@ -2659,16 +2659,17 @@ ld_make_sections(Ofl_desc *ofl) /* * Add any necessary versioning information. */ - if ((flags & (FLG_OF_VERNEED | FLG_OF_NOVERSEC)) == FLG_OF_VERNEED) { - if (make_verneed(ofl) == S_ERROR) + if (!(flags & FLG_OF_NOVERSEC)) { + if ((flags & FLG_OF_VERNEED) && + (make_verneed(ofl) == S_ERROR)) return (S_ERROR); - } - if ((flags & (FLG_OF_VERDEF | FLG_OF_NOVERSEC)) == FLG_OF_VERDEF) { - if (make_verdef(ofl) == S_ERROR) + if ((flags & FLG_OF_VERDEF) && + (make_verdef(ofl) == S_ERROR)) return (S_ERROR); - if ((ofl->ofl_osversym = make_sym_sec(ofl, + if ((flags & (FLG_OF_VERNEED | FLG_OF_VERDEF)) && + ((ofl->ofl_osversym = make_sym_sec(ofl, MSG_ORIG(MSG_SCN_SUNWVERSYM), SHT_SUNW_versym, - ld_targ.t_id.id_version)) == (Os_desc*)S_ERROR) + ld_targ.t_id.id_version)) == (Os_desc*)S_ERROR)) return (S_ERROR); } diff --git a/usr/src/cmd/sgs/libld/common/update.c b/usr/src/cmd/sgs/libld/common/update.c index d33ee869c4..b3b1da69b9 100644 --- a/usr/src/cmd/sgs/libld/common/update.c +++ b/usr/src/cmd/sgs/libld/common/update.c @@ -303,7 +303,8 @@ update_osym(Ofl_desc *ofl) * table. There is one entry for each symbol which contains the symbols * version index. */ - if ((flags & (FLG_OF_VERDEF | FLG_OF_NOVERSEC)) == FLG_OF_VERDEF) { + if (!(flags & FLG_OF_NOVERSEC) && + (flags & (FLG_OF_VERNEED | FLG_OF_VERDEF))) { versym = (Versym *)ofl->ofl_osversym->os_outdata->d_buf; versym[0] = 0; } else @@ -1188,9 +1189,9 @@ update_osym(Ofl_desc *ofl) if (sdp->sd_symndx && versym) { Half vndx = 0; - if (sdp->sd_flags & FLG_SY_MVTOCOMM) + if (sdp->sd_flags & FLG_SY_MVTOCOMM) { vndx = VER_NDX_GLOBAL; - else if (sdp->sd_ref == REF_REL_NEED) { + } else if (sdp->sd_ref == REF_REL_NEED) { Half symflags1 = sdp->sd_flags1; vndx = sap->sa_overndx; @@ -1201,6 +1202,13 @@ update_osym(Ofl_desc *ofl) else vndx = VER_NDX_GLOBAL; } + } else if ((sdp->sd_ref == REF_DYN_NEED) && + (sap->sa_dverndx > 0) && + (sap->sa_dverndx <= sdp->sd_file->ifl_vercnt) && + (sdp->sd_file->ifl_verndx != NULL)) { + /* Use index of verneed record */ + vndx = sdp->sd_file->ifl_verndx + [sap->sa_dverndx].vi_overndx; } versym[sdp->sd_symndx] = vndx; } @@ -2467,7 +2475,7 @@ update_overdef(Ofl_desc *ofl) Ver_desc *vdp, *_vdp; Verdef *vdf, *_vdf; int num = 0; - Os_desc *strosp, *symosp; + Os_desc *strosp; /* * Traverse the version descriptors and update the version structures @@ -2572,15 +2580,11 @@ update_overdef(Ofl_desc *ofl) if ((ofl->ofl_flags & FLG_OF_RELOBJ) || (ofl->ofl_flags & FLG_OF_STATIC)) { strosp = ofl->ofl_osstrtab; - symosp = ofl->ofl_ossymtab; } else { strosp = ofl->ofl_osdynstr; - symosp = ofl->ofl_osdynsym; } /* LINTED */ ofl->ofl_osverdef->os_shdr->sh_link = (Word)elf_ndxscn(strosp->os_scn); - /* LINTED */ - ofl->ofl_osversym->os_shdr->sh_link = (Word)elf_ndxscn(symosp->os_scn); /* * The version definition sections `info' field is used to indicate the @@ -2592,6 +2596,33 @@ update_overdef(Ofl_desc *ofl) } /* + * Finish the version symbol index section + */ +static int +update_oversym(Ofl_desc *ofl) +{ + Os_desc *symosp; + + /* + * Record the string table association with the version definition + * section, and the symbol table associated with the version symbol + * table (the actual contents of the version symbol table are filled + * in during symbol update). + */ + if ((ofl->ofl_flags & FLG_OF_RELOBJ) || + (ofl->ofl_flags & FLG_OF_STATIC)) { + symosp = ofl->ofl_ossymtab; + } else { + symosp = ofl->ofl_osdynsym; + } + + /* LINTED */ + ofl->ofl_osversym->os_shdr->sh_link = (Word)elf_ndxscn(symosp->os_scn); + + return (1); +} + +/* * Build the version needed section */ static int @@ -2601,7 +2632,8 @@ update_overneed(Ofl_desc *ofl) Ifl_desc *ifl; Verneed *vnd, *_vnd; Str_tbl *dynstr; - Word num = 0, cnt = 0; + Word num = 0; + int has_specver; dynstr = ofl->ofl_dynstrtab; _vnd = vnd = (Verneed *)ofl->ofl_osverneed->os_outdata->d_buf; @@ -2612,6 +2644,7 @@ update_overneed(Ofl_desc *ofl) */ for (LIST_TRAVERSE(&ofl->ofl_sos, lnp, ifl)) { Half _cnt; + Word cnt = 0; Vernaux *_vnap, *vnap; Sdf_desc *sdf = ifl->ifl_sdfdesc; size_t stoff; @@ -2626,16 +2659,25 @@ update_overneed(Ofl_desc *ofl) _vnap = vnap = (Vernaux *)(vnd + 1); - if (sdf && (sdf->sdf_flags & FLG_SDF_SPECVER)) { + has_specver = sdf && (sdf->sdf_flags & FLG_SDF_SPECVER); + if (has_specver) { Sdv_desc *sdv; Listnode *lnp2; /* * If version needed definitions were specified in - * a mapfile ($VERSION=*) then record those + * a mapfile ($SPECVERS=*) then record those * definitions. */ for (LIST_TRAVERSE(&sdf->sdf_verneed, lnp2, sdv)) { + /* + * If this $SPECVERS item corresponds + * to a real version, then skip it here + * in favor of the real one below. + */ + if (sdv->sdv_flags & FLG_SDV_MATCHED) + continue; + (void) st_setstring(dynstr, sdv->sdv_name, &stoff); vnap->vna_name = stoff; @@ -2650,41 +2692,62 @@ update_overneed(Ofl_desc *ofl) _vnap->vna_next = (Word)((uintptr_t)vnap - (uintptr_t)_vnap); } - } else { + } - /* - * Traverse the version index list recording - * each version as a needed dependency. - */ - for (cnt = _cnt = 0; _cnt <= ifl->ifl_vercnt; - _cnt++) { - Ver_index *vip = &ifl->ifl_verndx[_cnt]; - - if (vip->vi_flags & FLG_VER_REFER) { - (void) st_setstring(dynstr, - vip->vi_name, &stoff); - vnap->vna_name = stoff; - - if (vip->vi_desc) { - vnap->vna_hash = - vip->vi_desc->vd_hash; - vnap->vna_flags = - vip->vi_desc->vd_flags; - } else { - vnap->vna_hash = 0; - vnap->vna_flags = 0; - } - vnap->vna_other = 0; + /* + * Traverse the version index list recording + * each version as a needed dependency. + */ + for (_cnt = 0; _cnt <= ifl->ifl_vercnt; _cnt++) { + Ver_index *vip = &ifl->ifl_verndx[_cnt]; - _vnap = vnap; - vnap++, cnt++; - _vnap->vna_next = - /* LINTED */ - (Word)((uintptr_t)vnap - - (uintptr_t)_vnap); + if (vip->vi_flags & FLG_VER_REFER) { + (void) st_setstring(dynstr, vip->vi_name, + &stoff); + vnap->vna_name = stoff; + + if (vip->vi_desc) { + vnap->vna_hash = vip->vi_desc->vd_hash; + vnap->vna_flags = + vip->vi_desc->vd_flags; + } else { + vnap->vna_hash = 0; + vnap->vna_flags = 0; } + vnap->vna_other = vip->vi_overndx; + + /* + * If version A inherits version B, then + * B is implicit in A. It suffices for ld.so.1 + * to verify A at runtime and skip B. The + * version normalization process sets the INFO + * flag for the versions we want ld.so.1 to + * skip. By default, we progagate these flags + * to the output object as computed. + * + * The presence of $SPECVERS items alters + * matters. If $SPECVERS are present in the + * mapfile, then any version that corresponds + * to the $SPECVERS must be validated, and + * all others must be skipped. This is true + * even if it causes ld.so.1 to incorrectly + * validate the object ---- it is an override + * mechanism. + */ + if ((!has_specver && + (vip->vi_flags & VER_FLG_INFO)) || + (has_specver && + !(vip->vi_flags & FLG_VER_SPECVER))) + vnap->vna_flags |= VER_FLG_INFO; + + _vnap = vnap; + vnap++, cnt++; + _vnap->vna_next = + /* LINTED */ + (Word)((uintptr_t)vnap - (uintptr_t)_vnap); } } + _vnap->vna_next = 0; /* @@ -3753,12 +3816,17 @@ ld_update_outfile(Ofl_desc *ofl) */ if (update_oehdr(ofl) == S_ERROR) return (S_ERROR); - if ((flags & (FLG_OF_VERDEF | FLG_OF_NOVERSEC)) == FLG_OF_VERDEF) - if (update_overdef(ofl) == S_ERROR) + if (!(flags & FLG_OF_NOVERSEC)) { + if ((flags & FLG_OF_VERDEF) && + (update_overdef(ofl) == S_ERROR)) + return (S_ERROR); + if ((flags & FLG_OF_VERNEED) && + (update_overneed(ofl) == S_ERROR)) return (S_ERROR); - if ((flags & (FLG_OF_VERNEED | FLG_OF_NOVERSEC)) == FLG_OF_VERNEED) - if (update_overneed(ofl) == S_ERROR) + if ((flags & (FLG_OF_VERNEED | FLG_OF_VERDEF)) && + (update_oversym(ofl) == S_ERROR)) return (S_ERROR); + } if (flags & FLG_OF_DYNAMIC) { if (update_odynamic(ofl) == S_ERROR) return (S_ERROR); diff --git a/usr/src/cmd/sgs/libld/common/version.c b/usr/src/cmd/sgs/libld/common/version.c index 65f84a6035..6365addc27 100644 --- a/usr/src/cmd/sgs/libld/common/version.c +++ b/usr/src/cmd/sgs/libld/common/version.c @@ -20,10 +20,9 @@ */ /* - * Copyright 2007 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" #include <string.h> #include <stdio.h> @@ -313,12 +312,17 @@ vers_derefer(Ifl_desc *ifl, Ver_desc *vdp, int weak) Ver_index *vip = &ifl->ifl_verndx[vdp->vd_ndx]; /* - * If the head of the list was a weak then we only clear out + * Set the INFO bit on all dependencies that ld.so.1 + * can skip verification for. These are the dependencies + * that are inherited by others -- verifying the inheriting + * version implicitily covers this one. + * + * If the head of the list was a weak then we only mark * weak dependencies, but if the head of the list was 'strong' - * we clear the REFER bit on all dependencies. + * we set INFO on all dependencies. */ if ((weak && (vdp->vd_flags & VER_FLG_WEAK)) || (!weak)) - vip->vi_flags &= ~FLG_VER_REFER; + vip->vi_flags |= VER_FLG_INFO; for (LIST_TRAVERSE(&vdp->vd_deps, lnp, _vdp)) vers_derefer(ifl, _vdp, weak); @@ -334,6 +338,15 @@ ld_vers_check_need(Ofl_desc *ofl) { Listnode *lnp1; Ifl_desc *ifl; + Half needndx; + + /* + * Versym indexes for needed versions start with the next + * available version after the final definied version. + * However, it can never be less than 2. 0 is always for local + * scope, and 1 is always the first global definition. + */ + needndx = (ofl->ofl_vercnt > 0) ? (ofl->ofl_vercnt + 1) : 2; /* * Traverse the shared object list looking for dependencies. @@ -343,7 +356,7 @@ ld_vers_check_need(Ofl_desc *ofl) Ver_index *vip; Ver_desc *vdp; Sdf_desc *sdf = ifl->ifl_sdfdesc; - Byte cnt, need; + Byte cnt, need = 0; if (!(ifl->ifl_flags & FLG_IF_NEEDED)) continue; @@ -353,23 +366,30 @@ ld_vers_check_need(Ofl_desc *ofl) /* * If version needed definitions were specified in - * a mapfile ($SPECVERS=) then record those definitions + * a mapfile ($SPECVERS=) then record those definitions. */ if (sdf && (sdf->sdf_flags & FLG_SDF_SPECVER)) { Sdv_desc *sdv; for (LIST_TRAVERSE(&sdf->sdf_verneed, lnp2, sdv)) { + + /* + * If this $SPECVERS item corresponds to + * a real version, then don't issue it + * here, but use the real one instead. + * This preserves the ability to reference it + * from a symbol versym entry. + */ + if (sdv->sdv_flags & FLG_SDV_MATCHED) + continue; + + /* Not found in known versions. Count it */ ofl->ofl_verneedsz += sizeof (Vernaux); if (st_insert(ofl->ofl_dynstrtab, sdv->sdv_name) == -1) return (S_ERROR); + need++; } - ifl->ifl_flags |= FLG_IF_VERNEED; - ofl->ofl_verneedsz += sizeof (Verneed); - if (st_insert(ofl->ofl_dynstrtab, - ifl->ifl_soname) == -1) - return (S_ERROR); - continue; } /* @@ -379,7 +399,7 @@ ld_vers_check_need(Ofl_desc *ofl) * cause fatal errors from the runtime linker, non-weak * dependencies do. */ - for (need = 0, cnt = 0; cnt <= ifl->ifl_vercnt; cnt++) { + for (cnt = 0; cnt <= ifl->ifl_vercnt; cnt++) { vip = &ifl->ifl_verndx[cnt]; vdp = vip->vi_desc; @@ -421,7 +441,7 @@ ld_vers_check_need(Ofl_desc *ofl) * Finally, determine how many of the version dependencies need * to be recorded. */ - for (need = 0, cnt = 0; cnt <= ifl->ifl_vercnt; cnt++) { + for (cnt = 0; cnt <= ifl->ifl_vercnt; cnt++) { vip = &ifl->ifl_verndx[cnt]; /* @@ -429,6 +449,9 @@ ld_vers_check_need(Ofl_desc *ofl) * version dependency. */ if (vip->vi_flags & FLG_VER_REFER) { + /* Assign a VERSYM index for it */ + vip->vi_overndx = needndx++; + ofl->ofl_verneedsz += sizeof (Vernaux); if (st_insert(ofl->ofl_dynstrtab, vip->vi_name) == -1) @@ -513,15 +536,20 @@ vers_index(Ofl_desc *ofl, Ifl_desc *ifl, int avail) if (vdp->vd_flags & VER_FLG_WEAK) vip[ndx].vi_flags |= VER_FLG_WEAK; /* - * If this version is mentioned in a mapfile - * $ADDVERS syntax then add a FLG_IF_NEEDED flag now + * If this version is mentioned in a mapfile using + * $ADDVERS or $SPECVERS syntax then check to see if + * it corresponds to an actual version in the file. */ - if (sdf && (sdf->sdf_flags & FLG_SDF_ADDVER)) { + if (sdf && + (sdf->sdf_flags & (FLG_SDF_SPECVER|FLG_SDF_ADDVER))) { Listnode * lnp2; for (LIST_TRAVERSE(&sdf->sdf_verneed, lnp2, sdv)) { if (strcmp(vip[ndx].vi_name, sdv->sdv_name) == 0) { vip[ndx].vi_flags |= FLG_VER_REFER; + if (sdf->sdf_flags & FLG_SDF_SPECVER) + vip[ndx].vi_flags |= + FLG_VER_SPECVER; sdv->sdv_flags |= FLG_SDV_MATCHED; break; } diff --git a/usr/src/cmd/sgs/liblddbg/common/version.c b/usr/src/cmd/sgs/liblddbg/common/version.c index 914d5d11ec..b61758dd68 100644 --- a/usr/src/cmd/sgs/liblddbg/common/version.c +++ b/usr/src/cmd/sgs/liblddbg/common/version.c @@ -20,10 +20,9 @@ */ /* - * Copyright 2007 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" #include <stdio.h> #include <debug.h> @@ -109,10 +108,11 @@ Dbg_ver_nointerface(Lm_list *lml, const char *name) void Dbg_ver_desc_entry(Lm_list *lml, Ver_desc *vdp) { - const char *dep; - Ver_desc *_vdp, *__vdp; - Listnode * lnp; - char index[10]; + Conv_ver_flags_buf_t ver_flags_buf; + const char *dep; + Ver_desc *_vdp, *__vdp; + Listnode *lnp; + char index[10]; if (DBG_NOTCLASS(DBG_C_VERSIONS)) return; @@ -126,7 +126,7 @@ Dbg_ver_desc_entry(Lm_list *lml, Ver_desc *vdp) } (void) sprintf(index, MSG_ORIG(MSG_FMT_INDEX), vdp->vd_ndx); Elf_ver_line_1(lml, index, vdp->vd_name, dep, - conv_ver_flags(vdp->vd_flags)); + conv_ver_flags(vdp->vd_flags, 0, &ver_flags_buf)); /* * Loop through the dependency list in case there are more that one diff --git a/usr/src/cmd/sgs/packages/common/SUNWonld-README b/usr/src/cmd/sgs/packages/common/SUNWonld-README index d5ad09bd9d..ae3266f2d0 100644 --- a/usr/src/cmd/sgs/packages/common/SUNWonld-README +++ b/usr/src/cmd/sgs/packages/common/SUNWonld-README @@ -1378,3 +1378,5 @@ Bugid Risk Synopsis PSARC 2008/583 add gld options to ld(1) 6746674 setuid applications do not find libraries any more because trusted directories behavior changed +6749055 ld should generate GNU style VERSYM indexes for VERNEED records + PSARC/2008/603 ELF objects to adopt GNU-style Versym indexes diff --git a/usr/src/cmd/sgs/pvs/Makefile.com b/usr/src/cmd/sgs/pvs/Makefile.com index 969646160f..b082ec7f6e 100644 --- a/usr/src/cmd/sgs/pvs/Makefile.com +++ b/usr/src/cmd/sgs/pvs/Makefile.com @@ -20,11 +20,9 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# PROG= pvs @@ -34,7 +32,9 @@ include $(SRC)/cmd/sgs/Makefile.com COMOBJ= pvs.o BLTOBJ= msg.o -OBJS= $(BLTOBJ) $(COMOBJ) +TOOLOBJS = alist.o + +OBJS= $(BLTOBJ) $(COMOBJ) $(TOOLOBJS) MAPFILE= $(MAPFILE.NGB) MAPOPTS= $(MAPFILE:%=-M%) @@ -60,7 +60,8 @@ SGSMSGALL= $(SGSMSGCOM) SGSMSGFLAGS += -h $(BLTDEFS) -d $(BLTDATA) -m $(BLTMESG) -n pvs_msg -SRCS= $(COMOBJ:%.o=../common/%.c) $(BLTDATA) +SRCS= $(COMOBJ:%.o=../common/%.c) $(BLTDATA) \ + $(TOOLOBJS:%.o=$(SGSTOOLS)/common/%.c) LINTSRCS= $(SRCS) ../common/lintsup.c CLEANFILES += $(LINTOUTS) $(BLTFILES) diff --git a/usr/src/cmd/sgs/pvs/Makefile.targ b/usr/src/cmd/sgs/pvs/Makefile.targ index 4922b7ca29..e7a49190c9 100644 --- a/usr/src/cmd/sgs/pvs/Makefile.targ +++ b/usr/src/cmd/sgs/pvs/Makefile.targ @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,15 +19,18 @@ # CDDL HEADER END # # -#ident "%Z%%M% %I% %E% SMI" -# -# Copyright (c) 2001 by Sun Microsystems, Inc. -# All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + %.o: ../common/%.c $(COMPILE.c) $< $(POST_PROCESS_O) +%.o: $(SGSTOOLS)/common/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + all: $(PROG) clean: diff --git a/usr/src/cmd/sgs/pvs/common/lintsup.c b/usr/src/cmd/sgs/pvs/common/lintsup.c index 12a83f0706..1cb5cfba78 100644 --- a/usr/src/cmd/sgs/pvs/common/lintsup.c +++ b/usr/src/cmd/sgs/pvs/common/lintsup.c @@ -22,10 +22,9 @@ /* PROTOLIB1 */ /* - * 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" /* * Supplimental Pseudo-code to get lint to consider @@ -39,4 +38,12 @@ void foo() { dbg_print(0, _pvs_msg((Msg)&__pvs_msg[0])); + + alist_delete_by_offset(NULL, NULL); + (void) alist_insert_by_offset(NULL, NULL, 0, 0, 0); + alist_reset(NULL); + + (void) aplist_delete_value(NULL, NULL); + aplist_reset(NULL); + (void) aplist_test(NULL, NULL, 0); } 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. diff --git a/usr/src/cmd/sgs/pvs/common/pvs.msg b/usr/src/cmd/sgs/pvs/common/pvs.msg index ef3c2256f1..30883b3336 100644 --- a/usr/src/cmd/sgs/pvs/common/pvs.msg +++ b/usr/src/cmd/sgs/pvs/common/pvs.msg @@ -20,10 +20,9 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" @ _START_ @@ -34,9 +33,10 @@ # Argument usage messages. -@ MSG_USAGE_BRIEF "usage: %s [-Cdlnorsv] [-N Name] file(s)\n" +@ MSG_USAGE_BRIEF "usage: %s [-Cdlnorsv] [-I index] [-N Name] file(s)\n" @ MSG_USAGE_DETAIL "\t[-C]\t\tdemangle C++ symbol names\n\ \t[-d]\t\tprint version definition information\n\ + \t[-I index]\tqualify version with an index\n\ \t[-l]\t\tprint reduced symbols\n\ \t[-N name]\tqualify version with `name'\n\ \t[-n]\t\tnormalize version definition information\n\ @@ -64,6 +64,7 @@ @ MSG_SYS_OPEN "%s: %s: cannot open file: %s\n" @ MSG_SYS_MALLOC "%s: %s: malloc failed: %s\n" +@ MSG_STR_MATCH_RECORD "match record (-I/-N)" @ _END_ @@ -78,5 +79,37 @@ @ MSG_ELF_GETDATA "%s: %s: elf_getdata: %s\n" @ MSG_STR_EMPTY "" +@ MSG_STR_OPTIONS "CdI:lnorsvN:" + +@ MSG_FMT_COL_NL ":\n" +@ MSG_FMT_SEM_NL ";\n" +@ MSG_FMT_LIST_BEGIN "\t%s (" +@ MSG_FMT_LIST_FIRST "%s" +@ MSG_FMT_LIST_NEXT ", %s" +@ MSG_FMT_LIST_END_SEM ");\n" +@ MSG_FMT_LIST_END_COL "):\n" +@ MSG_FMT_OFIL "%s -" +@ MSG_FMT_TNCO "\t%s:\n" +@ MSG_FMT_TNSE "\t%s;\n" +@ MSG_FMT_VER_NAME "\t%s" +@ MSG_FMT_VER_FLG " [%s]" + +@ MSG_FMT_FILE "%s:\n" + +@ MSG_FMT_SYM_OFIL "%s -\t%s: " +@ MSG_FMT_SYM_NEED_OFIL "%s -\t%s (%s): " +@ MSG_FMT_SYM_OFLG "%s;\n" +@ MSG_FMT_SYM_SZ_OFLG "%s (%ld);\n" +@ MSG_FMT_SYM "\t\t%s;\n" +@ MSG_FMT_SYM_SZ "\t\t%s (%ld);\n" + +@ MSG_FMT_IN ": \t{%s" +@ MSG_FMT_IN_OFLG ": {%s" +@ MSG_FMT_IN_WEAK ":\t{%s" +@ MSG_FMT_IN_END "}" + +@ MSG_FMT_LOCSYM_HDR "\t_LOCAL_:\n" +@ MSG_FMT_LOCSYM "\t\t%s;\n" +@ MSG_FMT_LOCSYM_OFLG "%s -\t_LOCAL_: %s\n" @ MSG_SUNW_OST_SGS "SUNW_OST_SGS" diff --git a/usr/src/cmd/sgs/rtld/common/elf.c b/usr/src/cmd/sgs/rtld/common/elf.c index 5cae84bdeb..6d86c460cf 100644 --- a/usr/src/cmd/sgs/rtld/common/elf.c +++ b/usr/src/cmd/sgs/rtld/common/elf.c @@ -29,8 +29,6 @@ * All Rights Reserved */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Object file dependent support for ELF objects. */ @@ -518,6 +516,19 @@ elf_verify_vers(const char *name, Rt_map *clmp, Rt_map *nlmp) ulong_t _num, num = VERDEFNUM(nlmp); int found = 0; + /* + * Skip validation of versions that are marked + * INFO. This optimization is used for versions + * that are inherited by another version. Verification + * of the inheriting version is sufficient. + * + * Such versions are recorded in the object for the + * benefit of VERSYM entries that refer to them. This + * provides a purely diagnositic benefit. + */ + if (vnap->vna_flags & VER_FLG_INFO) + continue; + version = (char *)(cstrs + vnap->vna_name); DBG_CALL(Dbg_ver_need_entry(lml, 0, need, version)); |
