Index: llvm/include/llvm/Object/MutableELFObject.h =================================================================== --- llvm/include/llvm/Object/MutableELFObject.h +++ llvm/include/llvm/Object/MutableELFObject.h @@ -69,10 +69,12 @@ public: Elf_Phdr Header; + ArrayRef OriginalData; Optional ParentIndex; - MutableELFSegment(Elf_Phdr Header, Optional ParentIndex) - : Header(Header), ParentIndex(ParentIndex) {} + MutableELFSegment(Elf_Phdr Header, ArrayRef OriginalData, + Optional ParentIndex) + : Header(Header), OriginalData(OriginalData), ParentIndex(ParentIndex) {} operator const Elf_Phdr &() const { return Header; } }; @@ -310,6 +312,8 @@ Symbols(findSymbolTable(ELF::SHT_SYMTAB)), DynSymbols(findSymbolTable(ELF::SHT_DYNSYM)) {} + using ELFObjectFile::base; + using segment_iterator = content_iterator; section_iterator section_begin() const override { @@ -359,6 +363,10 @@ return iterator_range(segment_begin(), segment_end()); } + const Elf_Phdr &getOriginalSegment(size_t Index) const { + return Segments.getOriginal(Index); + } + /// Returns a mutable reference to the symbol at the specified index. Expected &> getMutableSymbol(SymbolRef Sym) { Expected Name = getSymbolName(Sym.getRawDataRefImpl()); @@ -402,14 +410,18 @@ /// Returns a mutable reference to a segment. Expected &> getMutableSegment(SegmentRef Segment) { const Elf_Phdr &Phdr = Segments[Segment.getIndex()]; - return Segments.makeMutable(Segment.getIndex(), Phdr, - findParentSegment(Segment)); + return Segments.makeMutable( + Segment.getIndex(), Phdr, + ArrayRef(this->base() + Phdr.p_offset, Phdr.p_filesz), + findParentSegment(Segment)); } void addSymbol(MutableELFSymbol Sym) { Symbols.add(std::move(Sym)); } void addSection(MutableELFSection Sec) { Sections.add(std::move(Sec)); } + void addSegment(MutableELFSegment Seg) { Segments.add(std::move(Seg)); } + /// Removes a symbol. void removeSymbol(SymbolRef Sym) { assert(Sections.getOriginal(Sym.getRawDataRefImpl().d.a).sh_type == Index: llvm/test/tools/llvm-objcopy/ELF/mutable-object-no-change.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/mutable-object-no-change.test @@ -0,0 +1,19 @@ +## Test that the object file is properly copied when using +## the mutable object interface, and no switches are specified. + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .test + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + +# RUN: yaml2obj %s | llvm-objcopy --use-mutable-object - %t +# RUN: llvm-readobj --sections %t | FileCheck %s + +# CHECK: Name: .test +# CHECK: Name: .shstrtab Index: llvm/tools/llvm-objcopy/CommonOpts.td =================================================================== --- llvm/tools/llvm-objcopy/CommonOpts.td +++ llvm/tools/llvm-objcopy/CommonOpts.td @@ -83,6 +83,9 @@ def keep_file_symbols : Flag<["--"], "keep-file-symbols">, HelpText<"Do not remove file symbols">; +def use_mutable_object : Flag<["--"], "use-mutable-object">, + HelpText<"Use MutableELFObject instead of Object">; + def only_keep_debug : Flag<["--"], "only-keep-debug">, HelpText<"Clear sections that would not be stripped by --strip-debug. " Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -177,6 +177,7 @@ bool StripUnneeded = false; bool Weaken = false; bool DecompressDebugSections = false; + bool UseMutableObject = false; DebugCompressionType CompressionType = DebugCompressionType::None; }; Index: llvm/tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.cpp +++ llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -652,6 +652,7 @@ Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); Config.DecompressDebugSections = InputArgs.hasArg(OBJCOPY_decompress_debug_sections); + Config.UseMutableObject = InputArgs.hasArg(OBJCOPY_use_mutable_object); if (Config.DiscardMode == DiscardType::All) Config.StripDebug = true; for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) Index: llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h =================================================================== --- llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h +++ llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h @@ -28,7 +28,9 @@ Buffer &Out); Error executeObjcopyOnBinary(const CopyConfig &Config, object::ELFObjectFileBase &In, Buffer &Out); - +Error executeObjcopyWithMutableObject(const CopyConfig &Config, + object::ELFObjectFileBase &In, + Buffer &Out); } // end namespace elf } // end namespace objcopy } // end namespace llvm Index: llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -811,6 +811,33 @@ return Error::success(); } +template +Error handleArgsAndWrite(ELFObjectFile *Obj, Buffer &Out) { + MutableELFObject MutableObject(std::move(*Obj)); + + // TODO: handleArgs + + MutableObjectWriter Writer(MutableObject, Out); + if (Error E = Writer.finalize()) + return E; + return Writer.write(); +} + +Error executeObjcopyWithMutableObject(const CopyConfig &Config, + object::ELFObjectFileBase &In, + Buffer &Out) { + if (auto *O = dyn_cast>(&In)) + return handleArgsAndWrite(O, Out); + else if (auto *O = dyn_cast>(&In)) + return handleArgsAndWrite(O, Out); + else if (auto *O = dyn_cast>(&In)) + return handleArgsAndWrite(O, Out); + else if (auto *O = dyn_cast>(&In)) + return handleArgsAndWrite(O, Out); + + return createStringError(errc::invalid_argument, "wrong file type"); +} + } // end namespace elf } // end namespace objcopy } // end namespace llvm 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 @@ -301,15 +302,19 @@ void visit(const StringTableSection &Sec) override; }; -class Writer { +struct WriterBase { + virtual ~WriterBase() {} + virtual Error finalize() = 0; + virtual Error write() = 0; +}; + +class Writer : public WriterBase { protected: Object &Obj; Buffer &Buf; public: virtual ~Writer(); - virtual Error finalize() = 0; - virtual Error write() = 0; Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {} }; @@ -347,6 +352,31 @@ ELFWriter(Object &Obj, Buffer &Buf, bool WSH); }; +using object::MutableELFObject; +template class MutableObjectWriter : public WriterBase { + MutableELFObject &Obj; + Buffer &Buf; + + using Ehdr = typename ELFT::Ehdr; + using Phdr = typename ELFT::Phdr; + using Shdr = typename ELFT::Shdr; + using Addr = typename ELFT::Addr; + + Error assignOffsets(); + Error layoutSegments(uint64_t &Offset); + Error layoutSections(uint64_t &Offset); + + size_t totalSize(); + +public: + virtual ~MutableObjectWriter() {} + + Error finalize() override; + Error write() override; + MutableObjectWriter(MutableELFObject &Obj, Buffer &Buf) + : Obj(Obj), Buf(Buf) {} +}; + class BinaryWriter : public Writer { private: std::unique_ptr SecWriter; Index: llvm/tools/llvm-objcopy/ELF/Object.cpp =================================================================== --- llvm/tools/llvm-objcopy/ELF/Object.cpp +++ llvm/tools/llvm-objcopy/ELF/Object.cpp @@ -2307,6 +2307,158 @@ return Error::success(); } +template +Error MutableObjectWriter::layoutSegments(uint64_t &Offset) { + using SegmentRef = typename MutableELFObject::SegmentRef; + Phdr EhdrSegment; + EhdrSegment.p_type = PT_PHDR; + EhdrSegment.p_offset = 0; + EhdrSegment.p_flags = 0; + EhdrSegment.p_vaddr = 0; + EhdrSegment.p_align = 0; + EhdrSegment.p_filesz = sizeof(EhdrSegment); + EhdrSegment.p_memsz = sizeof(EhdrSegment); + Phdr PhdrSegment; + PhdrSegment.p_type = PT_PHDR; + PhdrSegment.p_flags = 0; + PhdrSegment.p_paddr = 0; + PhdrSegment.p_offset = PhdrSegment.p_vaddr = Obj.getHeader().e_phoff; + PhdrSegment.p_align = sizeof(Addr); + Obj.addSegment({EhdrSegment, + ArrayRef(Obj.base(), sizeof(Ehdr)), + None}); + Obj.addSegment({PhdrSegment, + ArrayRef(Obj.base() + Obj.getHeader().e_phoff, + Obj.getHeader().e_phnum * sizeof(Phdr)), + None}); + std::vector Segments(Obj.segment_begin(), Obj.segment_end()); + PhdrSegment.p_filesz = sizeof(Phdr) * Segments.size(); + PhdrSegment.p_memsz = sizeof(Phdr) * Segments.size(); + llvm::stable_sort(Segments, [](auto First, auto Second) { + return First.getHeader().p_offset < Second.getHeader().p_offset; + }); + + size_t Index = 0; + for (SegmentRef Segment : Obj.segments()) { + Optional ParentIndex = Obj.findParentSegment(Segment); + auto MutPhdrOrErr = Obj.getMutableSegment(Segment); + if (!MutPhdrOrErr) + return MutPhdrOrErr.takeError(); + if (ParentIndex == None) { + Offset = alignToAddr(Offset, Segment.getHeader().p_vaddr, + Segment.getHeader().p_align); + MutPhdrOrErr->Header.p_offset = Offset; + } else { + auto ParentSegment = Obj.segment_begin(); + std::advance(ParentSegment, *ParentIndex); + MutPhdrOrErr->Header.p_offset = + ParentSegment->getHeader().p_offset + + Obj.getOriginalSegment(Index).p_offset - + Obj.getOriginalSegment(*ParentIndex).p_offset; + } + Offset = std::max(Offset, MutPhdrOrErr->Header.p_offset + + MutPhdrOrErr->Header.p_filesz); + ++Index; + } + + return Error::success(); +} + +template +Error MutableObjectWriter::layoutSections(uint64_t &Offset) { + // Skip null section. + iterator_range Sections(++Obj.section_begin(), + Obj.section_end()); + for (const SectionRef Sec : Sections) { + Expected &> SecOrErr = Obj.getMutableSection(Sec); + if (!SecOrErr) + return SecOrErr.takeError(); + MutableELFSection &Section = *SecOrErr; + if (Section.ParentIndex) { + auto SegmentIter = Obj.segment_begin(); + std::advance(SegmentIter, *Section.ParentIndex); + Section.Header.sh_offset = + SegmentIter->getHeader().p_offset + Section.Header.sh_offset - + Obj.getOriginalSegment(*Section.ParentIndex).p_offset; + } else { + Offset = alignTo( + Offset, !Section.Header.sh_addralign ? 1 : Section.Header.sh_addralign); + Section.Header.sh_offset = Offset; + if (Section.Header.sh_type != SHT_NOBITS) + Offset += Section.Header.sh_size; + } + } + + return Error::success(); +} + +template Error MutableObjectWriter::assignOffsets() { + uint64_t Offset = 0; + if (Error E = layoutSegments(Offset)) + return E; + if (Error E = layoutSections(Offset)) + return E; + Offset = alignTo(Offset, sizeof(Addr)); + Obj.getMutableHeader().e_shoff = Offset; + + return Error::success(); +} + +template Error MutableObjectWriter::finalize() { + if (Error E = assignOffsets()) + return E; + + return Error::success(); +} + +template size_t MutableObjectWriter::totalSize() { + size_t Ret = Obj.getHeader().e_shoff + + (std::distance(Obj.section_begin(), Obj.section_end()) * + sizeof(Shdr)); + return Ret; +} + +template Error MutableObjectWriter::write() { + if (Error E = Buf.allocate(totalSize())) + return E; + + using SegmentRef = typename MutableELFObject::SegmentRef; + for (const SegmentRef Segment : Obj.segments()) { + uint8_t *Start = Buf.getBufferStart() + Segment.getHeader().p_offset; + Expected &> MutSegment = + Obj.getMutableSegment(Segment); + if (!MutSegment) + return MutSegment.takeError(); + std::memcpy(Start, MutSegment->OriginalData.data(), + MutSegment->OriginalData.size()); + } + + for (const SectionRef Sec : Obj.sections()) { + if (!Obj.findParentSegment(Sec)) { + Expected &> SecOrErr = Obj.getMutableSection(Sec); + if (!SecOrErr) + return SecOrErr.takeError(); + uint8_t *Start = Buf.getBufferStart() + SecOrErr->Header.sh_offset; + std::memcpy(Start, SecOrErr->Data.data(), SecOrErr->Header.sh_size); + } + } + + Shdr *Shdrs = reinterpret_cast( + Buf.getBufferStart() + Obj.getHeader().e_shoff); + for (const SectionRef Sec : Obj.sections()) { + Expected &> SecOrErr = Obj.getMutableSection(Sec); + if (!SecOrErr) + return SecOrErr.takeError(); + *Shdrs = SecOrErr->Header; + ++Shdrs; + } + + std::memcpy(Buf.getBufferStart(), &Obj.getHeader(), + sizeof(Ehdr)); + + return Buf.commit(); +} + template class ELFBuilder; template class ELFBuilder; template class ELFBuilder; @@ -2317,6 +2469,10 @@ template class ELFWriter; template class ELFWriter; +template class MutableObjectWriter; +template class MutableObjectWriter; +template class MutableObjectWriter; +template class MutableObjectWriter; } // end namespace elf } // end namespace objcopy } // end namespace llvm Index: llvm/tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -158,9 +158,11 @@ /// of the input binary (ELF, MachO or COFF). static Error executeObjcopyOnBinary(const CopyConfig &Config, object::Binary &In, Buffer &Out) { - if (auto *ELFBinary = dyn_cast(&In)) + if (auto *ELFBinary = dyn_cast(&In)) { + if (Config.UseMutableObject) + return elf::executeObjcopyWithMutableObject(Config, *ELFBinary, Out); return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); - else if (auto *COFFBinary = dyn_cast(&In)) + } else if (auto *COFFBinary = dyn_cast(&In)) return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); else if (auto *MachOBinary = dyn_cast(&In)) return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out);