Index: test/tools/llvm-objcopy/basic-align-copy.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/basic-align-copy.test @@ -0,0 +1,37 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy -O binary %t %t2 +# RUN: od -t x2 %t2 | FileCheck %s +# RUN: wc -c < %t2 | FileCheck %s --check-prefix=SIZE + +!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: 0x0000000000001000 + Content: "c3c3c3c3" + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000001000 + Content: "32" +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + Sections: + - Section: .text + - Type: PT_LOAD + Flags: [ PF_R ] + Sections: + - Section: .data + +# 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/basic-binary-copy.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/basic-binary-copy.test @@ -0,0 +1,25 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy -O binary %t %t2 +# RUN: od -t x2 -v %t2 | FileCheck %s +# RUN: wc -c < %t2 | FileCheck %s --check-prefix=SIZE + +!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: 0x0000000000001000 + Content: "c3c3c3c3" +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + Sections: + - Section: .text + +# CHECK: 0000000 c3c3 c3c3 +# SIZE: 4 Index: test/tools/llvm-objcopy/basic-copy.test =================================================================== --- test/tools/llvm-objcopy/basic-copy.test +++ test/tools/llvm-objcopy/basic-copy.test @@ -22,11 +22,6 @@ # CHECK: Type: SHT_NULL -# CHECK: Name: .shstrtab -# CHECK-NEXT: Type: SHT_STRTAB -# CHECK-NEXT: Flags [ -# CHECK-NEXT: ] - # CHECK: Name: .bss # CHECK-NEXT: Type: SHT_NOBITS # CHECK-NEXT: Flags [ @@ -45,3 +40,8 @@ # CHECK-NEXT: Address: # CHECK-NEXT: Offset: [[OFFSET]] # CHECK-NEXT: Size: 4 + +# CHECK: Name: .shstrtab +# CHECK-NEXT: Type: SHT_STRTAB +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] Index: test/tools/llvm-objcopy/hello-world.s =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/hello-world.s @@ -0,0 +1,48 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t +# RUN: llvm-objcopy %t %t2 +# RUN: llvm-readobj -program-headers %t2 | FileCheck %s + + .global _start + .text +_start: + mov $1, %rax + mov $1, %rdi + mov $msg, %rsi + mov $14, %rdx + syscall + + mov $60, %rax + mov $0, %rdi + syscall + + .rodata +msg: + .ascii "Hello, World!\n" + +# CHECK: ProgramHeader { +# CHECK: Type: PT_LOAD +# CHECK-NEXT: Offset: +# CHECK-NEXT: VirtualAddress: +# CHECK-NEXT: PhysicalAddress: +# CHECK-NEXT: FileSize: +# CHECK-NEXT: MemSize: +# CHECK-NEXT: Flags [ +# CHECK-NEXT: PF_R +# CHECK-NEXT: ] +# CHECK-NEXT: Alignment: 4096 +# CHECK-NEXT: } + +# CHECK: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD +# CHECK-NEXT: Offset: +# CHECK-NEXT: VirtualAddress: +# CHECK-NEXT: PhysicalAddress: +# CHECK-NEXT: FileSize: 46 +# CHECK-NEXT: MemSize: 46 +# CHECK-NEXT: Flags [ +# CHECK-NEXT: PF_R +# CHECK-NEXT: PF_X +# CHECK-NEXT: ] +# CHECK-NEXT: Alignment: +# CHECK-NEXT: } Index: test/tools/llvm-objcopy/program-headers.test =================================================================== --- test/tools/llvm-objcopy/program-headers.test +++ /dev/null @@ -1,68 +0,0 @@ -# RUN: yaml2obj %s -o %t -# RUN: llvm-objcopy %t %t2 -# RUN: llvm-readobj -program-headers %t2 | FileCheck %s - -!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: 0x0000000000001000 - Content: "00000000" - - Name: .init - Type: SHT_PROGBITS - Flags: [ SHF_ALLOC, SHF_EXECINSTR ] - Content: "00000000" - AddressAlign: 0x0000000000000010 - - Name: .data - Type: SHT_PROGBITS - Flags: [ SHF_ALLOC ] - Content: "00000000" - AddressAlign: 0x0000000000001000 -ProgramHeaders: - - Type: PT_LOAD - Flags: [ PF_X, PF_R ] - VAddr: 0xAAAA1000 - PAddr: 0xFFFF1000 - Sections: - - Section: .text - - Section: .init - - Type: PT_LOAD - Flags: [ PF_R ] - VAddr: 0xAAAA2000 - PAddr: 0xFFFF2000 - Sections: - - Section: .data - -#CHECK: ProgramHeaders [ -#CHECK-NEXT: ProgramHeader { -#CHECK-NEXT: Type: PT_LOAD -#CHECK-NEXT: Offset: 0x1000 -#CHECK-NEXT: VirtualAddress: 0xAAAA1000 -#CHECK-NEXT: PhysicalAddress: 0xFFFF1000 -#CHECK-NEXT: FileSize: 20 -#CHECK-NEXT: MemSize: 20 -#CHECK-NEXT: Flags [ -#CHECK-NEXT: PF_R -#CHECK-NEXT: PF_X -#CHECK-NEXT: ] -#CHECK-NEXT: Alignment: 4096 -#CHECK-NEXT: } -#CHECK-NEXT: ProgramHeader { -#CHECK-NEXT: Type: PT_LOAD -#CHECK-NEXT: Offset: 0x2000 -#CHECK-NEXT: VirtualAddress: 0xAAAA2000 -#CHECK-NEXT: PhysicalAddress: 0xFFFF2000 -#CHECK-NEXT: FileSize: 4 -#CHECK-NEXT: MemSize: 4 -#CHECK-NEXT: Flags [ -#CHECK-NEXT: PF_R -#CHECK-NEXT: ] -#CHECK-NEXT: Alignment: 4096 -#CHECK-NEXT: } -#CHECK-NEXT:] Index: tools/llvm-objcopy/Object.h =================================================================== --- tools/llvm-objcopy/Object.h +++ tools/llvm-objcopy/Object.h @@ -22,7 +22,7 @@ class SectionBase { public: llvm::StringRef Name; - Segment *ParentSegment; + Segment *ParentSegment = nullptr; uint64_t HeaderOffset; uint64_t OriginalOffset; uint32_t Index; @@ -58,6 +58,7 @@ }; std::set Sections; + llvm::ArrayRef Contents; public: uint64_t Align; @@ -70,6 +71,7 @@ uint64_t Type; uint64_t VAddr; + Segment(llvm::ArrayRef Data) : Contents(Data) {} void finalize(); const SectionBase *firstSection() const { if (!Sections.empty()) @@ -78,6 +80,7 @@ } void addSection(const SectionBase *sec) { Sections.insert(sec); } template void writeHeader(llvm::FileOutputBuffer &Out) const; + void writeSegment(uint8_t *Buf) const; }; class Section : public SectionBase { @@ -117,14 +120,16 @@ typedef typename ELFT::Ehdr Elf_Ehdr; typedef typename ELFT::Phdr Elf_Phdr; + SecPtr makeSection(const llvm::object::ELFFile &ElfFile, + const Elf_Shdr &Shdr); + void readProgramHeaders(const llvm::object::ELFFile &ElfFile); + void readSectionHeaders(const llvm::object::ELFFile &ElfFile); + +protected: StringTableSection *SectionNames; std::vector Sections; std::vector Segments; - void sortSections(); - void assignOffsets(); - void readProgramHeaders(const llvm::object::ELFFile &ElfFile); - void readSectionHeaders(const llvm::object::ELFFile &ElfFile); void writeHeader(llvm::FileOutputBuffer &Out) const; void writeProgramHeaders(llvm::FileOutputBuffer &Out) const; void writeSectionData(llvm::FileOutputBuffer &Out) const; @@ -140,9 +145,46 @@ uint32_t Flags; Object(const llvm::object::ELFObjectFile &Obj); - size_t totalSize() const; - void finalize(); - void write(llvm::FileOutputBuffer &Out); + virtual size_t totalSize() const = 0; + virtual void finalize() = 0; + virtual void write(llvm::FileOutputBuffer &Out) = 0; + virtual ~Object(); }; +template class ObjectELF : public Object { +private: + typedef std::unique_ptr SecPtr; + typedef std::unique_ptr SegPtr; + + typedef typename ELFT::Shdr Elf_Shdr; + typedef typename ELFT::Ehdr Elf_Ehdr; + typedef typename ELFT::Phdr Elf_Phdr; + + void sortSections(); + void assignOffsets(); + +public: + ObjectELF(const llvm::object::ELFObjectFile &Obj) : Object(Obj) {} + void finalize() override; + size_t totalSize() const override; + void write(llvm::FileOutputBuffer &Out) override; +}; + +template class ObjectBinary : public Object { +private: + typedef std::unique_ptr SecPtr; + typedef std::unique_ptr SegPtr; + + typedef typename ELFT::Shdr Elf_Shdr; + typedef typename ELFT::Ehdr Elf_Ehdr; + typedef typename ELFT::Phdr Elf_Phdr; + +public: + ObjectBinary(const llvm::object::ELFObjectFile &Obj) + : Object(Obj) {} + virtual void finalize() override; + virtual size_t totalSize() const override; + virtual void write(llvm::FileOutputBuffer &Out) override; + +}; #endif Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -42,6 +42,12 @@ } } +void Segment::writeSegment(uint8_t *Buf) const { + // We want to maintain segments interstitial data and contents exactly. + // this lets us just copy segments directly. + std::copy(std::begin(Contents), std::end(Contents), Buf); +} + void SectionBase::finalize() {} template @@ -99,7 +105,8 @@ void Object::readProgramHeaders(const ELFFile &ElfFile) { uint32_t Index = 0; for (const auto &Phdr : unwrapOrError(ElfFile.program_headers())) { - Segments.emplace_back(make_unique()); + ArrayRef Data{ElfFile.base() + Phdr.p_offset, Phdr.p_filesz}; + Segments.emplace_back(make_unique(Data)); Segment &Seg = *Segments.back(); Seg.Type = Phdr.p_type; Seg.Flags = Phdr.p_flags; @@ -122,6 +129,22 @@ } } +template +std::unique_ptr +Object::makeSection(const llvm::object::ELFFile &ElfFile, + const Elf_Shdr &Shdr) { + ArrayRef Data; + switch (Shdr.sh_type) { + case SHT_STRTAB: + return make_unique(); + case SHT_NOBITS: + return make_unique
(Data); + default: + Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); + return make_unique
(Data); + } +} + template void Object::readSectionHeaders(const ELFFile &ElfFile) { uint32_t Index = 0; @@ -130,12 +153,7 @@ ++Index; continue; } - if (Shdr.sh_type == SHT_STRTAB) - continue; - ArrayRef Data; - if (Shdr.sh_type != SHT_NOBITS) - Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); - SecPtr Sec = make_unique
(Data); + SecPtr Sec = makeSection(ElfFile, Shdr); Sec->Name = unwrapOrError(ElfFile.getSectionName(&Shdr)); Sec->Type = Shdr.sh_type; Sec->Flags = Shdr.sh_flags; @@ -148,17 +166,10 @@ Sec->Align = Shdr.sh_addralign; Sec->EntrySize = Shdr.sh_entsize; Sec->Index = Index++; - SectionNames->addString(Sec->Name); Sections.push_back(std::move(Sec)); } } -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) + sizeof(Elf_Shdr); -} - template Object::Object(const ELFObjectFile &Obj) { const auto &ElfFile = *Obj.getELFFile(); const auto &Ehdr = *ElfFile.getHeader(); @@ -170,31 +181,86 @@ Entry = Ehdr.e_entry; Flags = Ehdr.e_flags; - Sections.push_back(make_unique()); - SectionNames = dyn_cast(Sections.back().get()); - SectionNames->Name = ".shstrtab"; - SectionNames->addString(SectionNames->Name); - readSectionHeaders(ElfFile); readProgramHeaders(ElfFile); + + SectionNames = + dyn_cast(Sections[Ehdr.e_shstrndx - 1].get()); +} + +template +void Object::writeHeader(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart(); + Elf_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; + Ehdr.e_phoff = sizeof(Elf_Ehdr); + Ehdr.e_shoff = SHOffset; + Ehdr.e_flags = Flags; + Ehdr.e_ehsize = sizeof(Elf_Ehdr); + Ehdr.e_phentsize = sizeof(Elf_Phdr); + Ehdr.e_phnum = Segments.size(); + Ehdr.e_shentsize = sizeof(Elf_Shdr); + Ehdr.e_shnum = Sections.size() + 1; + Ehdr.e_shstrndx = SectionNames->Index; } -template void Object::sortSections() { +template +void Object::writeProgramHeaders(FileOutputBuffer &Out) const { + for (auto &Phdr : Segments) + Phdr->template writeHeader(Out); +} + +template +void Object::writeSectionHeaders(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart() + SHOffset; + // This reference serves to write the dummy section header at the begining + // of the file. + Elf_Shdr &Shdr = *reinterpret_cast(Buf); + Shdr.sh_name = 0; + Shdr.sh_type = SHT_NULL; + Shdr.sh_flags = 0; + Shdr.sh_addr = 0; + Shdr.sh_offset = 0; + Shdr.sh_size = 0; + Shdr.sh_link = 0; + Shdr.sh_info = 0; + Shdr.sh_addralign = 0; + Shdr.sh_entsize = 0; + + for (auto &Section : Sections) + Section->template writeHeader(Out); +} + +template +Object::~Object() { } + +template +void Object::writeSectionData(FileOutputBuffer &Out) const { + for (auto &Section : Sections) + Section->writeSection(Out); +} + +template void ObjectELF::sortSections() { // Put all sections in offset order. Maintain the ordering as closely as // possible while meeting that demand however. auto CompareSections = [](const SecPtr &A, const SecPtr &B) { return A->OriginalOffset < B->OriginalOffset; }; - std::stable_sort(std::begin(Sections), std::end(Sections), CompareSections); + std::stable_sort(std::begin(this->Sections), std::end(this->Sections), + CompareSections); } -template void Object::assignOffsets() { +template void ObjectELF::assignOffsets() { // Decide file offsets and indexes. - size_t PhdrSize = Segments.size() * sizeof(Elf_Phdr); + size_t PhdrSize = this->Segments.size() * sizeof(Elf_Phdr); // We can put section data after the ELF header and the program headers. uint64_t Offset = sizeof(Elf_Ehdr) + PhdrSize; uint64_t Index = 1; - for (auto &Section : Sections) { + for (auto &Section : this->Sections) { // The segment can have a different alignment than the section. In the case // that there is a parent segment then as long as we satisfy the alignment // of the segment it should follow that that the section is aligned. @@ -241,90 +307,88 @@ // this needs to be 4-byte aligned and on 64-bit it needs to be 8-byte aligned // so the size of ELFT::Addr is used to ensure this. Offset = alignTo(Offset, sizeof(typename ELFT::Addr)); - SHOffset = Offset; + this->SHOffset = Offset; +} + +template size_t ObjectELF::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) + + sizeof(Elf_Shdr); +} + +template void ObjectELF::write(FileOutputBuffer &Out) { + this->writeHeader(Out); + this->writeProgramHeaders(Out); + this->writeSectionData(Out); + this->writeSectionHeaders(Out); } -template void Object::finalize() { +template void ObjectELF::finalize() { + for (const auto &Section : this->Sections) { + this->SectionNames->addString(Section->Name); + } + 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 // remaining issues. - uint64_t Offset = SHOffset + sizeof(Elf_Shdr); - for (auto &Section : Sections) { + uint64_t Offset = this->SHOffset + sizeof(Elf_Shdr); + for (auto &Section : this->Sections) { Section->HeaderOffset = Offset; Offset += sizeof(Elf_Shdr); - Section->NameIndex = SectionNames->findIndex(Section->Name); + 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 { - uint8_t *Buf = Out.getBufferStart(); - Elf_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; - Ehdr.e_phoff = sizeof(Elf_Ehdr); - Ehdr.e_shoff = SHOffset; - Ehdr.e_flags = Flags; - Ehdr.e_ehsize = sizeof(Elf_Ehdr); - Ehdr.e_phentsize = sizeof(Elf_Phdr); - Ehdr.e_phnum = Segments.size(); - Ehdr.e_shentsize = sizeof(Elf_Shdr); - Ehdr.e_shnum = Sections.size(); - Ehdr.e_shstrndx = SectionNames->Index; +template size_t ObjectBinary::totalSize() const { + size_t TotalSize = 0; + for (const auto &Segment : this->Segments) { + if (Segment->Type == PT_LOAD) { + TotalSize = alignTo(TotalSize, Segment->Align); + TotalSize += Segment->FileSize; + } + } + return TotalSize; } -template -void Object::writeProgramHeaders(FileOutputBuffer &Out) const { - for (auto &Phdr : Segments) - Phdr->template writeHeader(Out); +template void ObjectBinary::write(FileOutputBuffer &Out) { + uint8_t *Buf = Out.getBufferStart(); + uint64_t Offset = 0; + for (const auto &Segment : this->Segments) { + if (Segment->Type == PT_LOAD) { + Offset = alignTo(Offset, Segment->Align); + Segment->writeSegment(Buf + Offset); + Offset += Segment->FileSize; + } + } } -template -void Object::writeSectionHeaders(FileOutputBuffer &Out) const { - uint8_t *Buf = Out.getBufferStart() + SHOffset; - // This reference serves to write the dummy section header at the begining - // of the file. - Elf_Shdr &Shdr = *reinterpret_cast(Buf); - Shdr.sh_name = 0; - Shdr.sh_type = SHT_NULL; - Shdr.sh_flags = 0; - Shdr.sh_addr = 0; - Shdr.sh_offset = 0; - Shdr.sh_size = 0; - Shdr.sh_link = 0; - Shdr.sh_info = 0; - Shdr.sh_addralign = 0; - Shdr.sh_entsize = 0; - - for (auto &Section : Sections) - Section->template writeHeader(Out); -} +template void ObjectBinary::finalize() { + for (auto &Segment : this->Segments) + Segment->finalize(); -template -void Object::writeSectionData(FileOutputBuffer &Out) const { - for (auto &Section : Sections) - Section->writeSection(Out); + // Put all segments in offset order + auto CompareSections = [](const SegPtr &A, const SegPtr &B) { + return A->Offset < B->Offset; + }; + std::sort(std::begin(this->Segments), std::end(this->Segments), + CompareSections); } -template void Object::write(FileOutputBuffer &Out) { - writeHeader(Out); - writeProgramHeaders(Out); - writeSectionData(Out); - writeSectionHeaders(Out); -} +template class ObjectELF; +template class ObjectELF; +template class ObjectELF; +template class ObjectELF; -template class Object; -template class Object; -template class Object; -template class Object; +template class ObjectBinary; +template class ObjectBinary; +template class ObjectBinary; +template class ObjectBinary; Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -53,13 +53,18 @@ cl::opt InputFilename(cl::Positional, cl::desc("")); cl::opt OutputFilename(cl::Positional, cl::desc(""), cl::init("-")); +cl::opt OutputFormat("O", cl::desc("set output format")); void CopyBinary(const ELFObjectFile &ObjFile) { std::unique_ptr Buffer; - Object Obj{ObjFile}; - Obj.finalize(); + std::unique_ptr> Obj; + if (!OutputFormat.empty() && OutputFormat == "binary") + Obj = make_unique>(ObjFile); + else + Obj = make_unique>(ObjFile); + Obj->finalize(); ErrorOr> BufferOrErr = - FileOutputBuffer::create(OutputFilename, Obj.totalSize(), + FileOutputBuffer::create(OutputFilename, Obj->totalSize(), FileOutputBuffer::F_executable); if (auto EC = BufferOrErr.getError()) error("failed to open " + OutputFilename); @@ -70,7 +75,7 @@ make_unique(OutputFilename.data(), EC, sys::fs::F_None); if (EC) report_fatal_error(EC.message()); - Obj.write(*Buffer); + Obj->write(*Buffer); if (auto EC = Buffer->commit()) reportError(OutputFilename, EC); Out->keep();