Index: llvm/include/llvm/Object/ELFObjectFile.h =================================================================== --- llvm/include/llvm/Object/ELFObjectFile.h +++ llvm/include/llvm/Object/ELFObjectFile.h @@ -286,6 +286,8 @@ std::vector dynamic_relocation_sections() const override; section_iterator getRelocatedSection(DataRefImpl Sec) const override; + static bool isBerkeleyText(const Elf_Shdr *Shdr); + void moveRelocationNext(DataRefImpl &Rel) const override; uint64_t getRelocationOffset(DataRefImpl Rel) const override; symbol_iterator getRelocationSymbol(DataRefImpl Rel) const override; @@ -788,17 +790,22 @@ return getSection(Sec)->sh_type == ELF::SHT_NOBITS; } +template +bool ELFObjectFile::isBerkeleyText(const Elf_Shdr *Shdr) { + return Shdr->sh_flags & ELF::SHF_ALLOC && + (Shdr->sh_flags & ELF::SHF_EXECINSTR || + !(Shdr->sh_flags & ELF::SHF_WRITE)); +} + template bool ELFObjectFile::isBerkeleyText(DataRefImpl Sec) const { - return getSection(Sec)->sh_flags & ELF::SHF_ALLOC && - (getSection(Sec)->sh_flags & ELF::SHF_EXECINSTR || - !(getSection(Sec)->sh_flags & ELF::SHF_WRITE)); + return isBerkeleyText(getSection(Sec)); } template bool ELFObjectFile::isBerkeleyData(DataRefImpl Sec) const { const Elf_Shdr *EShdr = getSection(Sec); - return !isBerkeleyText(Sec) && EShdr->sh_type != ELF::SHT_NOBITS && + return !isBerkeleyText(EShdr) && EShdr->sh_type != ELF::SHT_NOBITS && EShdr->sh_flags & ELF::SHF_ALLOC; } Index: llvm/include/llvm/Object/MutableELFObject.h =================================================================== --- /dev/null +++ llvm/include/llvm/Object/MutableELFObject.h @@ -0,0 +1,183 @@ +//===-- 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" +#include "llvm/Object/MutableObject.h" + +namespace llvm { +namespace object { + +template class MutableELFObject; + +static inline DataRefImpl toDataRef(uintptr_t Ptr) { + DataRefImpl Ref; + Ref.p = Ptr; + return Ref; +} + +template class MutableELFSection { +public: + Elf_Shdr_Impl Header; + std::string Name; + OwningArrayRef Data; + + MutableELFSection(uintptr_t ToCopy, const MutableELFObject *ObjFile) + : Header(*ObjFile->getSection(toDataRef(ToCopy))), + Data(OwningArrayRef(Header.sh_size)) { + ::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 class MutableELFSection; + + MutableRange> Sections; + +protected: + using MappingType = + typename MutableRange>::MappingType; + + // Returns DataRef with pointer to the correct section header. + DataRefImpl getSectionRef(DataRefImpl Sec) const { + MappingType Mapping = Sections[Sec.p]; + return Mapping.New ? toDataRef(reinterpret_cast( + &Sections.getNew(Mapping.Ptr)->Header)) + : toDataRef(Mapping.Ptr); + } + + void moveSectionNext(DataRefImpl &Sec) const override; + Expected getSectionName(DataRefImpl Sec) const override; + uint64_t getSectionAddress(DataRefImpl Sec) const override; + uint64_t getSectionIndex(DataRefImpl Sec) const override; + uint64_t getSectionSize(DataRefImpl Sec) const override; + Expected> + getSectionContents(DataRefImpl Sec) const override; + uint64_t getSectionAlignment(DataRefImpl Sec) const override; + bool isSectionCompressed(DataRefImpl Sec) const override; + bool isSectionText(DataRefImpl Sec) const override; + bool isSectionData(DataRefImpl Sec) const override; + bool isSectionBSS(DataRefImpl Sec) const override; + bool isSectionVirtual(DataRefImpl Sec) const override; + bool isBerkeleyText(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.New) { + const MutableELFSection *NewSec = Sections.getNew(Mapping.Ptr); + return NewSec->Name; + } + return ELFObjectFile::getSectionName(toDataRef(Mapping.Ptr)); +} + +template +uint64_t MutableELFObject::getSectionAddress(DataRefImpl Sec) const { + return ELFObjectFile::getSectionAddress(getSectionRef(Sec)); +} + +template +uint64_t MutableELFObject::getSectionIndex(DataRefImpl Sec) const { + return Sec.p; +} + +template +uint64_t MutableELFObject::getSectionSize(DataRefImpl Sec) const { + return ELFObjectFile::getSectionSize(getSectionRef(Sec)); +} + +template +Expected> +MutableELFObject::getSectionContents(DataRefImpl Sec) const { + MappingType Mapping = Sections[Sec.p]; + if (Mapping.New) { + const MutableELFSection *NewSec = Sections.getNew(Mapping.Ptr); + return ArrayRef(NewSec->Data.data(), NewSec->Header.sh_size); + } + return ELFObjectFile::getSectionContents(toDataRef(Mapping.Ptr)); +} + +template +uint64_t MutableELFObject::getSectionAlignment(DataRefImpl Sec) const { + return ELFObjectFile::getSectionAlignment(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionCompressed(DataRefImpl Sec) const { + return ELFObjectFile::isSectionCompressed(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionText(DataRefImpl Sec) const { + return ELFObjectFile::isSectionText(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionData(DataRefImpl Sec) const { + return ELFObjectFile::isSectionData(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionBSS(DataRefImpl Sec) const { + return ELFObjectFile::isSectionBSS(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionVirtual(DataRefImpl Sec) const { + return ELFObjectFile::isSectionVirtual(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isBerkeleyText(DataRefImpl Sec) const { + return ELFObjectFile::isBerkeleyText(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isBerkeleyData(DataRefImpl Sec) const { + return ELFObjectFile::isBerkeleyData(getSectionRef(Sec)); +} + +} // namespace object +} // namespace llvm + +#endif // LLVM_OBJECT_MUTABLEELFOBJECT_H Index: llvm/include/llvm/Object/MutableObject.h =================================================================== --- /dev/null +++ llvm/include/llvm/Object/MutableObject.h @@ -0,0 +1,76 @@ +//===-- MutableObject.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_MUTABLEOBJECT_H +#define LLVM_OBJECT_MUTABLEOBJECT_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 New; + + MappingType(bool New, uintptr_t Ptr) : Ptr(Ptr), New(New) {} + }; + + 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) -> MappingType { + uintptr_t Extracted = Extract(Value); + assert(Extracted <= (UINTPTR_MAX >> 1) && + "returned type too large"); + return MappingType(false, Extracted); + }); + } + + 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 { return &NewValues[Index]; } + T *getNew(uint64_t Index) { return &NewValues[Index]; } + + template + T *makeMutable(uint64_t Index, Args &&... Arguments) { + MappingType Mapping = Mappings[Index]; + if (Mapping.New) + return &NewValues[reinterpret_cast(Mapping.Ptr)]; + NewValues.emplace_back(Mapping.Ptr, Arguments...); + Mappings[Index] = MappingType(true, NewValues.size() - 1); + return &NewValues.back(); + } +}; + +} // namespace object +} // namespace llvm + +#endif // LVM_OBJECT_MUTABLEOBJECT_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,171 @@ +//===- 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; + +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 getSectionName = [](section_iterator Iter) -> const char * { + StringRef Name; + Iter->getName(Name); + return Name.data(); + }; + + ptrdiff_t NumSections = + std::distance(MutableObject.section_begin(), MutableObject.section_end()); + + auto Iter = MutableObject.section_begin(); + EXPECT_STREQ(getSectionName(Iter), nullptr); + EXPECT_STREQ(getSectionName(++Iter), ".sec0"); + EXPECT_STREQ(getSectionName(++Iter), ".sec1"); + EXPECT_STREQ(getSectionName(++Iter), ".sec2"); + + Iter = MutableObject.section_begin(); + std::advance(Iter, 2); + auto *MutSection = MutableObject.getMutableSection(Iter); + MutSection->Name = ".new_name"; + + Iter = MutableObject.section_begin(); + EXPECT_STREQ(getSectionName(Iter), nullptr); + EXPECT_STREQ(getSectionName(++Iter), ".sec0"); + EXPECT_STREQ(getSectionName(++Iter), ".new_name"); + EXPECT_STREQ(getSectionName(++Iter), ".sec2"); + + // Make sure a section wasn't added. + ptrdiff_t MutNumSections = + std::distance(MutableObject.section_begin(), MutableObject.section_end()); + EXPECT_EQ(MutNumSections, NumSections); +} + +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); + + 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 ZeroData{'0', '0', '0', '0'}; + + auto *MutSec = MutableObject.getMutableSection(FirstSec); + MutSec->setData(ZeroData); + + FirstSec = ++MutableObject.section_begin(); + Contents = FirstSec->getContents(); + ASSERT_THAT_EXPECTED(Contents, Succeeded()); + EXPECT_EQ(*Contents, + StringRef(reinterpret_cast(ZeroData.data()), + ZeroData.size())); + + MutSec->Header.sh_size = 2; + Contents = FirstSec->getContents(); + ASSERT_THAT_EXPECTED(Contents, Succeeded()); + EXPECT_EQ(*Contents, + StringRef(reinterpret_cast(ZeroData.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); +} + +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(); + + auto TestIters = [](section_iterator ObjFile, section_iterator MutObj) { +#define EXPECT_EQ_ITER(getMethod) \ + EXPECT_EQ(ObjFile->getMethod(), MutObj->getMethod()) + EXPECT_EQ_ITER(getAddress); + EXPECT_EQ_ITER(getAlignment); + EXPECT_EQ_ITER(getIndex); + EXPECT_EQ_ITER(getSize); + EXPECT_EQ_ITER(isBerkeleyData); + EXPECT_EQ_ITER(isBerkeleyText); + EXPECT_EQ_ITER(isBitcode); + EXPECT_EQ_ITER(isBSS); + EXPECT_EQ_ITER(isCompressed); + EXPECT_EQ_ITER(isData); + EXPECT_EQ_ITER(isStripped); + EXPECT_EQ_ITER(isText); + EXPECT_EQ_ITER(isVirtual); + }; + + auto MutObjIter = MutableObject.section_begin(); + for (const auto &ObjFileIter : ObjFile.sections()) { + TestIters(ObjFileIter, MutObjIter); + ++MutObjIter; + } +}