Index: test/tools/llvm-objcopy/basic-binary-copy.s =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/basic-binary-copy.s @@ -0,0 +1,16 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t +# RUN: llvm-objcopy -output-binary %t %t2 +# RUN: od -t x2 -v %t2 | FileCheck %s +# RUN: wc -c < %t2 | FileCheck %s --check-prefix=SIZE + + .globl main + .text +main: + ret + ret + ret + ret + +# CHECK: 0000000 c3c3 c3c3 +# SIZE: 4 Index: test/tools/llvm-objcopy/binary-align-copy.s =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/binary-align-copy.s @@ -0,0 +1,22 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t +# RUN: llvm-objcopy -output-binary %t %t2 +# RUN: od -t x2 %t2 | FileCheck %s +# RUN: wc -c < %t2 | FileCheck %s --check-prefix=SIZE + + .globl main + .text +main: + ret + ret + ret + ret + + .data + .byte 50 + +# CHECK: 0000000 c3c3 c3c3 0000 0000 0000 0000 0000 0000 +# CHECK-NEXT: 0000020 0000 0000 0000 0000 0000 0000 0000 0000 +# CHECK-NEXT: * +# CHECK-NEXT: 0010000 0032 +# SIZE: 4097 Index: test/tools/llvm-objcopy/interstitial-binary-copy.s =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/interstitial-binary-copy.s @@ -0,0 +1,25 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t +# RUN: printf %4092s | dd of=%t bs=1 seek=4100 conv=notrunc +# RUN: printf "\x00\x10" | dd of=%t bs=1 seek=208 conv=notrunc +# RUN: printf "\x00\x10" | dd of=%t bs=1 seek=216 conv=notrunc +# RUN: llvm-objcopy -output-binary %t %t2 +# RUN: od -t x2 %t2 | FileCheck %s +# RUN: wc -c < %t2 | FileCheck %s --check-prefix=SIZE + + .globl main + .text +main: + ret + ret + ret + ret + + .data + .byte 50 + +# CHECK: 0000000 c3c3 c3c3 2020 2020 2020 2020 2020 2020 +# CHECK-NEXT: 0000020 2020 2020 2020 2020 2020 2020 2020 2020 +# CHECK-NEXT: * +# CHECK-NEXT: 0010000 0032 +# SIZE: 4097 Index: tools/llvm-objcopy/Object.h =================================================================== --- tools/llvm-objcopy/Object.h +++ tools/llvm-objcopy/Object.h @@ -27,8 +27,7 @@ class SectionBase { public: llvm::StringRef Name; - Segment* ParrentSegment; - uint64_t HeaderOffset; + Segment *ParrentSegment = nullptr; uint32_t Index; uint64_t Addr = 0; @@ -44,8 +43,8 @@ virtual ~SectionBase() {} virtual void finalize(); - template void writeHeader(llvm::FileOutputBuffer &Out) const; - virtual void writeSection(llvm::FileOutputBuffer &Out) const = 0; + template void writeHeader(uint8_t *Buf) const; + virtual void writeSection(uint8_t *Buf) const = 0; }; class Segment { @@ -57,6 +56,8 @@ }; std::set Sections; + std::vector> InterstitialSections; + llvm::ArrayRef Contents; public: uint64_t Align; @@ -68,6 +69,7 @@ uint64_t Type; uint64_t VAddr; + Segment(llvm::ArrayRef Data) : Contents(Data) {} void finalize(); const SectionBase * firstSection() const { if(Sections.size()) @@ -75,7 +77,8 @@ return nullptr; } void addSection(const SectionBase *sec) { Sections.insert(sec); } - template void writeHeader(llvm::FileOutputBuffer &Out) const; + void writeMemSegment(uint8_t *Buf) const; + template void writeHeader(uint8_t *Buf) const; }; class Section : public SectionBase { @@ -83,8 +86,8 @@ llvm::ArrayRef Contents; public: - Section(llvm::ArrayRef Data) : Contents(Data) {} - void writeSection(llvm::FileOutputBuffer &Out) const override; + Section(llvm::ArrayRef Data) : Contents(Data) { Size = Data.size(); } + void writeSection(uint8_t *Buf) const override; }; class StringTableSection : public SectionBase { @@ -100,7 +103,7 @@ void removeString(llvm::StringRef Name); uint32_t findIndex(llvm::StringRef Name) const; void finalize() override; - void writeSection(llvm::FileOutputBuffer &Out) const override; + void writeSection(uint8_t *Buf) const override; static bool classof(const SectionBase *S) { return S->Type == llvm::ELF::SHT_STRTAB; } @@ -136,15 +139,14 @@ SectionBase *DefinedIn, uint64_t Value, uint64_t Sz); void removeSymbol(llvm::StringRef); void finalize() override; - void writeSection(llvm::FileOutputBuffer &) const override; + void writeSection(uint8_t *Buf) const override; static bool classof(const SectionBase *S) { return S->Type == llvm::ELF::SHT_SYMTAB; } }; -template -class Object { -private: +template class ObjectCopyBase { +protected: typedef std::unique_ptr SecPtr; typedef typename ELFT::Shdr Elf_Shdr; typedef typename ELFT::Ehdr Elf_Ehdr; @@ -155,16 +157,11 @@ std::vector Sections; std::vector Segments; - void sortSections(); - void assignOffsets(); +private: void readProgramHeaders(const llvm::object::ELFFile &ElfFile); void readSectionHeaders(const llvm::object::ELFFile &ElfFile); void readSymbolTable(const llvm::object::ELFFile &ElfFile, const Elf_Shdr &SymTabShdr); - void writeHeader(llvm::FileOutputBuffer &Out) const; - void writeProgramHeaders(llvm::FileOutputBuffer &Out) const; - void writeSectionData(llvm::FileOutputBuffer &Out) const; - void writeSectionHeaders(llvm::FileOutputBuffer &Out) const; public: uint8_t Ident[16]; @@ -175,9 +172,42 @@ uint32_t Version; uint32_t Flags; - Object(const llvm::object::ELFObjectFile &Obj); - size_t totalSize() const; - void finalize(); - void write(llvm::FileOutputBuffer &Out); + ObjectCopyBase(const llvm::object::ELFObjectFile &Obj); + + virtual size_t totalSize() const = 0; + virtual void finalize() = 0; + virtual void write(llvm::FileOutputBuffer &Out) const = 0; + virtual ~ObjectCopyBase() {} +}; + +template class ObjectCopyELF : public ObjectCopyBase { +private: + typedef std::unique_ptr SecPtr; + typedef typename ELFT::Shdr Elf_Shdr; + typedef typename ELFT::Ehdr Elf_Ehdr; + typedef typename ELFT::Phdr Elf_Phdr; + + void sortSections(); + void assignOffsets(); + void writeHeader(llvm::FileOutputBuffer &Out) const; + void writeProgramHeaders(llvm::FileOutputBuffer &Out) const; + void writeSectionData(llvm::FileOutputBuffer &Out) const; + void writeSectionHeaders(llvm::FileOutputBuffer &Out) const; + +public: + ObjectCopyELF(const llvm::object::ELFObjectFile &Obj) + : ObjectCopyBase(Obj) {} + virtual size_t totalSize() const override; + virtual void finalize() override; + virtual void write(llvm::FileOutputBuffer &Out) const override; +}; + +template class ObjectCopyBinary : public ObjectCopyBase { +public: + ObjectCopyBinary(const llvm::object::ELFObjectFile &Obj) + : ObjectCopyBase(Obj) {} + virtual size_t totalSize() const override; + virtual void finalize() override; + virtual void write(llvm::FileOutputBuffer &Out) const override; }; #endif Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -13,9 +13,13 @@ using namespace object; using namespace ELF; -template void Segment::writeHeader(FileOutputBuffer &Out) const { - uint8_t *Buf = Out.getBufferStart(); - Buf += sizeof(typename ELFT::Ehdr) + Index * sizeof(typename ELFT::Phdr); +uint64_t align(uint64_t Value, uint64_t Multiple) { + if (!Multiple || Value % Multiple == 0) + return Value; + return Value + Multiple - Value % Multiple; +} + +template void Segment::writeHeader(uint8_t *Buf) const { typename ELFT::Phdr &Phdr = *reinterpret_cast(Buf); Phdr.p_type = Type; Phdr.p_flags = Flags; @@ -28,24 +32,60 @@ } void Segment::finalize() { - auto MinElem = - std::min_element(std::begin(Sections), std::end(Sections), - [](const SectionBase *a, const SectionBase *b) -> bool { - return a->Offset < b->Offset; - }); - Offset = (**MinElem).Offset; - FileSize = 0; - for (auto Section : Sections) - if (Section->Type != SHT_NOBITS) - FileSize += Section->Size; + auto MinElem = firstSection(); + if (!MinElem) + return; + if (Type == PT_LOAD) { + auto PrevSection = MinElem; + for (auto Sec : Sections) { + if (Sec->Addr > PrevSection->Addr + PrevSection->Size) { + // Then we have a gap which we can expect file layout to respect. We + // need to respect the contents of this gap as well as the user may have + // put something meanigful in them like trap instructions in the case + // that this segment is executable. + ArrayRef InterstitialData{Contents.data() + PrevSection->Addr - + VAddr + PrevSection->Size, + Contents.data() + Sec->Addr - VAddr}; + auto InterstitialSection = new Section(InterstitialData); + InterstitialSection->Addr = PrevSection->Addr + PrevSection->Size; + InterstitialSections.emplace_back(InterstitialSection); + } + PrevSection = Sec; + } + // Lastly there might be an interstitial gap between the last section and + // the end of the segment. + if (VAddr + FileSize > PrevSection->Addr + PrevSection->Size) { + ArrayRef InterstitialData{Contents.data() + PrevSection->Addr - + VAddr + PrevSection->Size, + Contents.data() + FileSize}; + auto InterstitialSection = new Section(InterstitialData); + InterstitialSection->Addr = PrevSection->Addr + PrevSection->Size; + InterstitialSections.emplace_back(InterstitialSection); + } + for (auto &InterstitialSection : InterstitialSections) + Sections.insert(InterstitialSection.get()); + } + + // In practice you could have a section that contained no sections. In this + // case the file offset is meaingless. Other aspects of it can still hold + // meaning however so we want to preserve those things still. So if there is + // no MinElem we just leave Offset as it was in the file. + if (MinElem) + Offset = MinElem->Offset; +} + +void Segment::writeMemSegment(uint8_t *Buf) const { + // Because we have filled in all gaps with interstitial sections we can be + // sure that we will cover the entire FileSize area + for (auto Section : Sections) { + Section->writeSection(Buf); + Buf += Section->Size; + } } void SectionBase::finalize() {} -template -void SectionBase::writeHeader(FileOutputBuffer &Out) const { - uint8_t *Buf = Out.getBufferStart(); - Buf += HeaderOffset; +template void SectionBase::writeHeader(uint8_t *Buf) const { typename ELFT::Shdr &Shdr = *reinterpret_cast(Buf); Shdr.sh_name = NameIndex; Shdr.sh_type = Type; @@ -59,8 +99,7 @@ Shdr.sh_entsize = EntrySize; } -void Section::writeSection(FileOutputBuffer &Out) const { - uint8_t *Buf = Out.getBufferStart() + Offset; +void Section::writeSection(uint8_t *Buf) const { std::copy(std::begin(Contents), std::end(Contents), Buf); } @@ -93,8 +132,7 @@ } } -void StringTableSection::writeSection(FileOutputBuffer &Out) const { - uint8_t *Buf = Out.getBufferStart() + Offset; +void StringTableSection::writeSection(uint8_t *Buf) const { for (const auto &Name : Strings) { Buf = std::copy(std::begin(Name.getKey()), std::end(Name.getKey()), Buf); // We need to set the null character and then increment the buffer past it @@ -156,9 +194,7 @@ } template -void SymbolTableSection::writeSection(llvm::FileOutputBuffer &Out) const { - uint8_t *Buf = Out.getBufferStart(); - Buf += Offset; +void SymbolTableSection::writeSection(uint8_t *Buf) const { typename ELFT::Sym *Sym = reinterpret_cast(Buf); for(auto &Symbol : FinalSymbols) { @@ -176,10 +212,11 @@ } template -void Object::readProgramHeaders(const ELFFile &ElfFile) { +void ObjectCopyBase::readProgramHeaders(const ELFFile &ElfFile) { uint32_t Index = 0; for (const auto &Phdr : unwrapOrError(ElfFile.program_headers())) { - Segments.emplace_back(); + ArrayRef Data{ElfFile.base() + Phdr.p_offset, Phdr.p_filesz}; + Segments.emplace_back(Data); Segment &Seg = Segments.back(); Seg.Type = Phdr.p_type; Seg.Flags = Phdr.p_flags; @@ -191,17 +228,18 @@ Seg.Index = Index++; for (auto &Section : Sections) { if (Seg.Offset <= Section->Offset && - Seg.Offset + Seg.FileSize >= Section->Offset + Section->Size) { - Seg.addSection(&*Section); + Seg.Offset + Seg.FileSize >= Section->Offset + Section->Size && + (Section->Flags & SHF_ALLOC)) { Section->ParrentSegment = &Seg; + Seg.addSection(Section.get()); } } } } template -void Object::readSymbolTable(const ELFFile &ElfFile, - const Elf_Shdr &SymTabShdr) { +void ObjectCopyBase::readSymbolTable(const ELFFile &ElfFile, + const Elf_Shdr &SymTabShdr) { StringTableSection *StrTab = dyn_cast(Sections[SymTabShdr.sh_link].get()); @@ -238,7 +276,7 @@ } template -void Object::readSectionHeaders(const ELFFile &ElfFile) { +void ObjectCopyBase::readSectionHeaders(const ELFFile &ElfFile) { uint32_t Index = 0; const Elf_Shdr *SymTabShdr = nullptr; for (const auto &Shdr : unwrapOrError(ElfFile.sections())) { @@ -277,13 +315,8 @@ readSymbolTable(ElfFile, *SymTabShdr); } -template size_t Object::totalSize() const { - // We already have the section header offset so we can calculate the total - // size by just adding up the size of each section header; - return SHOffset + Sections.size() * sizeof(Elf_Shdr); -} - -template Object::Object(const ELFObjectFile &Obj) { +template +ObjectCopyBase::ObjectCopyBase(const ELFObjectFile &Obj) { const auto &ElfFile = *Obj.getELFFile(); const auto &Ehdr = *ElfFile.getHeader(); @@ -304,31 +337,33 @@ readProgramHeaders(ElfFile); } -template void Object::sortSections() { - std::sort( - std::begin(Sections), std::end(Sections), - [](const SecPtr &A, const SecPtr &B) { return A->Index < B->Index; }); +template size_t ObjectCopyELF::totalSize() const { + // We already have the section header offset so we can calculate the total + // size by just adding up the size of each section header; + return this->SHOffset + this->Sections.size() * sizeof(Elf_Shdr); } -uint64_t align(uint64_t Value, uint64_t Multiple) { - if (!Multiple || Value % Multiple == 0) - return Value; - return Value + Multiple - Value % Multiple; +template void ObjectCopyELF::sortSections() { + std::sort( + std::begin(this->Sections), std::end(this->Sections), + [](const SecPtr &A, const SecPtr &B) { return A->Index < B->Index; }); } -template void Object::assignOffsets() { +template void ObjectCopyELF::assignOffsets() { // Decide file offsets and indexs - size_t PhdrSize = Segments.size() * sizeof(Elf_Phdr); + size_t PhdrSize = this->Segments.size() * sizeof(Elf_Phdr); // After the header and the program headers we can put section data. uint64_t Offset = sizeof(Elf_Ehdr) + PhdrSize; uint64_t Index = 0; - for (auto &Section : Sections) { + for (auto &Section : this->Sections) { // The segment can have a different alignment than the section. We need to // make sure if (Section->ParrentSegment) { auto FirstInSeg = Section->ParrentSegment->firstSection(); if (FirstInSeg == Section.get()) Offset = align(Offset, Section->ParrentSegment->Align); + // We should respect interstitial gaps of allocated sections + Offset = FirstInSeg->Offset + Section->Addr - FirstInSeg->Addr; } Offset = align(Offset, Section->Align); Section->Offset = Offset; @@ -341,77 +376,119 @@ // section header table offset to be exactly here. This spot might not be // aligned properlly however so we should align it as needed. This only takes // a little bit of tweaking to ensure that the sh_name is 4 byte aligned - Offset = align(Offset, 4); - SHOffset = Offset; + Offset = align(Offset, sizeof(typename ELFT::Word)); + this->SHOffset = Offset; } -template void Object::finalize() { +template void ObjectCopyELF::finalize() { sortSections(); assignOffsets(); // finalize SectionNames first so that we can assign name indexes. - SectionNames->finalize(); + this->SectionNames->finalize(); // Finally now that all offsets and indexes have been set we can finalize any // reamining issues. - uint64_t Offset = SHOffset; - for (auto &Section : Sections) { - Section->HeaderOffset = Offset; - Offset += sizeof(Elf_Shdr); - Section->NameIndex = SectionNames->findIndex(Section->Name); + for (auto &Section : this->Sections) { + Section->NameIndex = this->SectionNames->findIndex(Section->Name); Section->finalize(); } - for (auto &Segment : Segments) + for (auto &Segment : this->Segments) Segment.finalize(); } template -void Object::writeHeader(FileOutputBuffer &Out) const { +void ObjectCopyELF::writeHeader(FileOutputBuffer &Out) const { uint8_t *Buf = Out.getBufferStart(); typename ELFT::Ehdr &Ehdr = *reinterpret_cast(Buf); - std::copy(Ident, Ident + 16, Ehdr.e_ident); - Ehdr.e_type = Type; - Ehdr.e_machine = Machine; - Ehdr.e_version = Version; - Ehdr.e_entry = Entry; + std::copy(this->Ident, this->Ident + 16, Ehdr.e_ident); + Ehdr.e_type = this->Type; + Ehdr.e_machine = this->Machine; + Ehdr.e_version = this->Version; + Ehdr.e_entry = this->Entry; Ehdr.e_phoff = sizeof(Elf_Ehdr); - Ehdr.e_shoff = SHOffset; - Ehdr.e_flags = Flags; + Ehdr.e_shoff = this->SHOffset; + Ehdr.e_flags = this->Flags; Ehdr.e_ehsize = sizeof(Elf_Ehdr); Ehdr.e_phentsize = sizeof(Elf_Phdr); - Ehdr.e_phnum = Segments.size(); + Ehdr.e_phnum = this->Segments.size(); Ehdr.e_shentsize = sizeof(Elf_Shdr); - Ehdr.e_shnum = Sections.size(); - Ehdr.e_shstrndx = SectionNames->Index; + Ehdr.e_shnum = this->Sections.size(); + Ehdr.e_shstrndx = this->SectionNames->Index; } template -void Object::writeProgramHeaders(FileOutputBuffer &Out) const { - for (auto &Phdr : Segments) - Phdr.template writeHeader(Out); +void ObjectCopyELF::writeProgramHeaders(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart() + sizeof(Elf_Ehdr); + for (auto &Segment : this->Segments) { + Segment.template writeHeader(Buf); + Buf += sizeof(Elf_Phdr); + } } template -void Object::writeSectionHeaders(FileOutputBuffer &Out) const { - for (auto &Section : Sections) - Section->template writeHeader(Out); +void ObjectCopyELF::writeSectionHeaders(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart() + this->SHOffset; + for (auto &Section : this->Sections) { + Section->template writeHeader(Buf); + Buf += sizeof(Elf_Shdr); + } } template -void Object::writeSectionData(FileOutputBuffer &Out) const { - for (auto &Section : Sections) - Section->writeSection(Out); +void ObjectCopyELF::writeSectionData(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart(); + for (auto &Section : this->Sections) { + Section->writeSection(Buf + Section->Offset); + } } -template void Object::write(FileOutputBuffer &Out) { +template +void ObjectCopyELF::write(FileOutputBuffer &Out) const { writeHeader(Out); writeProgramHeaders(Out); writeSectionData(Out); writeSectionHeaders(Out); } -template class Object; -template class Object; -template class Object; -template class Object; +template void ObjectCopyBinary::finalize() { + for (auto &Segment : this->Segments) + Segment.finalize(); +} + +template size_t ObjectCopyBinary::totalSize() const { + uint64_t BinSize = 0; + for (auto &Segment : this->Segments) { + if (Segment.Type == PT_LOAD && Segment.Offset != 0) { + BinSize = align(BinSize, Segment.Align); + BinSize += Segment.FileSize; + } + } + return BinSize; +} + +template +void ObjectCopyBinary::write(FileOutputBuffer &Out) const { + // It's worth noting that Segments might be put in very different locations + // from each other. They will still be placed + uint8_t *Buf = Out.getBufferStart(); + uint64_t Offset = 0; + for (auto &Segment : this->Segments) { + if (Segment.Type == PT_LOAD && Segment.Offset != 0) { + Offset = align(Offset, Segment.Align); + Segment.writeMemSegment(Buf + Offset); + Offset += Segment.FileSize; + } + } +} + +template class ObjectCopyELF; +template class ObjectCopyELF; +template class ObjectCopyELF; +template class ObjectCopyELF; + +template class ObjectCopyBinary; +template class ObjectCopyBinary; +template class ObjectCopyBinary; +template class ObjectCopyBinary; Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -50,12 +50,20 @@ cl::opt InputFilename(cl::Positional, cl::desc("")); cl::opt OutputFilename(cl::Positional, cl::desc(""), cl::init("-")); +cl::opt OutputBinary("output-binary", cl::desc("output raw binary")); + void CopyBinary(const ELFObjectFile &ObjFile) { std::unique_ptr Buffer; - Object Obj{ObjFile}; - Obj.finalize(); + std::unique_ptr> ObjCopy; + + if (OutputBinary) + ObjCopy.reset(new ObjectCopyBinary(ObjFile)); + else + ObjCopy.reset(new ObjectCopyELF(ObjFile)); + ObjCopy->finalize(); + ErrorOr> BufferOrErr = - FileOutputBuffer::create(OutputFilename, Obj.totalSize(), + FileOutputBuffer::create(OutputFilename, ObjCopy->totalSize(), FileOutputBuffer::F_executable); if (auto EC = BufferOrErr.getError()) error("failed to open " + OutputFilename); @@ -67,7 +75,8 @@ if (EC) report_fatal_error(EC.message()); // now if the program fails for any reason the output file will be deleted - Obj.write(*Buffer); + ObjCopy->write(*Buffer); + if (auto EC = Buffer->commit()) reportError(OutputFilename, EC); Out->keep();