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 which only /// provides an interface for reading object files. +template struct MutableELFSymbol { + using Elf_Sym = Elf_Sym_Impl; + + 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 = Elf_Shdr_Impl; using Elf_Ehdr = Elf_Ehdr_Impl; + using Elf_Sym = Elf_Sym_Impl; + + using MutableSymbolTable = MutableTable>; MutableTable> Sections; + MutableSymbolTable Symbols; + MutableSymbolTable DynSymbols; const Elf_Ehdr &getHeader() const { return *reinterpret_cast(this->base()); @@ -157,18 +178,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 &getWhichTable(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 +208,54 @@ 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); + } + + Expected &> getMutableSymbol(symbol_iterator Sym) { + return getMutableSymbol(*Sym); + } + + /// Returns a mutable reference to the dynamic symbol at the specified index. + Expected &> getDynMutableSymbol(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); + } + + Expected &> getMutableDynSymbol(symbol_iterator Sym) { + return getMutableDynSymbol(*Sym); + } + /// 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} @@ -189,13 +267,7 @@ /// MutSecOrErr->Header.sh_addrallgin = 1; /// } /// @endcode - Expected &> getMutableSection(SectionRef Sec) { - const Elf_Shdr_Impl &Header = Sections[Sec.getRawDataRefImpl().p]; - Expected Name = getSectionName(Sec.getRawDataRefImpl()); - if (!Name) - return Name.takeError(); - return Sections.makeMutable(Sec.getRawDataRefImpl().p, Header, *Name, this); - } + Expected &> getMutableSection(SectionRef Sec); /// Returns a mutable reference to the section pointed to by the /// section_iterator. It is usually more ergonomic to use the overload @@ -231,6 +303,64 @@ return ELFObjectFile::getSectionContents(Sec); } +template +Expected +MutableELFObject::getSymbolName(DataRefImpl Sym) const { + const MutableSymbolTable &SymbolTable = getWhichTable(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; +} + +template +Expected &> +MutableELFObject::getMutableSection(SectionRef Sec) { + const Elf_Shdr_Impl &Header = Sections[Sec.getRawDataRefImpl().p]; + Expected Name = getSectionName(Sec.getRawDataRefImpl()); + if (!Name) + return Name.takeError(); + return Sections.makeMutable(Sec.getRawDataRefImpl().p, Header, *Name, this); +} + } // namespace object } // namespace llvm Index: llvm/unittests/Object/MutableELFObjectTest.cpp =================================================================== --- llvm/unittests/Object/MutableELFObjectTest.cpp +++ llvm/unittests/Object/MutableELFObjectTest.cpp @@ -212,3 +212,187 @@ 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)); + + auto TestSym = symbol_iterator(++MutableObject.symbol_begin()); + EXPECT_EQ(TestSym->getValue(), 0x1234U); + EXPECT_FALSE(TestSym->getFlags() & SymbolRef::SF_Global); + auto NameOrErr = TestSym->getName(); + ASSERT_THAT_EXPECTED(NameOrErr, Succeeded()); + EXPECT_EQ(*NameOrErr, "test"); + + auto SecondSym = symbol_iterator(++TestSym); + 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 public methods on SymbolRef 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> ObjOrErr = + ObjectFile::createObjectFile(MemoryBufferRef(OS.str(), "YamlObject")); + ASSERT_THAT_EXPECTED(ObjOrErr, Succeeded()); + auto *ELFObjFile = dyn_cast>(ObjOrErr->get()); + ASSERT_TRUE(ELFObjFile); + const ObjectFile &ObjFile = *ELFObjFile; + + Expected> MutObjOrErr = + ObjectFile::createObjectFile(MemoryBufferRef(OS.str(), "YamlObject")); + ASSERT_THAT_EXPECTED(MutObjOrErr, Succeeded()); + ELFObjFile = dyn_cast>(MutObjOrErr->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(std::move(*ELFObjFile)); + + auto TestSymbols = [](SymbolRef ObjFile, SymbolRef MutObj) { + EXPECT_EQ(ObjFile.getValue(), MutObj.getValue()); + EXPECT_EQ(ObjFile.getAlignment(), MutObj.getAlignment()); + EXPECT_EQ(ObjFile.getCommonSize(), MutObj.getCommonSize()); + EXPECT_EQ(ObjFile.getValue(), MutObj.getValue()); + + auto ObjNameOrErr = ObjFile.getName(); + ASSERT_THAT_EXPECTED(ObjNameOrErr, Succeeded()); + auto MutNameOrErr = MutObj.getName(); + ASSERT_THAT_EXPECTED(MutNameOrErr, Succeeded()); + EXPECT_EQ(*ObjNameOrErr, *MutNameOrErr); + + auto ObjAddrOrErr = ObjFile.getAddress(); + ASSERT_THAT_EXPECTED(ObjAddrOrErr, Succeeded()); + auto MutAddrOrErr = MutObj.getAddress(); + ASSERT_THAT_EXPECTED(MutAddrOrErr, Succeeded()); + EXPECT_EQ(*ObjAddrOrErr, *MutAddrOrErr); + + auto ObjTypeOrErr = ObjFile.getType(); + ASSERT_THAT_EXPECTED(ObjTypeOrErr, Succeeded()); + auto MutTypeOrErr = MutObj.getType(); + ASSERT_THAT_EXPECTED(MutTypeOrErr, Succeeded()); + EXPECT_EQ(*ObjTypeOrErr, *MutTypeOrErr); + }; + + for (const auto &Tuple : zip(ObjFile.symbols(), MutableObject.symbols())) + TestSymbols(std::get<0>(Tuple), std::get<1>(Tuple)); + + for (const SymbolRef &Sym : MutableObject.symbols()) { + auto MutSymOrErr = MutableObject.getMutableSymbol(Sym); + if (!MutSymOrErr) + consumeError(MutSymOrErr.takeError()); + } + + for (const auto &Tuple : zip(ObjFile.symbols(), MutableObject.symbols())) + TestSymbols(std::get<0>(Tuple), std::get<1>(Tuple)); +} + +// Test basic public methods on dynamic 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)); + + auto TestSym = symbol_iterator(++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(MutSymOrErr->Header.st_value, 5u); + + EXPECT_EQ(MutSymOrErr->Name, "test"); + MutSymOrErr->Name = "new_name"; + EXPECT_EQ(MutSymOrErr->Name, "new_name"); + TestSym = symbol_iterator(++MutableObject.symbol_begin()); + auto NameOrErr = TestSym->getName(); + ASSERT_THAT_EXPECTED(NameOrErr, Succeeded()); + EXPECT_EQ(*NameOrErr, "new_name"); +} + +// Test basic public methods on dynamic symbols. +TEST(MutableELFObject, BasicDynSymb) { + 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)); + + auto DynSym = symbol_iterator(++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"); +}