Index: llvm/include/llvm/Object/MutableELFObject.h =================================================================== --- llvm/include/llvm/Object/MutableELFObject.h +++ llvm/include/llvm/Object/MutableELFObject.h @@ -10,22 +10,24 @@ #define LLVM_OBJECT_MUTABLEELFOBJECT_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Sequence.h" #include "llvm/Object/ELFObjectFile.h" +#include namespace llvm { namespace object { +struct MappingType { + uintptr_t Ptr; + bool New; + + MappingType(bool New, uintptr_t Ptr) : Ptr(Ptr), New(New) {} +}; + // 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; @@ -49,6 +51,10 @@ }); } + MutableRange(function_ref &)> MappingCreator) { + MappingCreator(Mappings); + } + MappingType operator[](uint64_t Index) const { return Mappings[Index]; } size_t size() const { return Mappings.size(); } @@ -78,6 +84,59 @@ return Ref; } +template +static bool sectionWithinSegment(const Elf_Shdr_Impl &Sec, + const Elf_Phdr_Impl &Seg) { + uint64_t SecSize = Sec.sh_size ? Sec.sh_size : 1; + + if (Sec.sh_type == ELF::SHT_NOBITS) { + if (!(Sec.sh_flags & ELF::SHF_ALLOC)) + return false; + + bool SectionIsTLS = Sec.sh_flags & ELF::SHF_TLS; + bool SegmentIsTLS = Seg.p_type == ELF::PT_TLS; + if (SectionIsTLS != SegmentIsTLS) + return false; + + return Seg.p_vaddr <= Sec.sh_addr && + Seg.p_vaddr + Seg.p_memsz >= Sec.sh_addr + SecSize; + } + + return Seg.p_offset <= Sec.sh_offset && + Seg.p_offset + Seg.p_filesz >= Sec.sh_offset + SecSize; +} + +template class SegmentRef { + uint64_t Index; + MutableELFObject *OwningObject; + +public: + SegmentRef(uintptr_t Index, MutableELFObject *OwningObject) + : Index(Index), OwningObject(OwningObject) {} + + bool operator==(const SegmentRef &Other) const; + bool operator!=(const SegmentRef &Other) const; + + void moveNext(); + + uint64_t getIndex() const { return Index; } + const MutableELFObject *getObject() const { return OwningObject; } + + uint64_t getType() const; + uint64_t getOffset() const; + uint64_t getVirtualAddress() const; + uint64_t getPhysicalAddress() const; + uint64_t getFileSize() const; + uint64_t getMemSize() const; + uint64_t getFlags() const; + uint64_t getAlign() const; + + const Elf_Phdr_Impl &getHeader() const; + + bool hasSection(section_iterator Sec) const; + bool hasSection(uint64_t Sec) const; +}; + template struct MutableELFSection { Elf_Shdr_Impl Header; std::string Name; @@ -95,14 +154,70 @@ } }; +template class MutableELFSegment { + std::set Sections; + const MutableELFObject *ObjFile; + +public: + Elf_Phdr_Impl Header; + + MutableELFSegment(uintptr_t ToCopy, const MutableELFObject *ObjFile) + : ObjFile(ObjFile), + Header(*reinterpret_cast *>(ToCopy)) { + for (const auto &Sec : ObjFile->sections()) + if (sectionWithinSegment(ObjFile->getSectionHeader(Sec), Header)) + Sections.insert(Sec.getIndex()); + } + + void addSection(section_iterator Sec) { + Sections.insert(Sec->getRawDataRefImpl().p); + } + + void removeSection(section_iterator Sec) { + uintptr_t Index = Sec->getRawDataRefImpl().p; + assert(Sections.find(Index) != Sections.end() && + "Removing section not previously in segment"); + Sections.erase(Index); + } + + bool hasSection(section_iterator Sec) const { + return Sections.find(Sec->getRawDataRefImpl().p) != Sections.end(); + } + + bool hasSection(uint64_t Sec) const { + return Sections.find(Sec) != Sections.end(); + } + + class iterator : public std::set::iterator { + using Base = std::set::iterator; + const MutableELFObject *ObjFile; + + public: + iterator(Base B, const MutableELFObject *ObjFile) + : Base(B), ObjFile(ObjFile) {} + + SectionRef operator*() const { + return SectionRef(toDataRef(Base::operator*()), ObjFile); + } + }; + + iterator begin() { return iterator(Sections.begin(), ObjFile); } + + iterator end() { return iterator(Sections.end(), ObjFile); } +}; + template class MutableELFObject : public ELFObjectFile { friend struct MutableELFSection; + friend class MutableELFSegment; + friend class SegmentRef; MutableRange> Sections; + MutableRange> Segments; protected: - using MappingType = - typename MutableRange>::MappingType; + const Elf_Ehdr_Impl &header() { + return *reinterpret_cast *>(this->base()); + } // Returns DataRef with pointer to the correct section header. DataRefImpl getSectionRef(DataRefImpl Sec) const { @@ -112,6 +227,9 @@ : toDataRef(Mapping.Ptr); } + const Elf_Shdr_Impl &getSectionHeader(uint64_t Index) const; + const Elf_Shdr_Impl &getSectionHeader(section_iterator Sec) const; + void moveSectionNext(DataRefImpl &Sec) const override; Expected getSectionName(DataRefImpl Sec) const override; uint64_t getSectionAddress(DataRefImpl Sec) const override; @@ -128,11 +246,37 @@ bool isBerkeleyText(DataRefImpl Sec) const override; bool isBerkeleyData(DataRefImpl Sec) const override; + uint64_t getSegmentType(uint64_t Index) const; + uint64_t getSegmentOffset(uint64_t Index) const; + uint64_t getSegmentVirtualAddress(uint64_t Index) const; + uint64_t getSegmentPhysicalAddress(uint64_t Index) const; + uint64_t getSegmentFileSize(uint64_t Index) const; + uint64_t getSegmentMemSize(uint64_t Index) const; + uint64_t getSegmentFlags(uint64_t Index) const; + uint64_t getSegmentAlign(uint64_t Index) const; + const Elf_Phdr_Impl &getSegmentHeader(uint64_t Index) const; + Elf_Phdr_Impl &getSegmentHeader(uint64_t Index); + bool segmentHasSection(uint64_t Segment, uint64_t Section) const; + public: - MutableELFObject(ELFObjectFile &B) + explicit MutableELFObject(ELFObjectFile &B) : ELFObjectFile(std::move(B)), Sections(B.section_begin(), B.section_end(), - [&](SectionRef Ref) { return Ref.getRawDataRefImpl().p; }) {} + [&](SectionRef Ref) { return Ref.getRawDataRefImpl().p; }), + Segments([this](std::vector &Vec) { + const Elf_Ehdr_Impl &Header = + *reinterpret_cast *>(this->base()); + const Elf_Phdr_Impl *Phdrs = + reinterpret_cast *>(this->base() + + Header.e_phoff); + auto Sequence = seq(0ULL, (uint64_t)Header.e_phnum); + std::transform(Sequence.begin(), Sequence.end(), + std::back_inserter(Vec), [Phdrs](int Index) { + return MappingType( + /*New=*/false, + reinterpret_cast(Phdrs + Index)); + }); + }) {} section_iterator section_begin() const override { return section_iterator(SectionRef(toDataRef(0), this)); @@ -142,12 +286,105 @@ return section_iterator(SectionRef(toDataRef(Sections.size()), this)); } + section_iterator findSectionByName(StringRef Name) const; + MutableELFSection *getMutableSection(section_iterator Sec) { uintptr_t Index = Sec->getRawDataRefImpl().p; return Sections.makeMutable(Index, this); } + + using segment_iterator = content_iterator>; + + segment_iterator segment_begin() { + return segment_iterator(SegmentRef(0, this)); + } + + segment_iterator segment_end() { + return segment_iterator(SegmentRef(header().e_phnum, this)); + } + + iterator_range segments() { + return iterator_range(segment_begin(), segment_end()); + } + + MutableELFSegment *getMutableSegment(segment_iterator Seg) { + return Segments.makeMutable(Seg->getIndex(), this); + } }; +template void SegmentRef::moveNext() { ++Index; } + +template +bool SegmentRef::operator==(const SegmentRef &Other) const { + return Index == Other.Index && OwningObject == Other.OwningObject; +} + +template +bool SegmentRef::operator!=(const SegmentRef &Other) const { + return Index != Other.Index || OwningObject != Other.OwningObject; +} + +template uint64_t SegmentRef::getType() const { + return OwningObject->getSegmentType(Index); +} + +template uint64_t SegmentRef::getOffset() const { + return OwningObject->getSegmentOffset(Index); +} + +template uint64_t SegmentRef::getVirtualAddress() const { + return OwningObject->getSegmentVirtualAddress(Index); +} + +template uint64_t SegmentRef::getPhysicalAddress() const { + return OwningObject->getSegmentPhysicalAddress(Index); +} + +template uint64_t SegmentRef::getFileSize() const { + return OwningObject->getSegmentFileSize(Index); +} + +template uint64_t SegmentRef::getMemSize() const { + return OwningObject->getSegmentMemSize(Index); +} + +template uint64_t SegmentRef::getFlags() const { + return OwningObject->getSegmentFlags(Index); +} + +template uint64_t SegmentRef::getAlign() const { + return OwningObject->getSegmentAlign(Index); +} + +template +const Elf_Phdr_Impl &SegmentRef::getHeader() const { + return OwningObject->getSegmentHeader(Index); +} + +template +bool SegmentRef::hasSection(section_iterator Sec) const { + return OwningObject->segmentHasSection(Index, Sec->getRawDataRefImpl().p); +} + +template bool SegmentRef::hasSection(uint64_t Sec) const { + return OwningObject->segmentHasSection(Index, Sec); +} + +template +const Elf_Shdr_Impl & +MutableELFObject::getSectionHeader(uint64_t Index) const { + MappingType Mapping = Sections[Index]; + if (Mapping.New) + return Sections.getNew(Mapping.Ptr)->Header; + return *this->getSection(toDataRef(Mapping.Ptr)); +} + +template +const Elf_Shdr_Impl & +MutableELFObject::getSectionHeader(section_iterator Sec) const { + return getSectionHeader(Sec->getRawDataRefImpl().p); +} + template void MutableELFObject::moveSectionNext(DataRefImpl &Sec) const { ++Sec.p; @@ -230,6 +467,81 @@ return ELFObjectFile::isBerkeleyData(getSectionRef(Sec)); } +template +uint64_t MutableELFObject::getSegmentType(uint64_t Index) const { + return getSegmentHeader(Index).p_type; +} + +template +uint64_t MutableELFObject::getSegmentFlags(uint64_t Index) const { + return getSegmentHeader(Index).p_flags; +} + +template +uint64_t MutableELFObject::getSegmentOffset(uint64_t Index) const { + return getSegmentHeader(Index).p_offset; +} + +template +uint64_t +MutableELFObject::getSegmentVirtualAddress(uint64_t Index) const { + return getSegmentHeader(Index).p_vaddr; +} + +template +uint64_t +MutableELFObject::getSegmentPhysicalAddress(uint64_t Index) const { + return getSegmentHeader(Index).p_paddr; +} + +template +uint64_t MutableELFObject::getSegmentFileSize(uint64_t Index) const { + return getSegmentHeader(Index).p_filesz; +} + +template +uint64_t MutableELFObject::getSegmentMemSize(uint64_t Index) const { + return getSegmentHeader(Index).p_memsz; +} + +template +uint64_t MutableELFObject::getSegmentAlign(uint64_t Index) const { + return getSegmentHeader(Index).p_align; +} + +template +bool MutableELFObject::segmentHasSection(uint64_t Segment, + uint64_t Section) const { + MappingType Mapping = Segments[Segment]; + if (Mapping.New) + return Segments.getNew(Mapping.Ptr)->hasSection(Section); + return sectionWithinSegment(getSectionHeader(Section), + getSegmentHeader(Segment)); +} + +template +const Elf_Phdr_Impl & +MutableELFObject::getSegmentHeader(uint64_t Index) const { + MappingType Mapping = Segments[Index]; + if (Mapping.New) + return Segments.getNew(Index)->Header; + return *reinterpret_cast *>(Mapping.Ptr); +} + +template +section_iterator +MutableELFObject::findSectionByName(StringRef Name) const { + const section_iterator End = section_end(); + for (section_iterator Iter = section_begin(); Iter != End; ++Iter) { + StringRef SecName; + if (Iter->getName(SecName)) + return section_end(); + if (SecName == Name) + return Iter; + } + return section_end(); +} + } // namespace object } // namespace llvm Index: llvm/unittests/Object/MutableELFObjectTest.cpp =================================================================== --- llvm/unittests/Object/MutableELFObjectTest.cpp +++ llvm/unittests/Object/MutableELFObjectTest.cpp @@ -18,6 +18,14 @@ using namespace object; using namespace yaml; +template static ptrdiff_t iterRangeLength(const T &Range) { + return std::distance(Range.begin(), Range.end()); +} + +template static ptrdiff_t iterRangeLength(T &Range) { + return std::distance(Range.begin(), Range.end()); +} + // Change a sections name and test that SectionRef::getName() returns the new // name. TEST(MutableELFObject, ChangeSectionName) { @@ -96,7 +104,7 @@ MutableELFObject MutableObject(*ELFObjFile); ptrdiff_t NumSections = - std::distance(MutableObject.section_begin(), MutableObject.section_end()); + std::distance(MutableObject.section_begin(), MutableObject.section_end()); auto FirstSec = ++MutableObject.section_begin(); Expected Contents = FirstSec->getContents(); @@ -159,9 +167,9 @@ const ObjectFile &ObjFile = *ErrOrObj->get(); ptrdiff_t ObjFileSecs = - std::distance(ELFObjFile->section_begin(), ELFObjFile->section_end()); + std::distance(ELFObjFile->section_begin(), ELFObjFile->section_end()); ptrdiff_t MutObjSecs = - std::distance(MutableObject.section_begin(), MutableObject.section_end()); + std::distance(MutableObject.section_begin(), MutableObject.section_end()); EXPECT_EQ(ObjFileSecs, MutObjSecs); auto TestSections = [](SectionRef ObjFile, SectionRef MutObj) { @@ -180,8 +188,185 @@ EXPECT_EQ_ITER(isStripped); EXPECT_EQ_ITER(isText); EXPECT_EQ_ITER(isVirtual); +#undef EXPECT_EQ_ITER }; for (const auto &Tuple : zip(MutableObject.sections(), ObjFile.sections())) TestSections(std::get<0>(Tuple), std::get<1>(Tuple)); } + +// Test findSectionByName method +TEST(MutableELFObject, FindSectionByName) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x1000 + Size: 24)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(*ELFObjFile); + + section_iterator Section1 = ++MutableObject.section_begin(); + section_iterator FoundSection = MutableObject.findSectionByName(".text"); + EXPECT_EQ(Section1, FoundSection); + + section_iterator DoesntExist = + MutableObject.findSectionByName("doesnt exist"); + EXPECT_EQ(DoesntExist, MutableObject.section_end()); +} + +// Test MutableELFObject constructor properly reads program headers. +TEST(MutableELFObject, ProgramHeaders) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + PAddr: 0x2000)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(*ELFObjFile); + + auto FirstSegment = MutableObject.segment_begin(); + EXPECT_EQ(FirstSegment->getType(), ELF::PT_LOAD); + EXPECT_TRUE(FirstSegment->getFlags() & (ELF::PF_X | ELF::PF_R)); + EXPECT_FALSE(FirstSegment->getFlags() & ELF::PF_W); + EXPECT_EQ(FirstSegment->getVirtualAddress(), 0x1000U); + EXPECT_EQ(FirstSegment->getPhysicalAddress(), 0x2000U); + + EXPECT_EQ(iterRangeLength(MutableObject.segments()), 1); +} + +// Test +TEST(MutableELFObject, SectionWithinSegment) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Size: 24 + - Name: .data + Flags: [ SHF_ALLOC ] + Type: SHT_PROGBITS + Size: 4064 + Content: "DEADBEEF" + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_ALLOC ] +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + Sections: + - Section: .text + - Type: PT_LOAD + Flags: [ PF_X, PF_W, PF_R ] + VAddr: 0x2000 + Sections: + - Section: .data)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(*ELFObjFile); + + using segment_iterator = MutableELFObject::segment_iterator; + + EXPECT_EQ(iterRangeLength(MutableObject.segments()), 2); + segment_iterator FirstSegment = MutableObject.segment_begin(); + segment_iterator SecondSegment = ++MutableObject.segment_begin(); + + section_iterator End = MutableObject.section_end(); + section_iterator TextSection = MutableObject.findSectionByName(".text"); + EXPECT_NE(TextSection, End); + section_iterator DataSection = MutableObject.findSectionByName(".data"); + EXPECT_NE(DataSection, End); + section_iterator BssSection = MutableObject.findSectionByName(".bss"); + EXPECT_NE(BssSection, End); + EXPECT_TRUE(FirstSegment->hasSection(TextSection)); + EXPECT_FALSE(FirstSegment->hasSection(DataSection)); + EXPECT_FALSE(FirstSegment->hasSection(BssSection)); + + EXPECT_FALSE(SecondSegment->hasSection(TextSection)); + EXPECT_TRUE(SecondSegment->hasSection(DataSection)); + EXPECT_FALSE(SecondSegment->hasSection(BssSection)); +} + +// Test adding and removing sections from segments +TEST(MutableELFObject, AddRemoveSections) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Size: 24 + - Name: .data + Flags: [ SHF_ALLOC ] + Type: SHT_PROGBITS + Size: 4064 + Content: "DEADBEEF" + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_ALLOC ] +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + Sections: + - Section: .text + - Type: PT_LOAD + Flags: [ PF_X, PF_W, PF_R ] + VAddr: 0x2000 + Sections: + - Section: .data)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(*ELFObjFile); + + using segment_iterator = MutableELFObject::segment_iterator; + + segment_iterator SecondSegment = ++MutableObject.segment_begin(); + section_iterator DataSection = MutableObject.findSectionByName(".data"); + section_iterator BssSection = MutableObject.findSectionByName(".bss"); + auto *MutableSegment = MutableObject.getMutableSegment(SecondSegment); + EXPECT_TRUE(MutableSegment->hasSection(DataSection)); + MutableSegment->addSection(BssSection); + EXPECT_TRUE(MutableSegment->hasSection(BssSection)); + EXPECT_TRUE(SecondSegment->hasSection(BssSection)); +}