Index: llvm/include/llvm/Object/MutableELFObject.h =================================================================== --- llvm/include/llvm/Object/MutableELFObject.h +++ llvm/include/llvm/Object/MutableELFObject.h @@ -17,6 +17,7 @@ namespace object { template class MutableELFObject; +template class MutableELFSegment; /// This class is used by MutableELFObject to provide a mutable version of an /// ELFObjectFile Section. @@ -25,10 +26,12 @@ Elf_Shdr Header; std::string Name; + ssize_t ParentIndex; OwningArrayRef Data; - MutableELFSection(const Elf_Shdr &Header, StringRef Name, void *Base) - : Header(Header), Name(Name), + MutableELFSection(const Elf_Shdr &Header, StringRef Name, ssize_t ParentIndex, + void *Base) + : Header(Header), Name(Name), ParentIndex(ParentIndex), Data(OwningArrayRef(Header.sh_size)) { std::memcpy(Data.data(), reinterpret_cast(Base) + Header.sh_offset, @@ -61,6 +64,19 @@ operator const Elf_Sym &() const { return Header; } }; +template class MutableELFSegment { + using Elf_Phdr = Elf_Phdr_Impl; + +public: + Elf_Phdr Header; + ssize_t ParentIndex; + + MutableELFSegment(Elf_Phdr Header, ssize_t ParentIndex) + : Header(Header), ParentIndex(ParentIndex) {} + + operator const Elf_Phdr &() 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. @@ -203,11 +219,13 @@ using Elf_Shdr = typename ELFT::Shdr; using Elf_Ehdr = typename ELFT::Ehdr; + using Elf_Phdr = typename ELFT::Phdr; using Elf_Sym = typename ELFT::Sym; using MutableSymbolTable = MutableTable>; Elf_Ehdr FileHeader; + MutableTable> Segments; MutableTable> Sections; MutableSymbolTable Symbols; MutableSymbolTable DynSymbols; @@ -236,6 +254,16 @@ Expected> getSectionContents(DataRefImpl Sec) const override; + /// Returns -1 if the section has no parent segment, otherwise returns the + /// index of the parent. + ssize_t findSectionsParentSegment(SectionRef Sec) const; + +public: + class SegmentRef; + +private: + ssize_t findSegmentsParentSegment(SegmentRef Segment) const; + void moveSymbolNext(DataRefImpl &Sym) const override; Expected getSymbolName(DataRefImpl Sym) const override; ArrayRef findSymbolTable(uint64_t ShType) const; @@ -249,17 +277,49 @@ static DataRefImpl toDataRef(uintptr_t Ptr); static DataRefImpl toDataRef(uint32_t A, uint32_t B); + static bool segmentWithinSegment(const Elf_Phdr &Child, + const Elf_Phdr &Parent); + static bool sectionWithinSegment(const Elf_Shdr &Sec, const Elf_Phdr &Seg); public: explicit MutableELFObject(ELFObjectFile &&B) : ELFObjectFile(std::move(B)), FileHeader(*reinterpret_cast(this->base())), + Segments(ArrayRef(reinterpret_cast( + this->base() + getHeader().e_phoff), + getHeader().e_phnum)), Sections(ArrayRef(reinterpret_cast( this->base() + getHeader().e_shoff), getHeader().e_shnum)), Symbols(findSymbolTable(ELF::SHT_SYMTAB)), DynSymbols(findSymbolTable(ELF::SHT_DYNSYM)) {} + class SegmentRef { + MutableELFObject *Object; + size_t Index; + + public: + SegmentRef(MutableELFObject *Object, size_t Index) + : Object(Object), Index(Index) {} + + bool operator==(const SegmentRef &Other) const { + return Object == Other.Object && Index == Other.Index; + } + + bool operator<(const SegmentRef &Other) const { + assert(Object == Other.Object && "Not from the same object"); + return Index < Other.Index; + } + + size_t getIndex() const { return Index; } + + void moveNext() { ++Index; } + + const Elf_Phdr &getHeader() const { return Object->Segments[Index]; } + }; + + using segment_iterator = content_iterator; + section_iterator section_begin() const override { return section_iterator( SectionRef(toDataRef(Sections.getFirstIndex()), this)); @@ -295,6 +355,18 @@ this)); } + segment_iterator segment_begin() { + return segment_iterator(SegmentRef(this, 0)); + } + + segment_iterator segment_end() { + return segment_iterator(SegmentRef(this, Segments.getEndIndex())); + } + + iterator_range segments() { + return iterator_range(segment_begin(), segment_end()); + } + /// Returns a mutable reference to the symbol at the specified index. Expected &> getMutableSymbol(SymbolRef Sym) { Expected Name = getSymbolName(Sym.getRawDataRefImpl()); @@ -331,7 +403,15 @@ Expected Name = getSectionName(Sec.getRawDataRefImpl()); if (!Name) return Name.takeError(); - return Sections.makeMutable(Sec.getRawDataRefImpl().p, Header, *Name, this); + return Sections.makeMutable(Sec.getRawDataRefImpl().p, Header, *Name, + findSectionsParentSegment(Sec), this); + } + + /// Returns a mutable reference to a segment. + Expected &> getMutableSegment(SegmentRef Segment) { + const Elf_Phdr &Phdr = Segments[Segment.getIndex()]; + return Segments.makeMutable(Segment.getIndex(), Phdr, + findSegmentsParentSegment(Segment)); } void addSymbol(MutableELFSymbol Sym) { Symbols.add(std::move(Sym)); } @@ -347,6 +427,16 @@ Symbols.remove(Sym.getRawDataRefImpl().d.b); } + bool sectionInSegment(SectionRef Sec, SegmentRef Segment) const { + return findSectionsParentSegment(Sec) == + static_cast(Segment.getIndex()); + } + + bool segmentInSegment(SegmentRef First, SegmentRef Second) const { + return findSegmentsParentSegment(First) == + static_cast(Second.getIndex()); + } + /// Removes a section. void removeSection(SectionRef Sec) { assert(Sec.getRawDataRefImpl().p && "Cannot remove index 0 section"); @@ -437,6 +527,68 @@ return Ref; } +template +ssize_t +MutableELFObject::findSectionsParentSegment(SectionRef Sec) const { + uint64_t Index = Sec.getRawDataRefImpl().p; + if (const MutableELFSection *Section = Sections.getConstIfNew(Index)) + return Section->ParentIndex; + + const Elf_Shdr &Shdr = Sections[Index]; + for (size_t I = 0, E = Segments.originalSize(); I < E; ++I) { + const Elf_Phdr &Phdr = Segments.getOriginal(I); + if (sectionWithinSegment(Shdr, Phdr)) + return I; + } + return -1; +} + +template +ssize_t +MutableELFObject::findSegmentsParentSegment(SegmentRef Segment) const { + size_t Index = Segment.getIndex(); + if (const MutableELFSegment *Segment = Segments.getConstIfNew(Index)) + return Segment->ParentIndex; + + const Elf_Phdr &Child = Segments[Index]; + for (size_t I = 0, E = Segments.originalSize(); I < E; ++I) { + const Elf_Phdr &Phdr = Segments.getOriginal(I); + if (segmentWithinSegment(Child, Phdr)) + return I; + } + return -1; +} + +template +bool MutableELFObject::sectionWithinSegment( + const typename ELFT::Shdr &Sec, const typename ELFT::Phdr &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 +bool MutableELFObject::segmentWithinSegment( + const typename ELFT::Phdr &Child, const typename ELFT::Phdr &Parent) { + + return Parent.p_offset <= Child.p_offset && + Parent.p_offset + Parent.p_filesz > Child.p_offset; +} + } // namespace object } // namespace llvm Index: llvm/unittests/Object/MutableELFObjectTest.cpp =================================================================== --- llvm/unittests/Object/MutableELFObjectTest.cpp +++ llvm/unittests/Object/MutableELFObjectTest.cpp @@ -759,3 +759,160 @@ ASSERT_THAT_EXPECTED(StartAddr, Succeeded()); EXPECT_EQ(*StartAddr, 5u); } + +// 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 + - Type: 0x12345 + FileSize: 0 + MemSize: 100)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(std::move(*ELFObjFile)); + + EXPECT_EQ( + std::distance(MutableObject.segment_begin(), MutableObject.segment_end()), + 2); + + auto FirstSegment = MutableObject.segment_begin(); + EXPECT_EQ(FirstSegment->getHeader().p_type, ELF::PT_LOAD); + EXPECT_TRUE(FirstSegment->getHeader().p_flags & (ELF::PF_X | ELF::PF_R)); + EXPECT_FALSE(FirstSegment->getHeader().p_flags & ELF::PF_W); + EXPECT_EQ(FirstSegment->getHeader().p_vaddr, 0x1000U); + EXPECT_EQ(FirstSegment->getHeader().p_paddr, 0x2000U); + + auto SecondSegment = ++MutableObject.segment_begin(); + EXPECT_EQ(SecondSegment->getHeader().p_type, 0x12345u); + EXPECT_EQ(SecondSegment->getHeader().p_filesz, 0u); + EXPECT_EQ(SecondSegment->getHeader().p_memsz, 100u); +} + +// Test mutating program headers. +TEST(MutableELFObject, MutateProgramHeaders) { + 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 ] + Offset: 0x1000 + PAddr: 0x2000)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(std::move(*ELFObjFile)); + + auto FirstSegment = MutableObject.segment_begin(); + EXPECT_EQ(FirstSegment->getHeader().p_offset, 0x1000u); + Expected &> MutSegmentOrErr = + MutableObject.getMutableSegment(*FirstSegment); + ASSERT_THAT_EXPECTED(MutSegmentOrErr, Succeeded()); + MutableELFSegment &MutSegment = *MutSegmentOrErr; + MutSegment.Header.p_offset = 0x1100; + EXPECT_EQ(FirstSegment->getHeader().p_offset, 0x1100u); +} + +// Test sectionInSegment method. +TEST(MutableELFObject, SectionInSegment) { + 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(std::move(*ELFObjFile)); + + using segment_iterator = MutableELFObject::segment_iterator; + + EXPECT_EQ( + std::distance(MutableObject.segment_begin(), MutableObject.segment_end()), + 2); + segment_iterator FirstSegment = MutableObject.segment_begin(); + section_iterator TextSection = ++MutableObject.section_begin(); + section_iterator DataSection = ++(++MutableObject.section_begin()); + EXPECT_TRUE(MutableObject.sectionInSegment(*TextSection, *FirstSegment)); + EXPECT_FALSE(MutableObject.sectionInSegment(*DataSection, *FirstSegment)); +} + +// Test when a segment is in a segment. +TEST(MutableELFObject, SegmentInSegment) { + 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 ] + Offset: 0x1000 + FileSize: 0x1000 + - Type: 0 + Offset: 0x1010)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(std::move(*ELFObjFile)); + + using segment_iterator = MutableELFObject::segment_iterator; + + EXPECT_EQ( + std::distance(MutableObject.segment_begin(), MutableObject.segment_end()), + 2); + segment_iterator FirstSegment = MutableObject.segment_begin(); + segment_iterator SecondSegment = ++MutableObject.segment_begin(); + EXPECT_TRUE(MutableObject.segmentInSegment(*SecondSegment, *FirstSegment)); +}