Index: llvm/include/llvm/Object/ELFObjectFile.h =================================================================== --- llvm/include/llvm/Object/ELFObjectFile.h +++ llvm/include/llvm/Object/ELFObjectFile.h @@ -394,7 +394,7 @@ const Elf_Rel *getRel(DataRefImpl Rel) const; const Elf_Rela *getRela(DataRefImpl Rela) const; - const Elf_Sym *getSymbol(DataRefImpl Sym) const { + virtual const Elf_Sym *getSymbol(DataRefImpl Sym) const { auto Ret = EF.template getEntry(Sym.d.a, Sym.d.b); if (!Ret) report_fatal_error(errorToErrorCode(Ret.takeError()).message()); @@ -408,8 +408,8 @@ basic_symbol_iterator symbol_begin() const override; basic_symbol_iterator symbol_end() const override; - elf_symbol_iterator dynamic_symbol_begin() const; - elf_symbol_iterator dynamic_symbol_end() const; + virtual elf_symbol_iterator dynamic_symbol_begin() const; + virtual elf_symbol_iterator dynamic_symbol_end() const; section_iterator section_begin() const override; section_iterator section_end() const override; Index: llvm/include/llvm/Object/MutableELFObject.h =================================================================== --- llvm/include/llvm/Object/MutableELFObject.h +++ llvm/include/llvm/Object/MutableELFObject.h @@ -44,6 +44,18 @@ /// This class is used for doing mutations on an ELFObjectFile. The /// ELFObjectFile class only provides an interface for reading object files. +template struct MutableELFSymbol { + using Elf_Sym = typename ELFT::Sym; + + Elf_Sym Header; + std::string Name; + + MutableELFSymbol(const Elf_Sym &Header, StringRef Name) + : Header(Header), Name(Name) {} + + operator const Elf_Sym &() const { return Header; } +}; + template class MutableELFObject : public ELFObjectFile { /// This class is used for a 'copy on write' effect with tables in an ELF /// object file. @@ -56,11 +68,13 @@ /// The table keeps a list of mappings, these mappings can have one of two /// states either Original or New. In the case of Original the index /// associated with the mapping is into the original table in the file. For - /// a New mapping, the index is into the NewValues vector. This design allows - /// fewer copies to be made than there would otherwise need to be, entries - /// with no modifications never get copied and the only overhead for those is - /// an index. Entries which get modified can have richer types during program - /// executation than are allowed by the object file standard. + /// a New mapping, the index is into the NewValues vector. + /// + /// The design of MutableTable allows fewer copies to be made than there + /// would otherwise need to be: entries with no modifications never get + /// copied and the only overhead for those is an index. Entries which get + /// modified can have richer types during program executation than are + /// allowed by the object file standard. template class MutableTable { struct MappingType { enum MappedType { Original, New }; @@ -129,12 +143,19 @@ /// Return the number of elements in the table. size_t size() const { return Mappings.size(); } + + size_t originalSize() const { return OriginalValues.size(); } }; using Elf_Shdr = typename ELFT::Shdr; using Elf_Ehdr = typename ELFT::Ehdr; + using Elf_Sym = typename ELFT::Sym; + + using MutableSymbolTable = MutableTable>; MutableTable> Sections; + MutableSymbolTable Symbols; + MutableSymbolTable DynSymbols; const Elf_Ehdr &getHeader() const { return *reinterpret_cast(this->base()); @@ -147,6 +168,13 @@ return &Sections[Sec.p]; } + const Elf_Sym *getSymbol(DataRefImpl Sym) const override { + const MutableSymbolTable &Table = getSymbolTable(Sym); + if (const MutableELFSymbol *SymOrNull = Table.getConstIfNew(Sym.d.b)) + return &SymOrNull->Header; + return ELFObjectFile::getSymbol(Sym); + } + /// moveSectionNext must be overriden because the section_iterators in /// MutableELFObject work differently than in ELFObjectFile. In this class, /// sections are iterated with their index, not address in the file, which @@ -157,18 +185,27 @@ Expected> getSectionContents(DataRefImpl Sec) const override; - static DataRefImpl toDataRef(uintptr_t Ptr) { - DataRefImpl Ref; - Ref.p = Ptr; - return Ref; + Expected getSymbolName(DataRefImpl Sym) const override; + ArrayRef findSymbolTable(uint64_t ShType) const; + uint32_t findSectionOfType(uint64_t ShType) const; + + const MutableSymbolTable &getSymbolTable(DataRefImpl Sym) const { + return Sections.getOriginal(Sym.d.a).sh_type == ELF::SHT_SYMTAB + ? Symbols + : DynSymbols; } + static DataRefImpl toDataRef(uintptr_t Ptr); + static DataRefImpl toDataRef(uint32_t A, uint32_t B); + public: explicit MutableELFObject(ELFObjectFile &&B) : ELFObjectFile(std::move(B)), Sections(ArrayRef(reinterpret_cast( this->base() + getHeader().e_shoff), - getHeader().e_shnum)) {} + getHeader().e_shnum)), + Symbols(findSymbolTable(ELF::SHT_SYMTAB)), + DynSymbols(findSymbolTable(ELF::SHT_DYNSYM)) {} section_iterator section_begin() const override { return section_iterator(SectionRef(toDataRef(0), this)); @@ -178,6 +215,46 @@ return section_iterator(SectionRef(toDataRef(Sections.size()), this)); } + basic_symbol_iterator symbol_begin() const override { + return basic_symbol_iterator( + SymbolRef(toDataRef(findSectionOfType(ELF::SHT_SYMTAB), 0), this)); + } + + basic_symbol_iterator symbol_end() const override { + return basic_symbol_iterator(SymbolRef( + toDataRef(findSectionOfType(ELF::SHT_SYMTAB), Symbols.size()), this)); + } + + elf_symbol_iterator dynamic_symbol_begin() const override { + return basic_symbol_iterator( + SymbolRef(toDataRef(findSectionOfType(ELF::SHT_DYNSYM), 0), this)); + } + + elf_symbol_iterator dynamic_symbol_end() const override { + return basic_symbol_iterator(SymbolRef( + toDataRef(findSectionOfType(ELF::SHT_DYNSYM), DynSymbols.size()), + this)); + } + + /// Returns a mutable reference to the symbol at the specified index. + Expected &> getMutableSymbol(SymbolRef Sym) { + Expected Name = getSymbolName(Sym.getRawDataRefImpl()); + if (!Name) + return Name.takeError(); + return Symbols.makeMutable(Sym.getRawDataRefImpl().d.b, + Symbols[Sym.getRawDataRefImpl().d.b], *Name); + } + + /// Returns a mutable reference to the dynamic symbol at the specified index. + Expected &> getMutableDynamicSymbol(SymbolRef Sym) { + Expected Name = getSymbolName(Sym.getRawDataRefImpl()); + if (!Name) + return Name.takeError(); + return DynSymbols.makeMutable(Sym.getRawDataRefImpl().d.b, + DynSymbols[Sym.getRawDataRefImpl().d.b], + *Name); + } + /// Returns a mutable reference to the section pointed to by Sec. A possible /// usage to change all sections with alignment of 0 to 1 could be: /// @code{.cpp} @@ -196,13 +273,6 @@ return Name.takeError(); return Sections.makeMutable(Sec.getRawDataRefImpl().p, Header, *Name, this); } - - /// Returns a mutable reference to the section pointed to by the - /// section_iterator. It is usually more ergonomic to use the overload - /// which takes a SectionRef. - Expected &> getMutableSection(section_iterator Sec) { - return getMutableSection(*Sec); - } }; template @@ -231,6 +301,54 @@ return ELFObjectFile::getSectionContents(Sec); } +template +Expected +MutableELFObject::getSymbolName(DataRefImpl Sym) const { + const MutableSymbolTable &SymbolTable = getSymbolTable(Sym); + if (const MutableELFSymbol *SymOrNull = + SymbolTable.getConstIfNew(Sym.d.b)) + return SymOrNull->Name; + return ELFObjectFile::getSymbolName(Sym); +} + +template +ArrayRef +MutableELFObject::findSymbolTable(uint64_t ShType) const { + assert(ShType == ELF::SHT_SYMTAB || + ShType == ELF::SHT_DYNSYM && "Not a symbol table type"); + for (const auto &Sec : this->sections()) + if (ELFSectionRef(Sec).getType() == ShType) + return ArrayRef( + reinterpret_cast(this->base() + + ELFSectionRef(Sec).getOffset()), + Sec.getSize() / sizeof(Elf_Sym)); + + return {}; +} + +template +uint32_t MutableELFObject::findSectionOfType(uint64_t ShType) const { + for (uint32_t I = 0; I < Sections.originalSize(); ++I) + if (Sections.getOriginal(I).sh_type == ShType) + return I; + return 0; +} + +template +DataRefImpl MutableELFObject::toDataRef(uintptr_t Ptr) { + DataRefImpl Ref; + Ref.p = Ptr; + return Ref; +} + +template +DataRefImpl MutableELFObject::toDataRef(uint32_t A, uint32_t B) { + DataRefImpl Ref; + Ref.d.a = A; + Ref.d.b = B; + return Ref; +} + } // namespace object } // namespace llvm Index: llvm/unittests/Object/MutableELFObjectTest.cpp =================================================================== --- llvm/unittests/Object/MutableELFObjectTest.cpp +++ llvm/unittests/Object/MutableELFObjectTest.cpp @@ -133,7 +133,7 @@ Iter = MutableObject.section_begin(); std::advance(Iter, 2); - auto MutSectionOrErr = MutableObject.getMutableSection(Iter); + auto MutSectionOrErr = MutableObject.getMutableSection(*Iter); ASSERT_EQ(std::distance(MutableObject.section_begin(), Iter), 2); ASSERT_EQ( std::distance(MutableObject.section_begin(), MutableObject.section_end()), @@ -181,7 +181,7 @@ ArrayRef Data{'1', '2', '3', '4'}; - auto MutSecOrErr = MutableObject.getMutableSection(FirstSec); + auto MutSecOrErr = MutableObject.getMutableSection(*FirstSec); ASSERT_THAT_EXPECTED(MutSecOrErr, Succeeded()); MutSecOrErr->setData(Data); @@ -208,3 +208,189 @@ std::distance(MutableObject.section_begin(), MutableObject.section_end()); EXPECT_EQ(NewNumSections, NumSections); } + +// Test basic public methods on symbols. +TEST(MutableELFObject, BasicSymbol) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Symbols: + - Name: test + Index: SHN_ABS + Value: 0x1234 + Binding: STB_LOCAL + - Name: second + Index: SHN_ABS + Value: 1 + Binding: STB_GLOBAL)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(std::move(*ELFObjFile)); + + EXPECT_EQ( + std::distance(MutableObject.symbol_begin(), MutableObject.symbol_end()), + 3); + + symbol_iterator FirstSym(++MutableObject.symbol_begin()); + EXPECT_EQ(FirstSym->getValue(), 0x1234U); + EXPECT_FALSE(FirstSym->getFlags() & SymbolRef::SF_Global); + auto NameOrErr = FirstSym->getName(); + ASSERT_THAT_EXPECTED(NameOrErr, Succeeded()); + EXPECT_EQ(*NameOrErr, "test"); + + symbol_iterator SecondSym(++FirstSym); + EXPECT_EQ(SecondSym->getValue(), 1U); + EXPECT_TRUE(SecondSym->getFlags() & SymbolRef::SF_Global); + NameOrErr = SecondSym->getName(); + ASSERT_THAT_EXPECTED(NameOrErr, Succeeded()); + EXPECT_EQ(*NameOrErr, "second"); +} + +// Test that there is no change in SymbolRef's public methods between +// MutableELFObjectFile and ELFObjectFile. +TEST(MutableELFObject, SameAsELFObjectFile) { + StringRef Yaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DynamicSymbols: + - Name: test + Index: SHN_ABS + Value: 0x1234 + Binding: STB_GLOBAL + - Name: second + Index: SHN_ABS + Value: 0 + Binding: STB_GLOBAL)"; + + SmallString<0> Storage; + raw_svector_ostream OS(Storage); + yaml::Input Input(Yaml); + ASSERT_THAT_ERROR(convertYAML(Input, OS), Succeeded()); + + Expected> MutObjOrErr = + ObjectFile::createObjectFile(MemoryBufferRef(OS.str(), "YamlObject")); + ASSERT_THAT_EXPECTED(MutObjOrErr, Succeeded()); + auto *ELFObjFile = dyn_cast>(MutObjOrErr->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(std::move(*ELFObjFile)); + + Expected> ObjOrErr = + ObjectFile::createObjectFile(MemoryBufferRef(OS.str(), "YamlObject")); + ASSERT_THAT_EXPECTED(ObjOrErr, Succeeded()); + ELFObjFile = dyn_cast>(ObjOrErr->get()); + ASSERT_TRUE(ELFObjFile); + + auto TestSymbols = [](SymbolRef FromBase, SymbolRef FromMutable) { + EXPECT_EQ(FromBase.getValue(), FromMutable.getValue()); + EXPECT_EQ(FromBase.getAlignment(), FromMutable.getAlignment()); + EXPECT_EQ(FromBase.getCommonSize(), FromMutable.getCommonSize()); + EXPECT_EQ(FromBase.getValue(), FromMutable.getValue()); + + auto ObjNameOrErr = FromBase.getName(); + ASSERT_THAT_EXPECTED(ObjNameOrErr, Succeeded()); + auto MutNameOrErr = FromMutable.getName(); + ASSERT_THAT_EXPECTED(MutNameOrErr, Succeeded()); + EXPECT_EQ(*ObjNameOrErr, *MutNameOrErr); + + auto ObjAddrOrErr = FromBase.getAddress(); + ASSERT_THAT_EXPECTED(ObjAddrOrErr, Succeeded()); + auto MutAddrOrErr = FromMutable.getAddress(); + ASSERT_THAT_EXPECTED(MutAddrOrErr, Succeeded()); + EXPECT_EQ(*ObjAddrOrErr, *MutAddrOrErr); + + auto ObjTypeOrErr = FromBase.getType(); + ASSERT_THAT_EXPECTED(ObjTypeOrErr, Succeeded()); + auto MutTypeOrErr = FromMutable.getType(); + ASSERT_THAT_EXPECTED(MutTypeOrErr, Succeeded()); + EXPECT_EQ(*ObjTypeOrErr, *MutTypeOrErr); + }; + + for (const auto &Tuple : zip(ELFObjFile->symbols(), MutableObject.symbols())) + TestSymbols(std::get<0>(Tuple), std::get<1>(Tuple)); + + // Make every symbol mutable to test that there is no change between original + // symbols and mutable symbols. + for (const SymbolRef &Sym : MutableObject.symbols()) + ASSERT_THAT_EXPECTED(MutableObject.getMutableSymbol(Sym), Succeeded()); + + for (const auto &Tuple : zip(ELFObjFile->symbols(), MutableObject.symbols())) + TestSymbols(std::get<0>(Tuple), std::get<1>(Tuple)); +} + +// Test mutating symbols. +TEST(MutableELFObject, MutateSymbol) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Symbols: + - Name: test + Index: SHN_ABS + Value: 2 + Binding: STB_GLOBAL)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(std::move(*ELFObjFile)); + + symbol_iterator TestSym(++MutableObject.symbol_begin()); + Expected &> MutSymOrErr = + MutableObject.getMutableSymbol(*TestSym); + ASSERT_THAT_EXPECTED(MutSymOrErr, Succeeded()); + EXPECT_EQ(MutSymOrErr->Header.st_value, 2u); + MutSymOrErr->Header.st_value = 5; + EXPECT_EQ(TestSym->getValue(), 5u); + + EXPECT_EQ(MutSymOrErr->Name, "test"); + MutSymOrErr->Name = "new_name"; + Expected NameOrErr = TestSym->getName(); + ASSERT_THAT_EXPECTED(NameOrErr, Succeeded()); + EXPECT_EQ(*NameOrErr, "new_name"); +} + +// Test basic public methods on dynamic symbols. Symbols and dynamic symbols +// share the same inner working so it isn't crucial to test both. This test of +// dynamic symbols is enough to ensure that dynamic symbols work properly. +TEST(MutableELFObject, BasicDynamicSymbol) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DynamicSymbols: + - Name: test + Index: SHN_ABS + Value: 0x1234 + Binding: STB_GLOBAL)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(std::move(*ELFObjFile)); + + symbol_iterator DynSym(++MutableObject.dynamic_symbol_begin()); + EXPECT_EQ(DynSym->getValue(), 0x1234U); + EXPECT_TRUE(DynSym->getFlags() & SymbolRef::SF_Global); + auto NameOrErr = DynSym->getName(); + ASSERT_THAT_EXPECTED(NameOrErr, Succeeded()); + EXPECT_EQ(*NameOrErr, "test"); +}