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 generic ABI. +/// 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,63 @@ 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; + + auto SymsOrErr = symbols(*SymTabOrErr); + if (!SymsOrErr) + return SymsOrErr.takeError(); + ArrayRef SymTab = *SymsOrErr; + + const Elf_Hash *HashTab = + reinterpret_cast(base() + Sec.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 (Sec.sh_size < sizeof(Elf_Hash) || + Sec.sh_size < sizeof(Elf_Hash) + sizeof(Elf_Word) * HashTab->nbucket + + sizeof(Elf_Word) * HashTab->nchain) + return createError("section " + getSecIndexForError(*this, Sec) + + " has invalid sh_size: " + Twine(Sec.sh_size)); + + const uint32_t Hash = hashSysV(Name); + const Elf_Word NBucket = HashTab->nbucket; + ArrayRef Bucket = HashTab->buckets(); + ArrayRef Chain = HashTab->chains(); + + for (Elf_Word I = Bucket[Hash % NBucket]; I != ELF::STN_UNDEF; I = Chain[I]) { + if (I >= SymTab.size()) + return createError("symbol [index " + Twine(I) + + "] is greater than the number of symbols: " + + Twine(SymTab.size())); + 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 +1283,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,374 @@ 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 + Link: .dynsym + Bucket: [ 1, 2, 4 ] + Chain: [ 0, 0, 0, 0, 3 ] + - Name: .dynsym + Type: SHT_DYNSYM + Link: .dynstr + - Name: .dynstr + Type: SHT_STRTAB +DynamicSymbols: + - Name: x + Index: SHN_ABS + Value: 0x0 + - Name: y + Index: SHN_ABS + Value: 0x1 + - Name: w + Index: SHN_ABS + Value: 0x2 + - Name: z + Index: SHN_ABS + Value: 0x3 +)"); + + 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()); + StringRef StrTab(ContentsOrErr->begin(), ContentsOrErr->size()); + + for (StringRef SymbolName : {"w", "x", "y", "z"}) { + 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); + } + + auto MissingOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "not_in_object"); + ASSERT_THAT_EXPECTED(MissingOrErr, Succeeded()); + ASSERT_TRUE(!*MissingOrErr); + + // Check that we return an error if the section is not ST_HASH. + auto SymOrErr = Elf.getSymbolFromHashTable(**StrTabOrErr, "x"); + EXPECT_THAT_EXPECTED( + SymOrErr, + FailedWithMessage("invalid sh_type for hash table, expected SHT_HASH")); +} + +// Test for getSymbolFromHashTable: check that it fails with an invalid offset. +TEST(ELFObjectFileTest, SymbolFromHashTableTestOffset) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Link: .dynsym + Bucket: [ 1 ] + Chain: [ 0 ] + ShOffset: 9999 + - Name: .dynsym + Type: SHT_DYNSYM + Link: .dynstr + - Name: .dynstr + Type: SHT_STRTAB +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/*SHT_HASH=*/1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + EXPECT_THAT_EXPECTED( + SymOrErr, + FailedWithMessage("section [index 1] has invalid sh_offset: 9999")); +} + +// Test for getSymbolFromHashTable: check that it fails with an invalid offset. +TEST(ELFObjectFileTest, SymbolFromHashTableTestStrOffset) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Link: .dynsym + Bucket: [ 1 ] + Chain: [ 0 ] + - Name: .dynsym + Type: SHT_DYNSYM + Link: .dynstr + ShOffset: 9999 + - Name: .dynstr + Type: SHT_STRTAB +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/*SHT_HASH=*/1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + EXPECT_THAT_EXPECTED( + SymOrErr, + FailedWithMessage("section [index 2] has a sh_offset (0x270f) + sh_size (0x18) that is greater than the file size (0x218)")); +} + +// Test for getSymbolFromHashTable: check that it fails with an invalid size. +TEST(ELFObjectFileTest, SymbolFromHashTableTestSize) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Link: .dynsym + Bucket: [ 1 ] + Chain: [ 0, 2, 0 ] + ShSize: 1 + - Name: .dynsym + Type: SHT_DYNSYM + Link: .dynstr + - Name: .dynstr + Type: SHT_STRTAB +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/*SHT_HASH=*/1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + EXPECT_THAT_EXPECTED( + SymOrErr, FailedWithMessage("section [index 1] has invalid sh_size: 1")); +} + +// Test for getSymbolFromHashTable: check that it fails with an invalid size +// containing the chains and buckets. +TEST(ELFObjectFileTest, SymbolFromHashTableTestSizeHeader) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Link: .dynsym + Bucket: [ 1 ] + Chain: [ 0 ] + ShSize: 9 + - Name: .dynsym + Type: SHT_DYNSYM + Link: .dynstr + - Name: .dynstr + Type: SHT_STRTAB +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/*SHT_HASH=*/1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + EXPECT_THAT_EXPECTED( + SymOrErr, FailedWithMessage("section [index 1] has invalid sh_size: 9")); +} + +// Test for getSymbolFromHashTable: check that it fails with an invalid symbol +// index. +TEST(ELFObjectFileTest, SymbolFromHashTableTestSymIndex) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Link: .dynsym + Bucket: [ 999 ] + Chain: [ 0 ] + - Name: .dynsym + Type: SHT_DYNSYM + Link: .dynstr + - Name: .dynstr + Type: SHT_STRTAB +DynamicSymbols: + - Name: x + Index: SHN_ABS + Value: 0x1 +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/*SHT_HASH=*/1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + EXPECT_THAT_EXPECTED( + SymOrErr, + FailedWithMessage( + "symbol [index 999] is greater than the number of symbols: 2")); +} + +// Test for getSymbolFromHashTable: check that it fails with an invalid symbol +// name offset. +TEST(ELFObjectFileTest, SymbolFromHashTableTestName) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Link: .dynsym + Bucket: [ 1 ] + Chain: [ 0 ] + - Name: .dynsym + Type: SHT_DYNSYM + Link: .dynstr + - Name: .dynstr + Type: SHT_STRTAB +DynamicSymbols: + - Name: x + Index: SHN_ABS + Value: 0x1 + StName: 9999 +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/*SHT_HASH=*/1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + EXPECT_THAT_EXPECTED( + SymOrErr, + FailedWithMessage("symbol [index 1] has invalid st_name: 9999")); +} + +// Test for getSymbolFromHashTable: check that it fails when not referencing a +// dynamic symbol table. +TEST(ELFObjectFileTest, SymbolFromHashTableTestLink) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Link: .dynstr + - Name: .dynstr + Type: SHT_STRTAB +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/*SHT_HASH=*/1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + EXPECT_THAT_EXPECTED(SymOrErr, + FailedWithMessage("invalid sh_type for symbol table, " + "expected SHT_SYMTAB or SHT_DYNSYM")); +} + +// Test for getSymbolFromHashTable: check that it fails with an invalid sh_link. +TEST(ELFObjectFileTest, SymbolFromHashTableTestSymLink) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH + Link: 999 +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/*SHT_HASH=*/1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + EXPECT_THAT_EXPECTED(SymOrErr, + FailedWithMessage("invalid section index: 999")); +} + +// Test for getSymbolFromHashTable: check that it fails given an invalid section +// header table offset. +TEST(ELFObjectFileTest, SymbolFromHashTableTestHeaderOffset) { + SmallString<0> Storage; + Expected> ElfOrErr = toBinary(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .hash + Type: SHT_HASH +)"); + + ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); + const ELFFile &Elf = ElfOrErr->getELFFile(); + + auto HashOrErr = Elf.getSection(/*SHT_HASH=*/1); + ASSERT_THAT_EXPECTED(HashOrErr, Succeeded()); + + // Mutate this directly, otherwise it would fail to parse the ELF above. + const_cast(&Elf.getHeader())->e_shoff = 9999; + auto SymOrErr = Elf.getSymbolFromHashTable(**HashOrErr, "x"); + EXPECT_THAT_EXPECTED(SymOrErr, + FailedWithMessage("section header table goes past the " + "end of the file: e_shoff = 0x270f")); +}