Index: include/llvm/Object/ELF.h |
=================================================================== |
--- include/llvm/Object/ELF.h (revision 152141) |
+++ include/llvm/Object/ELF.h (working copy) |
@@ -18,6 +18,7 @@ |
#include "llvm/ADT/StringSwitch.h" |
#include "llvm/ADT/Triple.h" |
#include "llvm/ADT/DenseMap.h" |
+#include "llvm/ADT/PointerIntPair.h" |
#include "llvm/Object/ObjectFile.h" |
#include "llvm/Support/Casting.h" |
#include "llvm/Support/ELF.h" |
@@ -176,8 +177,73 @@ |
} |
}; |
-// Elf_Dyn: Entry in the dynamic table |
+/// Elf_Versym: This is the structure of entries in the SHT_GNU_versym section |
+/// (.gnu.version). This structure is identical for ELF32 and ELF64. |
template<support::endianness target_endianness, bool is64Bits> |
+struct Elf_Versym_Impl { |
+ LLVM_ELF_IMPORT_TYPES(target_endianness, is64Bits) |
+ Elf_Half vs_index; // Version index with flags (e.g. VERSYM_HIDDEN) |
+}; |
+ |
+template<support::endianness target_endianness, bool is64Bits> |
+struct Elf_Verdaux_Impl; |
+ |
+/// Elf_Verdef: This is the structure of entries in the SHT_GNU_verdef section |
+/// (.gnu.version_d). This structure is identical for ELF32 and ELF64. |
+template<support::endianness target_endianness, bool is64Bits> |
+struct Elf_Verdef_Impl { |
+ LLVM_ELF_IMPORT_TYPES(target_endianness, is64Bits) |
+ typedef Elf_Verdaux_Impl<target_endianness, is64Bits> Elf_Verdaux; |
+ Elf_Half vd_version; // Version of this structure (e.g. VER_DEF_CURRENT) |
+ Elf_Half vd_flags; // Bitwise flags (VER_DEF_*) |
+ Elf_Half vd_ndx; // Version index, used in .gnu.version entries |
+ Elf_Half vd_cnt; // Number of Verdaux entries |
+ Elf_Word vd_hash; // Hash of name |
+ Elf_Word vd_aux; // Offset to the first Verdaux entry (in bytes) |
+ Elf_Word vd_next; // Offset to the next Verdef entry (in bytes) |
+ |
+ /// Get the first Verdaux entry for this Verdef. |
+ const Elf_Verdaux *getAux() const { |
+ return reinterpret_cast<const Elf_Verdaux*>((const char*)this + vd_aux); |
+ } |
+}; |
+ |
+/// Elf_Verdaux: This is the structure of auxilary data in the SHT_GNU_verdef |
+/// section (.gnu.version_d). This structure is identical for ELF32 and ELF64. |
+template<support::endianness target_endianness, bool is64Bits> |
+struct Elf_Verdaux_Impl { |
+ LLVM_ELF_IMPORT_TYPES(target_endianness, is64Bits) |
+ Elf_Word vda_name; // Version name (offset in string table) |
+ Elf_Word vda_next; // Offset to next Verdaux entry (in bytes) |
+}; |
+ |
+/// Elf_Verneed: This is the structure of entries in the SHT_GNU_verneed |
+/// section (.gnu.version_r). This structure is identical for ELF32 and ELF64. |
+template<support::endianness target_endianness, bool is64Bits> |
+struct Elf_Verneed_Impl { |
+ LLVM_ELF_IMPORT_TYPES(target_endianness, is64Bits) |
+ Elf_Half vn_version; // Version of this structure (e.g. VER_NEED_CURRENT) |
+ Elf_Half vn_cnt; // Number of associated Vernaux entries |
+ Elf_Word vn_file; // Library name (string table offset) |
+ Elf_Word vn_aux; // Offset to first Vernaux entry (in bytes) |
+ Elf_Word vn_next; // Offset to next Verneed entry (in bytes) |
+}; |
+ |
+/// Elf_Vernaux: This is the structure of auxiliary data in SHT_GNU_verneed |
+/// section (.gnu.version_r). This structure is identical for ELF32 and ELF64. |
+template<support::endianness target_endianness, bool is64Bits> |
+struct Elf_Vernaux_Impl { |
+ LLVM_ELF_IMPORT_TYPES(target_endianness, is64Bits) |
+ Elf_Word vna_hash; // Hash of dependency name |
+ Elf_Half vna_flags; // Bitwise Flags (VER_FLAG_*) |
+ Elf_Half vna_other; // Version index, used in .gnu.version entries |
+ Elf_Word vna_name; // Dependency name |
+ Elf_Word vna_next; // Offset to next Vernaux entry (in bytes) |
+}; |
+ |
+/// Elf_Dyn_Base: This structure matches the form of entries in the dynamic |
+/// table section (.dynamic) look like. |
+template<support::endianness target_endianness, bool is64Bits> |
struct Elf_Dyn_Base; |
template<support::endianness target_endianness> |
@@ -200,6 +266,7 @@ |
} d_un; |
}; |
+/// Elf_Dyn_Impl: This inherits from Elf_Dyn_Base, adding getters and setters. |
template<support::endianness target_endianness, bool is64Bits> |
struct Elf_Dyn_Impl : Elf_Dyn_Base<target_endianness, is64Bits> { |
using Elf_Dyn_Base<target_endianness, is64Bits>::d_tag; |
@@ -323,6 +390,11 @@ |
typedef Elf_Dyn_Impl<target_endianness, is64Bits> Elf_Dyn; |
typedef Elf_Rel_Impl<target_endianness, is64Bits, false> Elf_Rel; |
typedef Elf_Rel_Impl<target_endianness, is64Bits, true> Elf_Rela; |
+ typedef Elf_Verdef_Impl<target_endianness, is64Bits> Elf_Verdef; |
+ typedef Elf_Verdaux_Impl<target_endianness, is64Bits> Elf_Verdaux; |
+ typedef Elf_Verneed_Impl<target_endianness, is64Bits> Elf_Verneed; |
+ typedef Elf_Vernaux_Impl<target_endianness, is64Bits> Elf_Vernaux; |
+ typedef Elf_Versym_Impl<target_endianness, is64Bits> Elf_Versym; |
typedef DynRefImpl<target_endianness, is64Bits> DynRef; |
typedef content_iterator<DynRef> dyn_iterator; |
@@ -368,11 +440,41 @@ |
IndexMap_t SymbolTableSectionsIndexMap; |
DenseMap<const Elf_Sym*, ELF::Elf64_Word> ExtendedSymbolTable; |
- const Elf_Shdr *dot_dynamic_sec; // .dynamic |
+ const Elf_Shdr *dot_dynamic_sec; // .dynamic |
+ const Elf_Shdr *dot_gnu_version_sec; // .gnu.version |
+ const Elf_Shdr *dot_gnu_version_r_sec; // .gnu.version_r |
+ const Elf_Shdr *dot_gnu_version_d_sec; // .gnu.version_d |
+ |
// Pointer to SONAME entry in dynamic string table |
// This is set the first time getLoadName is called. |
mutable const char *dt_soname; |
+ // Records for each version index the corresponding Verdef or Vernaux entry. |
+ // This is filled the first time LoadVersionMap() is called. |
+ class VersionMapEntry : public PointerIntPair<const void*, 1> { |
+ public: |
+ // If the integer is 0, this is an Elf_Verdef*. |
+ // If the integer is 1, this is an Elf_Vernaux*. |
+ VersionMapEntry() : PointerIntPair<const void*, 1>(NULL, 0) { } |
+ VersionMapEntry(const Elf_Verdef *verdef) |
+ : PointerIntPair<const void*, 1>(verdef, 0) { } |
+ VersionMapEntry(const Elf_Vernaux *vernaux) |
+ : PointerIntPair<const void*, 1>(vernaux, 1) { } |
+ bool isNull() const { return getPointer() == NULL; } |
+ bool isVerdef() const { return !isNull() && getInt() == 0; } |
+ bool isVernaux() const { return !isNull() && getInt() == 1; } |
+ const Elf_Verdef *getVerdef() const { |
+ return isVerdef() ? (const Elf_Verdef*)getPointer() : NULL; |
+ } |
+ const Elf_Vernaux *getVernaux() const { |
+ return isVernaux() ? (const Elf_Vernaux*)getPointer() : NULL; |
+ } |
+ }; |
+ mutable SmallVector<VersionMapEntry, 16> VersionMap; |
+ void LoadVersionDefs(const Elf_Shdr *sec) const; |
+ void LoadVersionNeeds(const Elf_Shdr *ec) const; |
+ void LoadVersionMap() const; |
+ |
/// @brief Map sections to an array of relocation sections that reference |
/// them sorted by section index. |
RelocMap_t SectionRelocMap; |
@@ -396,6 +498,10 @@ |
error_code getSymbolName(const Elf_Shdr *section, |
const Elf_Sym *Symb, |
StringRef &Res) const; |
+ error_code getSymbolVersion(const Elf_Shdr *section, |
+ const Elf_Sym *Symb, |
+ StringRef &Version, |
+ bool &IsDefault) const; |
void VerifyStrTab(const Elf_Shdr *sh) const; |
protected: |
@@ -404,7 +510,8 @@ |
public: |
const Elf_Dyn *getDyn(DataRefImpl DynData) const; |
- |
+ error_code getSymbolVersion(SymbolRef Symb, StringRef &Version, |
+ bool &IsDefault) const; |
protected: |
virtual error_code getSymbolNext(DataRefImpl Symb, SymbolRef &Res) const; |
virtual error_code getSymbolName(DataRefImpl Symb, StringRef &Res) const; |
@@ -473,6 +580,7 @@ |
virtual uint8_t getBytesInAddress() const; |
virtual StringRef getFileFormatName() const; |
+ virtual StringRef getObjectType() const { return "ELF"; } |
virtual unsigned getArch() const; |
virtual StringRef getLoadName() const; |
@@ -489,7 +597,90 @@ |
static inline bool classof(const ELFObjectFile *v) { return true; } |
}; |
+// Iterate through the version definitions, and place each Elf_Verdef |
+// in the VersionMap according to its index. |
template<support::endianness target_endianness, bool is64Bits> |
+void ELFObjectFile<target_endianness, is64Bits>:: |
+ LoadVersionDefs(const Elf_Shdr *sec) const { |
+ unsigned vd_size = sec->sh_size; // Size of section in bytes |
+ unsigned vd_count = sec->sh_info; // Number of Verdef entries |
+ const char *sec_start = (const char*)base() + sec->sh_offset; |
+ const char *sec_end = sec_start + vd_size; |
+ // The first Verdef entry is at the start of the section. |
+ const char *p = sec_start; |
+ for (unsigned i = 0; i < vd_count; i++) { |
+ if (p + sizeof(Elf_Verdef) > sec_end) |
+ report_fatal_error("Section ended unexpectedly while scanning " |
+ "version definitions."); |
+ const Elf_Verdef *vd = reinterpret_cast<const Elf_Verdef *>(p); |
+ if (vd->vd_version != ELF::VER_DEF_CURRENT) |
+ report_fatal_error("Unexpected verdef version"); |
+ size_t index = vd->vd_ndx & ELF::VERSYM_VERSION; |
+ if (index >= VersionMap.size()) |
+ VersionMap.resize(index+1); |
+ VersionMap[index] = VersionMapEntry(vd); |
+ p += vd->vd_next; |
+ } |
+} |
+ |
+// Iterate through the versions needed section, and place each Elf_Vernaux |
+// in the VersionMap according to its index. |
+template<support::endianness target_endianness, bool is64Bits> |
+void ELFObjectFile<target_endianness, is64Bits>:: |
+ LoadVersionNeeds(const Elf_Shdr *sec) const { |
+ unsigned vn_size = sec->sh_size; // Size of section in bytes |
+ unsigned vn_count = sec->sh_info; // Number of Verneed entries |
+ const char *sec_start = (const char*)base() + sec->sh_offset; |
+ const char *sec_end = sec_start + vn_size; |
+ // The first Verneed entry is at the start of the section. |
+ const char *p = sec_start; |
+ for (unsigned i = 0; i < vn_count; i++) { |
+ if (p + sizeof(Elf_Verneed) > sec_end) |
+ report_fatal_error("Section ended unexpectedly while scanning " |
+ "version needed records."); |
+ const Elf_Verneed *vn = reinterpret_cast<const Elf_Verneed *>(p); |
+ if (vn->vn_version != ELF::VER_NEED_CURRENT) |
+ report_fatal_error("Unexpected verneed version"); |
+ // Iterate through the Vernaux entries |
+ const char *paux = p + vn->vn_aux; |
+ for (unsigned j = 0; j < vn->vn_cnt; j++) { |
+ if (paux + sizeof(Elf_Vernaux) > sec_end) |
+ report_fatal_error("Section ended unexpected while scanning auxiliary " |
+ "version needed records."); |
+ const Elf_Vernaux *vna = reinterpret_cast<const Elf_Vernaux *>(paux); |
+ size_t index = vna->vna_other & ELF::VERSYM_VERSION; |
+ if (index >= VersionMap.size()) |
+ VersionMap.resize(index+1); |
+ VersionMap[index] = VersionMapEntry(vna); |
+ paux += vna->vna_next; |
+ } |
+ p += vn->vn_next; |
+ } |
+} |
+ |
+template<support::endianness target_endianness, bool is64Bits> |
+void ELFObjectFile<target_endianness, is64Bits>::LoadVersionMap() const { |
+ // If there is no dynamic symtab or version table, there is nothing to do. |
+ if (SymbolTableSections[0] == NULL || dot_gnu_version_sec == NULL) |
+ return; |
+ |
+ // Has the VersionMap already been loaded? |
+ if (VersionMap.size() > 0) |
+ return; |
+ |
+ // The first two version indexes are reserved. |
+ // Index 0 is LOCAL, index 1 is GLOBAL. |
+ VersionMap.push_back(VersionMapEntry()); |
+ VersionMap.push_back(VersionMapEntry()); |
+ |
+ if (dot_gnu_version_d_sec) |
+ LoadVersionDefs(dot_gnu_version_d_sec); |
+ |
+ if (dot_gnu_version_r_sec) |
+ LoadVersionNeeds(dot_gnu_version_r_sec); |
+} |
+ |
+template<support::endianness target_endianness, bool is64Bits> |
void ELFObjectFile<target_endianness, is64Bits> |
::validateSymbol(DataRefImpl Symb) const { |
const Elf_Sym *symb = getSymbol(Symb); |
@@ -546,6 +737,18 @@ |
} |
template<support::endianness target_endianness, bool is64Bits> |
+error_code ELFObjectFile<target_endianness, is64Bits> |
+ ::getSymbolVersion(SymbolRef SymRef, |
+ StringRef &Version, |
+ bool &IsDefault) const { |
+ DataRefImpl Symb = SymRef.getRawDataRefImpl(); |
+ validateSymbol(Symb); |
+ const Elf_Sym *symb = getSymbol(Symb); |
+ return getSymbolVersion(SymbolTableSections[Symb.d.b], symb, |
+ Version, IsDefault); |
+} |
+ |
+template<support::endianness target_endianness, bool is64Bits> |
ELF::Elf64_Word ELFObjectFile<target_endianness, is64Bits> |
::getSymbolTableIndex(const Elf_Sym *symb) const { |
if (symb->st_shndx == ELF::SHN_XINDEX) |
@@ -1264,7 +1467,11 @@ |
, dot_strtab_sec(0) |
, dot_dynstr_sec(0) |
, dot_dynamic_sec(0) |
- , dt_soname(0) { |
+ , dot_gnu_version_sec(0) |
+ , dot_gnu_version_r_sec(0) |
+ , dot_gnu_version_d_sec(0) |
+ , dt_soname(0) |
+ { |
const uint64_t FileSize = Data->getBufferSize(); |
@@ -1300,32 +1507,61 @@ |
SymbolTableSections.push_back(NULL); |
for (uint64_t i = 0, e = getNumSections(); i != e; ++i) { |
- if (sh->sh_type == ELF::SHT_SYMTAB_SHNDX) { |
+ switch (sh->sh_type) { |
+ case ELF::SHT_SYMTAB_SHNDX: { |
if (SymbolTableSectionHeaderIndex) |
// FIXME: Proper error handling. |
report_fatal_error("More than one .symtab_shndx!"); |
SymbolTableSectionHeaderIndex = sh; |
+ break; |
} |
- if (sh->sh_type == ELF::SHT_SYMTAB) { |
+ case ELF::SHT_SYMTAB: { |
SymbolTableSectionsIndexMap[i] = SymbolTableSections.size(); |
SymbolTableSections.push_back(sh); |
+ break; |
} |
- if (sh->sh_type == ELF::SHT_DYNSYM) { |
+ case ELF::SHT_DYNSYM: { |
if (SymbolTableSections[0] != NULL) |
// FIXME: Proper error handling. |
report_fatal_error("More than one .dynsym!"); |
SymbolTableSectionsIndexMap[i] = 0; |
SymbolTableSections[0] = sh; |
+ break; |
} |
- if (sh->sh_type == ELF::SHT_REL || sh->sh_type == ELF::SHT_RELA) { |
+ case ELF::SHT_REL: |
+ case ELF::SHT_RELA: { |
SectionRelocMap[getSection(sh->sh_info)].push_back(i); |
+ break; |
} |
- if (sh->sh_type == ELF::SHT_DYNAMIC) { |
+ case ELF::SHT_DYNAMIC: { |
if (dot_dynamic_sec != NULL) |
// FIXME: Proper error handling. |
report_fatal_error("More than one .dynamic!"); |
dot_dynamic_sec = sh; |
+ break; |
} |
+ case ELF::SHT_GNU_versym: { |
+ if (dot_gnu_version_sec != NULL) |
+ // FIXME: Proper error handling. |
+ report_fatal_error("More than one .gnu.version section!"); |
+ dot_gnu_version_sec = sh; |
+ break; |
+ } |
+ case ELF::SHT_GNU_verdef: { |
+ if (dot_gnu_version_d_sec != NULL) |
+ // FIXME: Proper error handling. |
+ report_fatal_error("More than one .gnu.version_d section!"); |
+ dot_gnu_version_d_sec = sh; |
+ break; |
+ } |
+ case ELF::SHT_GNU_verneed: { |
+ if (dot_gnu_version_r_sec != NULL) |
+ // FIXME: Proper error handling. |
+ report_fatal_error("More than one .gnu.version_r section!"); |
+ dot_gnu_version_r_sec = sh; |
+ break; |
+ } |
+ } |
++sh; |
} |
@@ -1774,6 +2010,89 @@ |
} |
template<support::endianness target_endianness, bool is64Bits> |
+error_code ELFObjectFile<target_endianness, is64Bits> |
+ ::getSymbolVersion(const Elf_Shdr *section, |
+ const Elf_Sym *symb, |
+ StringRef &Version, |
+ bool &IsDefault) const { |
+ // Handle non-dynamic symbols. |
+ if (section != SymbolTableSections[0]) { |
+ // Non-dynamic symbols can have versions in their names |
+ // A name of the form 'foo@V1' indicates version 'V1', non-default. |
+ // A name of the form 'foo@@V2' indicates version 'V2', default version. |
+ StringRef Name; |
+ error_code ec = getSymbolName(section, symb, Name); |
+ if (ec != object_error::success) |
+ return ec; |
+ size_t atpos = Name.find('@'); |
+ if (atpos == StringRef::npos) { |
+ Version = ""; |
+ IsDefault = false; |
+ return object_error::success; |
+ } |
+ ++atpos; |
+ if (atpos < Name.size() && Name[atpos] == '@') { |
+ IsDefault = true; |
+ ++atpos; |
+ } else { |
+ IsDefault = false; |
+ } |
+ Version = Name.substr(atpos); |
+ return object_error::success; |
+ } |
+ |
+ // This is a dynamic symbol. Look in the GNU symbol version table. |
+ if (dot_gnu_version_sec == NULL) { |
+ // No version table. |
+ Version = ""; |
+ IsDefault = false; |
+ return object_error::success; |
+ } |
+ |
+ // Determine the position in the symbol table of this entry. |
+ const char *sec_start = (const char*)base() + section->sh_offset; |
+ size_t entry_index = ((const char*)symb - sec_start)/section->sh_entsize; |
+ |
+ // Get the corresponding version index entry |
+ const Elf_Versym *vs = getEntry<Elf_Versym>(dot_gnu_version_sec, entry_index); |
+ size_t version_index = vs->vs_index & ELF::VERSYM_VERSION; |
+ |
+ // Special markers for unversioned symbols. |
+ if (version_index == ELF::VER_NDX_LOCAL || |
+ version_index == ELF::VER_NDX_GLOBAL) { |
+ Version = ""; |
+ IsDefault = false; |
+ return object_error::success; |
+ } |
+ |
+ // Lookup this symbol in the version table |
+ LoadVersionMap(); |
+ if (version_index >= VersionMap.size() || VersionMap[version_index].isNull()) |
+ report_fatal_error("Symbol has version index without corresponding " |
+ "define or reference entry"); |
+ const VersionMapEntry &entry = VersionMap[version_index]; |
+ |
+ // Get the version name string |
+ size_t name_offset; |
+ if (entry.isVerdef()) { |
+ // The first Verdaux entry holds the name. |
+ name_offset = entry.getVerdef()->getAux()->vda_name; |
+ } else { |
+ name_offset = entry.getVernaux()->vna_name; |
+ } |
+ Version = getString(dot_dynstr_sec, name_offset); |
+ |
+ // Set IsDefault |
+ if (entry.isVerdef()) { |
+ IsDefault = !(vs->vs_index & ELF::VERSYM_HIDDEN); |
+ } else { |
+ IsDefault = false; |
+ } |
+ |
+ return object_error::success; |
+} |
+ |
+template<support::endianness target_endianness, bool is64Bits> |
inline DynRefImpl<target_endianness, is64Bits> |
::DynRefImpl(DataRefImpl DynP, const OwningType *Owner) |
: DynPimpl(DynP) |
@@ -1821,7 +2140,36 @@ |
return DynPimpl; |
} |
+/// This is a generic interface for retrieving GNU symbol version |
+/// information from an ELFObjectFile. |
+static inline error_code GetELFSymbolVersion(const ObjectFile *Obj, |
+ const SymbolRef &Sym, |
+ StringRef &Version, |
+ bool &IsDefault) { |
+ // Little-endian 32-bit |
+ if (const ELFObjectFile<support::little, false> *ELFObj = |
+ dyn_cast<ELFObjectFile<support::little, false> >(Obj)) |
+ return ELFObj->getSymbolVersion(Sym, Version, IsDefault); |
+ |
+ // Big-endian 32-bit |
+ if (const ELFObjectFile<support::big, false> *ELFObj = |
+ dyn_cast<ELFObjectFile<support::big, false> >(Obj)) |
+ return ELFObj->getSymbolVersion(Sym, Version, IsDefault); |
+ |
+ // Little-endian 64-bit |
+ if (const ELFObjectFile<support::little, true> *ELFObj = |
+ dyn_cast<ELFObjectFile<support::little, true> >(Obj)) |
+ return ELFObj->getSymbolVersion(Sym, Version, IsDefault); |
+ |
+ // Big-endian 64-bit |
+ if (const ELFObjectFile<support::big, true> *ELFObj = |
+ dyn_cast<ELFObjectFile<support::big, true> >(Obj)) |
+ return ELFObj->getSymbolVersion(Sym, Version, IsDefault); |
+ |
+ llvm_unreachable("Object passed to GetELFSymbolVersion() is not ELF"); |
} |
+ |
} |
+} |
#endif |