Index: llvm/include/llvm/Object/ELFObjectFile.h =================================================================== --- llvm/include/llvm/Object/ELFObjectFile.h +++ llvm/include/llvm/Object/ELFObjectFile.h @@ -401,7 +401,7 @@ return *Ret; } - const Elf_Shdr *getSection(DataRefImpl Sec) const { + virtual const Elf_Shdr *getSection(DataRefImpl Sec) const { return reinterpret_cast(Sec.p); } Index: llvm/include/llvm/Object/MutableELFObject.h =================================================================== --- /dev/null +++ llvm/include/llvm/Object/MutableELFObject.h @@ -0,0 +1,196 @@ +//===-- MutableELFObject.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJECT_MUTABLEELFOBJECT_H +#define LLVM_OBJECT_MUTABLEELFOBJECT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Object/ELFObjectFile.h" + +namespace llvm { +namespace object { + +// T is a wrapper type around Iterable::value_type and must have a constructor +// taking an Iterable::value_type. +template class MutableRange { +public: + struct MappingType { + uintptr_t Ptr; + bool IsNew; + + MappingType(uintptr_t Ptr, bool IsNew) : Ptr(Ptr), IsNew(IsNew) {} + }; + + using iterator = typename std::vector::iterator; + using value_type = MappingType; + +private: + std::vector Mappings; + std::vector NewValues; + +public: + template + MutableRange( + Iterable Range, + function_ref Extract) + : MutableRange(Range.begin(), Range.end(), Extract) {} + + template + MutableRange(Iter Begin, Iter End, + function_ref Extract) { + std::transform(Begin, End, std::back_inserter(Mappings), + [&Extract](typename Iter::value_type Value) { + return MappingType(Extract(Value), false); + }); + } + + MappingType operator[](uint64_t Index) const { return Mappings[Index]; } + + size_t size() const { return Mappings.size(); } + + iterator begin() { return Mappings.begin(); } + iterator end() { return Mappings.end(); } + + const T *getNew(uint64_t Index) const { + assert(Index < NewValues.size() && "Out of range"); + return &NewValues[Index]; + } + + T *getNew(uint64_t Index) { + assert(Index < NewValues.size() && "Out of range"); + return &NewValues[Index]; + } + + template + T *makeMutable(uint64_t Index, Args &&... Arguments) { + MappingType Mapping = Mappings[Index]; + if (Mapping.IsNew) + return &NewValues[Mapping.Ptr]; + NewValues.emplace_back(Mapping.Ptr, Arguments...); + Mappings[Index] = MappingType(NewValues.size() - 1, true); + return &NewValues.back(); + } +}; + +template class MutableELFObject; + +static inline DataRefImpl toDataRef(uintptr_t Ptr) { + DataRefImpl Ref; + Ref.p = Ptr; + return Ref; +} + +template struct MutableELFSection { + Elf_Shdr_Impl Header; + std::string Name; + OwningArrayRef Data; + + MutableELFSection(uintptr_t ToCopy, const MutableELFObject *ObjFile) + : Header(*ObjFile->getSection(ToCopy)), + Data(OwningArrayRef(Header.sh_size)) { + std::memcpy(Data.data(), ObjFile->base() + Header.sh_offset, + Header.sh_size); + } + + void setData(ArrayRef Ref) { + Data = OwningArrayRef(Ref); + Header.sh_size = Data.size(); + } +}; + +template class MutableELFObject : public ELFObjectFile { + friend struct MutableELFSection; + + MutableRange> Sections; + +protected: + using MappingType = + typename MutableRange>::MappingType; + + const Elf_Shdr_Impl *getSection(uintptr_t Sec) const { + return ELFObjectFile::getSection(toDataRef(Sec)); + } + + const Elf_Shdr_Impl *getSection(DataRefImpl Sec) const override { + MappingType Mapping = Sections[Sec.p]; + if (Mapping.IsNew) + return &Sections.getNew(Mapping.Ptr)->Header; + return reinterpret_cast*>(Mapping.Ptr); + } + + void moveSectionNext(DataRefImpl &Sec) const override; + Expected getSectionName(DataRefImpl Sec) const override; + uint64_t getSectionIndex(DataRefImpl Sec) const override; + Expected> + getSectionContents(DataRefImpl Sec) const override; + bool isBerkeleyData(DataRefImpl Sec) const override; + +public: + MutableELFObject(ELFObjectFile &B) + : ELFObjectFile(std::move(B)), + Sections(B.section_begin(), B.section_end(), + [](SectionRef Ref) { return Ref.getRawDataRefImpl().p; }) {} + + section_iterator section_begin() const override { + return section_iterator(SectionRef(toDataRef(0), this)); + } + + section_iterator section_end() const override { + return section_iterator(SectionRef(toDataRef(Sections.size()), this)); + } + + MutableELFSection *getMutableSection(section_iterator Sec) { + uintptr_t Index = Sec->getRawDataRefImpl().p; + return Sections.makeMutable(Index, this); + } +}; + +template +void MutableELFObject::moveSectionNext(DataRefImpl &Sec) const { + ++Sec.p; +} + +template +Expected +MutableELFObject::getSectionName(DataRefImpl Sec) const { + MappingType Mapping = Sections[Sec.p]; + if (Mapping.IsNew) { + const MutableELFSection *NewSec = Sections.getNew(Mapping.Ptr); + return NewSec->Name; + } + return ELFObjectFile::getSectionName(Sec); +} + +template +uint64_t MutableELFObject::getSectionIndex(DataRefImpl Sec) const { + return Sec.p; +} + +template +Expected> +MutableELFObject::getSectionContents(DataRefImpl Sec) const { + MappingType Mapping = Sections[Sec.p]; + if (Mapping.IsNew) { + const MutableELFSection *NewSec = Sections.getNew(Mapping.Ptr); + return ArrayRef(NewSec->Data.data(), NewSec->Header.sh_size); + } + return ELFObjectFile::getSectionContents(Sec); +} + +template +bool MutableELFObject::isBerkeleyData(DataRefImpl Sec) const { + const Elf_Shdr_Impl *EShdr = getSection(Sec); + return !this->isBerkeleyText(Sec) && + EShdr->sh_type != ELF::SHT_NOBITS && + EShdr->sh_flags & ELF::SHF_ALLOC; +} + +} // namespace object +} // namespace llvm + +#endif // LLVM_OBJECT_MUTABLEELFOBJECT_H Index: llvm/unittests/Object/CMakeLists.txt =================================================================== --- llvm/unittests/Object/CMakeLists.txt +++ llvm/unittests/Object/CMakeLists.txt @@ -1,10 +1,12 @@ set(LLVM_LINK_COMPONENTS BinaryFormat Object + ObjectYAML ) add_llvm_unittest(ObjectTests MinidumpTest.cpp + MutableELFObjectTest.cpp SymbolSizeTest.cpp SymbolicFileTest.cpp ) Index: llvm/unittests/Object/MutableELFObjectTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Object/MutableELFObjectTest.cpp @@ -0,0 +1,195 @@ +//===- MutableELFObjectTest.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/MutableELFObject.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/Error.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace object; +using namespace yaml; + +// Change a section's name and test that SectionRef::getName() returns the new +// name. +TEST(MutableELFObject, ChangeSectionName) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .sec0 + Type: SHT_PROGBITS + - Name: .sec1 + Type: SHT_PROGBITS + - Name: .sec2 + Type: SHT_PROGBITS)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(*ELFObjFile); + + auto compareSectionName = [](section_iterator Iter, const char * Name) { + StringRef SecName; + EXPECT_FALSE(Iter->getName(SecName)); + EXPECT_EQ(SecName, Name); + }; + + ptrdiff_t NumSections = + std::distance(MutableObject.section_begin(), MutableObject.section_end()); + + auto Iter = MutableObject.section_begin(); + compareSectionName(Iter, nullptr); + compareSectionName(++Iter, ".sec0"); + compareSectionName(++Iter, ".sec1"); + compareSectionName(++Iter, ".sec2"); + + Iter = MutableObject.section_begin(); + std::advance(Iter, 2); + auto *MutSection = MutableObject.getMutableSection(Iter); + MutSection->Name = ".new_name"; + + Iter = MutableObject.section_begin(); + compareSectionName(Iter, nullptr); + compareSectionName(++Iter, ".sec0"); + compareSectionName(++Iter, ".new_name"); + compareSectionName(++Iter, ".sec2"); + + // Make sure a section wasn't added. + ptrdiff_t NewNumSections = + std::distance(MutableObject.section_begin(), MutableObject.section_end()); + EXPECT_EQ(NewNumSections, NumSections); +} + +// Test MutableELFSection::setData(). +TEST(MutableELFObject, ChangeSectionContents) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Content: "DEADBEEF")"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(*ELFObjFile); + + ptrdiff_t NumSections = + std::distance(MutableObject.section_begin(), MutableObject.section_end()); + + auto FirstSec = ++MutableObject.section_begin(); + Expected Contents = FirstSec->getContents(); + ASSERT_THAT_EXPECTED(Contents, Succeeded()); + + EXPECT_EQ(*Contents, "\xDE\xAD\xBE\xEF"); + EXPECT_EQ(FirstSec->getSize(), Contents->size()); + + ArrayRef Data{'1', '2', '3', '4'}; + + auto *MutSec = MutableObject.getMutableSection(FirstSec); + MutSec->setData(Data); + + FirstSec = ++MutableObject.section_begin(); + Contents = FirstSec->getContents(); + ASSERT_THAT_EXPECTED(Contents, Succeeded()); + EXPECT_EQ(*Contents, + StringRef(reinterpret_cast(Data.data()), + Data.size())); + + MutSec->Header.sh_size = 2; + Contents = FirstSec->getContents(); + ASSERT_THAT_EXPECTED(Contents, Succeeded()); + EXPECT_EQ(*Contents, + StringRef(reinterpret_cast(Data.data()), 2)); + + // Check that getSize properly uses the header's sh_size value. + EXPECT_EQ(FirstSec->getSize(), 2UL); + + // Check that Contents has size 2 because header's sh_size was changed. + EXPECT_EQ(Contents->size(), 2UL); + + // Make sure a section wasn't added. + ptrdiff_t NewNumSections = + std::distance(MutableObject.section_begin(), MutableObject.section_end()); + EXPECT_EQ(NewNumSections, NumSections); +} + +// Test that when no modifications have been made SectionRef's methods are +// the same between ELFObjectFile and MutableELFObject. +TEST(MutableELFObject, NoChange) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Content: "DEADBEEF")"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(*ELFObjFile); + const ObjectFile &ObjFile = *ErrOrObj->get(); + + ptrdiff_t ObjFileSecs = + std::distance(ELFObjFile->section_begin(), ELFObjFile->section_end()); + ptrdiff_t MutObjSecs = + std::distance(MutableObject.section_begin(), MutableObject.section_end()); + EXPECT_EQ(ObjFileSecs, MutObjSecs); + + auto TestSections = [](SectionRef ObjFile, SectionRef MutObj) { + EXPECT_EQ(ObjFile.getAddress(), MutObj.getAddress()); + EXPECT_EQ(ObjFile.getAlignment(), MutObj.getAlignment()); + EXPECT_EQ(ObjFile.getIndex(), MutObj.getIndex()); + EXPECT_EQ(ObjFile.getSize(), MutObj.getSize()); + EXPECT_EQ(ObjFile.isBerkeleyData(), MutObj.isBerkeleyData()); + EXPECT_EQ(ObjFile.isBerkeleyText(), MutObj.isBerkeleyText()); + EXPECT_EQ(ObjFile.isBitcode(), MutObj.isBitcode()); + EXPECT_EQ(ObjFile.isBSS(), MutObj.isBSS()); + EXPECT_EQ(ObjFile.isCompressed(), MutObj.isCompressed()); + EXPECT_EQ(ObjFile.isData(), MutObj.isData()); + EXPECT_EQ(ObjFile.isStripped(), MutObj.isStripped()); + EXPECT_EQ(ObjFile.isText(), MutObj.isText()); + EXPECT_EQ(ObjFile.isVirtual(), MutObj.isVirtual()); + }; + + for (const auto &Tuple : zip(MutableObject.sections(), ObjFile.sections())) + TestSections(std::get<0>(Tuple), std::get<1>(Tuple)); + + // Copy every section header but make no changes. SectionRefs now point to + // section headers outside of the file's mapping. + for (auto Iter = MutableObject.section_begin(), + End = MutableObject.section_end(); + Iter != End; ++Iter) + MutableObject.getMutableSection(Iter); + + for (const auto &Tuple : zip(MutableObject.sections(), ObjFile.sections())) + TestSections(std::get<0>(Tuple), std::get<1>(Tuple)); +}