diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h --- a/llvm/include/llvm/Object/ELF.h +++ b/llvm/include/llvm/Object/ELF.h @@ -71,6 +71,21 @@ uint32_t getELFRelativeRelocationType(uint32_t Machine); StringRef getELFSectionTypeName(uint32_t Machine, uint32_t Type); +/// This function returns the hash value for a symbol in the .dynsym section +/// Name of the API remains consistent as specified in the libelf +/// REF : http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#hash +inline unsigned hashSysV(StringRef SymbolName) { + unsigned h = 0, g; + for (char C : SymbolName) { + h = (h << 4) + C; + g = h & 0xf0000000L; + if (g != 0) + h ^= g >> 24; + h &= ~g; + } + return h; +} + // Subclasses of ELFFile may need this for template instantiation inline std::pair getElfArchType(StringRef Object) { @@ -380,6 +395,9 @@ Expected getSymbol(const Elf_Shdr *Sec, uint32_t Index) const; + Expected getSymbolFromHashTable(const Elf_Shdr &Sec, + StringRef Name) const; + Expected getSectionName(const Elf_Shdr &Section, WarningHandler WarnHandler = &defaultWarningHandler) const; @@ -480,6 +498,54 @@ return &Symbols[Index]; } +template +Expected +ELFFile::getSymbolFromHashTable(const Elf_Shdr &Sec, + StringRef Name) const { + if (Sec.sh_type != ELF::SHT_HASH) + return createError("invalid sh_type for hash table, expected SHT_HASH"); + Expected SectionsOrError = sections(); + if (!SectionsOrError) + return SectionsOrError.takeError(); + + auto SymTabOrErr = object::getSection(*SectionsOrError, Sec.sh_link); + if (!SymTabOrErr) + return SymTabOrErr.takeError(); + auto StrTabOrErr = getStringTableForSymtab(**SymTabOrErr, *SectionsOrError); + if (!StrTabOrErr) + return StrTabOrErr.takeError(); + StringRef StrTab = *StrTabOrErr; + + const Elf_Hash *HashTab = + reinterpret_cast(base() + Sec.sh_offset); + const Elf_Sym *SymTab = + reinterpret_cast(base() + (*SymTabOrErr)->sh_offset); + + if (Sec.sh_offset + Sec.sh_size >= Buf.size()) + return createError("section " + getSecIndexForError(*this, Sec) + + "has invalid sh_offset: " + Twine(Sec.sh_offset)); + if ((*SymTabOrErr)->sh_offset + (*SymTabOrErr)->sh_size >= Buf.size()) + return createError( + "section " + getSecIndexForError(*this, **SymTabOrErr) + + "has invalid sh_offset: " + Twine((*SymTabOrErr)->sh_offset)); + + const uint32_t Hash = hashSysV(Name); + const uint32_t NBucket = HashTab->nbucket; + const auto Bucket = HashTab->buckets(); + const auto Chain = HashTab->chains(); + + for (uint32_t I = Bucket[Hash % NBucket]; I != ELF::STN_UNDEF; I = Chain[I]) { + if (SymTab[I].st_name >= StrTab.size()) + return createError("symbol [index " + Twine(I) + + "] has invalid st_name: " + Twine(SymTab[I].st_name)); + + if (StrTab.drop_front(SymTab[I].st_name).data() == Name) + return &SymTab[I]; + } + + return nullptr; +} + template template Expected> @@ -1208,21 +1274,6 @@ return StringRef(DotShstrtab.data() + Offset); } -/// This function returns the hash value for a symbol in the .dynsym section -/// Name of the API remains consistent as specified in the libelf -/// REF : http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#hash -inline unsigned hashSysV(StringRef SymbolName) { - unsigned h = 0, g; - for (char C : SymbolName) { - h = (h << 4) + C; - g = h & 0xf0000000L; - if (g != 0) - h ^= g >> 24; - h &= ~g; - } - return h; -} - } // end namespace object } // end namespace llvm diff --git a/llvm/unittests/Object/ELFObjectFileTest.cpp b/llvm/unittests/Object/ELFObjectFileTest.cpp --- a/llvm/unittests/Object/ELFObjectFileTest.cpp +++ b/llvm/unittests/Object/ELFObjectFileTest.cpp @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "llvm/Object/ELFObjectFile.h" -#include "llvm/Support/MemoryBuffer.h" #include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" @@ -806,3 +806,153 @@ RelocatableFileYamlString += ContentsString; DoCheck(RelocatableFileYamlString); } + +// Test for getSymbolFromHashTable: check that it returns the expected symbol +// using its associated name. +TEST(ELFObjectFileTest, SymbolFromHashTableTest) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Flags: [ SHF_ALLOC ] + Address: 0x100 + Link: .dynsym + AddressAlign: 0x8 + Bucket: [ 1 ] + Chain: [ 0, 2, 0 ] + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ SHF_ALLOC ] + Address: 0x200 + Link: .dynstr + AddressAlign: 0x8 + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Address: 0x300 + AddressAlign: 0x1 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x2000 + AddressAlign: 0x4 + Content: DEADBEEFDEADBEEF +DynamicSymbols: + - Name: y + Type: STT_OBJECT + Section: .data + Binding: STB_GLOBAL + Value: 0x2004 + Size: 0x4 + - Name: x + Type: STT_OBJECT + Section: .data + Binding: STB_GLOBAL + Value: 0x2000 + Size: 0x4 +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/* SHT_HASH */ 1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + auto StrTabOrErr = Elf.getSection(/* SHT_STRTAB */ 3); + ASSERT_THAT_EXPECTED(StrTabOrErr, Succeeded()); + auto ContentsOrErr = Elf.getSectionContentsAsArray(**StrTabOrErr); + ASSERT_THAT_EXPECTED(ContentsOrErr, Succeeded()); + auto StrTab = StringRef(ContentsOrErr->begin(), ContentsOrErr->size()); + + for (StringRef SymbolName : {"x", "y"}) { + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, SymbolName); + ASSERT_THAT_EXPECTED(SymOrErr, Succeeded()); + auto FoundSymNameOrErr = (*SymOrErr)->getName(StrTab); + ASSERT_THAT_EXPECTED(FoundSymNameOrErr, Succeeded()); + ASSERT_TRUE(*FoundSymNameOrErr == SymbolName); + } +} + +// Check to ensure that we gracefully fail when given invalid sizes or offsets. +TEST(ELFObjectFileTest, SymbolFromHashTableTestError) { + LLVM_ELF_IMPORT_TYPES_ELFT(ELF64LE) + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Flags: [ SHF_ALLOC ] + Address: 0x100 + Link: .dynsym + AddressAlign: 0x8 + Bucket: [ 1 ] + Chain: [ 0, 0 ] + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ SHF_ALLOC ] + Address: 0x200 + Link: .dynstr + AddressAlign: 0x8 + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Address: 0x300 + AddressAlign: 0x1 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x2000 + AddressAlign: 0x4 + Content: DEADBEEF +DynamicSymbols: + - Name: x + Type: STT_OBJECT + Section: .data + Binding: STB_GLOBAL + Value: 0x2000 + Size: 0x4 +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/* SHT_HASH */ 1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + auto SymTabOrErr = Elf.getSection((*HashOrErr)->sh_link); + ASSERT_THAT_EXPECTED(SymTabOrErr, Succeeded()); + + // Ensure we can look up the original symbol. + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + ASSERT_THAT_EXPECTED(SymOrErr, Succeeded()); + + // Ensure that an invalid string table index is properly handled. + typename ELF64LE::Sym *Sym = const_cast(*SymOrErr); + Sym->st_name = 9999; + auto BadIndexErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + ASSERT_THAT_EXPECTED(BadIndexErr, Failed()); + + // Ensure that an invalid symbol table size in properly handled. + typename ELF64LE::Shdr *SymTab = + const_cast(*SymTabOrErr); + SymTab->sh_size = 9999; + auto BadSymErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + ASSERT_THAT_EXPECTED(BadSymErr, Failed()); + + // Ensure that an invalid hash table size in properly handled. + typename ELF64LE::Shdr *HashTab = + const_cast(*HashOrErr); + HashTab->sh_size = 9999; + auto BadHashErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + ASSERT_THAT_EXPECTED(BadHashErr, Failed()); +}