Index: llvm/include/llvm/Object/MutableELFObject.h =================================================================== --- llvm/include/llvm/Object/MutableELFObject.h +++ llvm/include/llvm/Object/MutableELFObject.h @@ -90,6 +90,9 @@ } operator const Elf_Phdr &() const { return Header; } + + std::set::iterator begin() const { return Sections.begin(); } + std::set::iterator end() const { return Sections.end(); } }; template class MutableELFObject : public ELFObjectFile { @@ -469,6 +472,11 @@ } Elf_Ehdr &getHeader() { return FileHeader; } + + const Elf_Shdr &getSectionHeader(SectionRef Sec) const { + return *getSection(Sec.getRawDataRefImpl()); + } + }; template Index: llvm/tools/llvm-objcopy/ELF/Object.h =================================================================== --- llvm/tools/llvm-objcopy/ELF/Object.h +++ llvm/tools/llvm-objcopy/ELF/Object.h @@ -17,6 +17,7 @@ #include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/MutableELFObject.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileOutputBuffer.h" #include @@ -1058,6 +1059,212 @@ } }; +using namespace object; + +DataRefImpl toDataRef(uint64_t P) { + DataRefImpl DRI; + DRI.p = P; + return DRI; +} + +template class MutableObjectWriter { + object::MutableELFObject &MutableObject; + + StringTableBuilder ShstrBuilder; + object::MutableELFSection *Shstrtab = nullptr; + StringTableBuilder SymStrBuilder; + object::MutableELFSection *SymStrtab = nullptr; + + size_t CurrentOffset = sizeof(typename ELFT::Ehdr); + + std::set SectionsInSegments; + + Error init(); + Error layoutSegmentsAndSections(); + Error layoutSection(uint64_t Section); + Error layoutOtherSections(); + +public: + MutableObjectWriter(object::MutableELFObject &Object) + : MutableObject(Object), ShstrBuilder(StringTableBuilder::ELF), + SymStrBuilder(StringTableBuilder::ELF) {} + ~MutableObjectWriter() {} + + Error finalize(); + Error write(StringRef Filename); +}; + +template Error MutableObjectWriter::init() { + Expected &> ShstrtabOrErr = + MutableObject.getMutableSection(SectionRef( + toDataRef(MutableObject.getHeader().e_shstrndx), &MutableObject)); + if (!ShstrtabOrErr) + return ShstrtabOrErr.takeError(); + Shstrtab = &ShstrtabOrErr.get(); + assert(Shstrtab->Header.sh_type == ELF::SHT_STRTAB); + + section_iterator Sec = + llvm::find_if(MutableObject.sections(), [](SectionRef Sec) { + return ELFSectionRef(Sec).getType() == ELF::SHT_SYMTAB; + }); + + if (Sec != MutableObject.section_end()) { + uint64_t Link = MutableObject.getSectionHeader(*Sec).sh_link; + Expected &> SymStrtabOrErr = + MutableObject.getMutableSection( + SectionRef(toDataRef(Link), &MutableObject)); + if (!SymStrtabOrErr) + return SymStrtabOrErr.takeError(); + SymStrtab = &SymStrtabOrErr.get(); + } + + MutableObject.getHeader().e_shnum = + std::distance(MutableObject.section_begin(), MutableObject.section_end()); + MutableObject.getHeader().e_phnum = + std::distance(MutableObject.segment_begin(), MutableObject.segment_end()); + MutableObject.getHeader().e_phoff = CurrentOffset; + CurrentOffset += + sizeof(typename ELFT::Phdr) * MutableObject.getHeader().e_phnum; + MutableObject.getHeader().e_shoff = CurrentOffset; + CurrentOffset += + sizeof(typename ELFT::Shdr) * MutableObject.getHeader().e_shnum; + + for (SectionRef Sec : MutableObject.sections()) { + StringRef Name; + if (std::error_code EC = Sec.getName(Name)) + return createStringError(EC, "couldn't get section name"); + ShstrBuilder.add(Name); + } + ShstrBuilder.finalize(); + Shstrtab->Data = OwningArrayRef(ShstrBuilder.getSize()); + ShstrBuilder.write(Shstrtab->Data.data()); + + for (BasicSymbolRef BSR : MutableObject.symbols()) { + ELFSymbolRef Sym(BSR); + Expected NameOrErr = Sym.getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + SymStrBuilder.add(*NameOrErr); + } + SymStrBuilder.finalize(); + SymStrtab->Data = OwningArrayRef(SymStrBuilder.getSize()); + SymStrBuilder.write(SymStrtab->Data.data()); + + return Error::success(); +} + +template +Error MutableObjectWriter::layoutSection(uint64_t Section) { + Expected &> SecOrErr = + MutableObject.getMutableSection( + SectionRef(toDataRef(Section), &MutableObject)); + if (!SecOrErr) + return SecOrErr.takeError(); + MutableELFSection &Sec = *SecOrErr; + CurrentOffset = alignTo( + CurrentOffset, Sec.Header.sh_addralign ? Sec.Header.sh_addralign : 1); + Sec.Header.sh_offset = CurrentOffset; + Sec.Header.sh_name = 0; // ShstrBuilder.getOffset(StringRef(Sec.Name)); + if (Sec.Header.sh_type != ELF::SHT_NOBITS) + CurrentOffset += Sec.Header.sh_size; + return Error::success(); +} + +template +Error MutableObjectWriter::layoutSegmentsAndSections() { + for (auto Seg : MutableObject.segments()) { + Expected &> SegmentOrErr = + MutableObject.getMutableSegment(Seg); + if (!SegmentOrErr) + return SegmentOrErr.takeError(); + MutableELFSegment &Segment = *SegmentOrErr; + Segment.Header.p_offset = CurrentOffset; + + for (uint64_t Section : Segment) { + assert(SectionsInSegments.find(Section) == SectionsInSegments.end() && + "Section in multiple segments"); + SectionsInSegments.insert(Section); + if (Error E = layoutSection(Section)) + return E; + } + CurrentOffset = alignTo( + CurrentOffset, Segment.Header.p_align ? Segment.Header.p_align : 1); + Segment.Header.p_filesz = Segment.Header.p_offset - CurrentOffset; + } + + return Error::success(); +} + +template +Error MutableObjectWriter::layoutOtherSections() { + // Don't layout the first section. + for (uint64_t Sec : + seq(1, std::distance(MutableObject.section_begin(), + MutableObject.section_end()))) + if (SectionsInSegments.find(Sec) == SectionsInSegments.end()) + if (Error E = layoutSection(Sec)) + return E; + + return Error::success(); +} + +template Error MutableObjectWriter::finalize() { + if (Error E = init()) + return E; + + CurrentOffset += + MutableObject.getHeader().e_shnum * sizeof(typename ELFT::Phdr); + + if (Error E = layoutSegmentsAndSections()) + return E; + + if (Error E = layoutOtherSections()) + return E; + + return Error::success(); +} + +template +Error MutableObjectWriter::write(StringRef Filename) { + Expected> BufferOrErr = + FileOutputBuffer::create(Filename, CurrentOffset, + FileOutputBuffer::F_executable); + if (!BufferOrErr) + return BufferOrErr.takeError(); + + std::unique_ptr Buf = std::move(*BufferOrErr); + uint8_t *const Start = reinterpret_cast(Buf->getBufferStart()); + uint8_t *Current = Start; + + auto WriteToBuf = [&Current](void *Src, size_t Size) { + memcpy(Current, Src, Size); + Current += Size; + }; + + WriteToBuf(&MutableObject.getHeader(), sizeof(typename ELFT::Ehdr)); + + for (auto Phdr : MutableObject.segments()) { + auto PhdrOrErr = MutableObject.getMutableSegment(Phdr); + if (!PhdrOrErr) + return PhdrOrErr.takeError(); + WriteToBuf(&PhdrOrErr->Header, sizeof(typename ELFT::Phdr)); + } + + for (auto Sec : MutableObject.sections()) { + auto SecOrErr = MutableObject.getMutableSection(Sec); + if (!SecOrErr) + return SecOrErr.takeError(); + WriteToBuf(&SecOrErr->Header, sizeof(typename ELFT::Shdr)); + memcpy(Start + SecOrErr->Header.sh_offset, SecOrErr->Data.data(), + SecOrErr->Header.sh_size); + } + + if (Error E = Buf->commit()) + return E; + + return Error::success(); +} + } // end namespace elf } // end namespace objcopy } // end namespace llvm Index: llvm/unittests/tools/CMakeLists.txt =================================================================== --- llvm/unittests/tools/CMakeLists.txt +++ llvm/unittests/tools/CMakeLists.txt @@ -5,6 +5,7 @@ endif() add_subdirectory( + llvm-objcopy llvm-exegesis ) Index: llvm/unittests/tools/llvm-objcopy/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/unittests/tools/llvm-objcopy/CMakeLists.txt @@ -0,0 +1,16 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/tools/llvm-objcopy + ) + +set(LLVM_LINK_COMPONENTS + MC + Object + ObjectYaml + Support + ) + +add_llvm_unittest(LLVMObjcopyTests + MutableObjectWriterTest.cpp + ) + +target_link_libraries(LLVMObjcopyTests PRIVATE LLVMTestingSupport) Index: llvm/unittests/tools/llvm-objcopy/MutableObjectWriterTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/tools/llvm-objcopy/MutableObjectWriterTest.cpp @@ -0,0 +1,52 @@ + +#include "ELF/Object.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Object/MutableELFObject.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace object; +using namespace objcopy; + +TEST(MutableObjectWriter, Test) { + SmallString<0> Storage; + Expected> ErrOrObj = + yaml::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 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + Sections: + - Section: .sec0 + - Section: .sec1 + - Section: .sec2)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + ASSERT_TRUE(ELFObjFile); + MutableELFObject MutableObject(std::move(*ELFObjFile)); + + elf::MutableObjectWriter Writer(MutableObject); + Error E = Writer.finalize(); + ASSERT_THAT_ERROR(std::move(E), Succeeded()); + E = Writer.write("test.tmp"); + ASSERT_THAT_ERROR(std::move(E), Succeeded()); +}