diff --git a/test/tools/llvm-objcopy/basic-align-copy.test b/test/tools/llvm-objcopy/basic-align-copy.test new file mode 100644 index 00000000000..f47f7097c79 --- /dev/null +++ b/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 diff --git a/test/tools/llvm-objcopy/basic-binary-copy.test b/test/tools/llvm-objcopy/basic-binary-copy.test new file mode 100644 index 00000000000..5808f1aadf3 --- /dev/null +++ b/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 diff --git a/tools/llvm-objcopy/LLVMBuild.txt b/tools/llvm-objcopy/LLVMBuild.txt index 4a75d525876..0a3473a222b 100644 --- a/tools/llvm-objcopy/LLVMBuild.txt +++ b/tools/llvm-objcopy/LLVMBuild.txt @@ -18,4 +18,4 @@ type = Tool name = llvm-objcopy parent = Tools -required_libraries = Object MC +required_libraries = Object Support MC diff --git a/tools/llvm-objcopy/Object.cpp b/tools/llvm-objcopy/Object.cpp index 77ac8d913de..c57dfe005ed 100644 --- a/tools/llvm-objcopy/Object.cpp +++ b/tools/llvm-objcopy/Object.cpp @@ -42,6 +42,13 @@ void Segment::finalize() { } } +void Segment::writeSegment(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart() + Offset; + // 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 +106,8 @@ template void Object::readProgramHeaders(const ELFFile &ElfFile) { uint32_t Index = 0; for (const auto &Phdr : unwrapOrError(ElfFile.program_headers())) { - Segments.emplace_back(llvm::make_unique()); + ArrayRef Data{ElfFile.base() + Phdr.p_offset, Phdr.p_filesz}; + Segments.emplace_back(llvm::make_unique(Data)); Segment &Seg = *Segments.back(); Seg.Type = Phdr.p_type; Seg.Flags = Phdr.p_flags; @@ -135,7 +143,7 @@ Object::makeSection(const llvm::object::ELFFile &ElfFile, default: Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); return llvm::make_unique
(Data); - }; + } } template @@ -163,12 +171,6 @@ void Object::readSectionHeaders(const ELFFile &ElfFile) { } } -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(); @@ -187,94 +189,6 @@ template Object::Object(const ELFObjectFile &Obj) { dyn_cast(Sections[Ehdr.e_shstrndx - 1].get()); } -template void Object::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); -} - -template void Object::assignOffsets() { - // Decide file offsets and indexes. - size_t PhdrSize = 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) { - // 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. - if (Section->ParentSegment) { - auto FirstInSeg = Section->ParentSegment->firstSection(); - if (FirstInSeg == Section.get()) { - Offset = alignTo(Offset, Section->ParentSegment->Align); - // There can be gaps at the start of a segment before the first section. - // So first we assign the alignment of the segment and then assign the - // location of the section from there - Section->Offset = - Offset + Section->OriginalOffset - Section->ParentSegment->Offset; - } - // We should respect interstitial gaps of allocated sections. We *must* - // maintain the memory image so that addresses are preserved. As, with the - // exception of SHT_NOBITS sections at the end of segments, the memory - // image is a copy of the file image, we preserve the file image as well. - // There's a strange case where a thread local SHT_NOBITS can cause the - // memory image and file image to not be the same. This occurs, on some - // systems, when a thread local SHT_NOBITS is between two SHT_PROGBITS - // and the thread local SHT_NOBITS section is at the end of a TLS segment. - // In this case to faithfully copy the segment file image we must use - // relative offsets. In any other case this would be the same as using the - // relative addresses so this should maintian the memory image as desired. - Offset = FirstInSeg->Offset + Section->OriginalOffset - - FirstInSeg->OriginalOffset; - } - // Alignment should have already been handled by the above if statement if - // this if this section is in a segment. Technically this shouldn't do - // anything bad if the alignments of the sections are all correct and the - // file image isn't corrupted. Still in sticking with the motto "maintain - // the file image" we should avoid messing up the file image if the - // alignment disagrees with the file image. - if (!Section->ParentSegment && Section->Align) - Offset = alignTo(Offset, Section->Align); - Section->Offset = Offset; - Section->Index = Index++; - if (Section->Type != SHT_NOBITS) - Offset += Section->Size; - } - // 'offset' should now be just after all the section data so we should set the - // section header table offset to be exactly here. This spot might not be - // aligned properly however so we should align it as needed. For 32-bit ELF - // 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; -} - -template void Object::finalize() { - for (auto &Section : Sections) - SectionNames->addString(Section->Name); - - sortSections(); - assignOffsets(); - - // Finalize SectionNames first so that we can assign name indexes. - 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) { - Section->HeaderOffset = Offset; - Offset += sizeof(Elf_Shdr); - Section->NameIndex = SectionNames->findIndex(Section->Name); - Section->finalize(); - } - - for (auto &Segment : Segments) - Segment->finalize(); -} - template void Object::writeHeader(FileOutputBuffer &Out) const { uint8_t *Buf = Out.getBufferStart(); @@ -328,14 +242,151 @@ void Object::writeSectionData(FileOutputBuffer &Out) const { Section->writeSection(Out); } -template void Object::write(FileOutputBuffer &Out) { - writeHeader(Out); - writeProgramHeaders(Out); - writeSectionData(Out); - writeSectionHeaders(Out); +template void ELFObject::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(this->Sections), std::end(this->Sections), + CompareSections); } -template class Object; -template class Object; -template class Object; -template class Object; +template void ELFObject::assignOffsets() { + // Decide file offsets and indexes. + 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 : 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. + if (Section->ParentSegment) { + auto FirstInSeg = Section->ParentSegment->firstSection(); + if (FirstInSeg == Section.get()) { + Offset = alignTo(Offset, Section->ParentSegment->Align); + // There can be gaps at the start of a segment before the first section. + // So first we assign the alignment of the segment and then assign the + // location of the section from there + Section->Offset = + Offset + Section->OriginalOffset - Section->ParentSegment->Offset; + } + // We should respect interstitial gaps of allocated sections. We *must* + // maintain the memory image so that addresses are preserved. As, with the + // exception of SHT_NOBITS sections at the end of segments, the memory + // image is a copy of the file image, we preserve the file image as well. + // There's a strange case where a thread local SHT_NOBITS can cause the + // memory image and file image to not be the same. This occurs, on some + // systems, when a thread local SHT_NOBITS is between two SHT_PROGBITS + // and the thread local SHT_NOBITS section is at the end of a TLS segment. + // In this case to faithfully copy the segment file image we must use + // relative offsets. In any other case this would be the same as using the + // relative addresses so this should maintian the memory image as desired. + Offset = FirstInSeg->Offset + Section->OriginalOffset - + FirstInSeg->OriginalOffset; + } + // Alignment should have already been handled by the above if statement if + // this if this section is in a segment. Technically this shouldn't do + // anything bad if the alignments of the sections are all correct and the + // file image isn't corrupted. Still in sticking with the motto "maintain + // the file image" we should avoid messing up the file image if the + // alignment disagrees with the file image. + if (!Section->ParentSegment && Section->Align) + Offset = alignTo(Offset, Section->Align); + Section->Offset = Offset; + Section->Index = Index++; + if (Section->Type != SHT_NOBITS) + Offset += Section->Size; + } + // 'offset' should now be just after all the section data so we should set the + // section header table offset to be exactly here. This spot might not be + // aligned properly however so we should align it as needed. For 32-bit ELF + // 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)); + this->SHOffset = Offset; +} + +template size_t ELFObject::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 ELFObject::write(FileOutputBuffer &Out) const { + this->writeHeader(Out); + this->writeProgramHeaders(Out); + this->writeSectionData(Out); + this->writeSectionHeaders(Out); +} + +template void ELFObject::finalize() { + for (const auto &Section : this->Sections) { + this->SectionNames->addString(Section->Name); + } + + sortSections(); + assignOffsets(); + + // Finalize SectionNames first so that we can assign name indexes. + this->SectionNames->finalize(); + // Finally now that all offsets and indexes have been set we can finalize any + // remaining issues. + uint64_t Offset = this->SHOffset + sizeof(Elf_Shdr); + for (auto &Section : this->Sections) { + Section->HeaderOffset = Offset; + Offset += sizeof(Elf_Shdr); + Section->NameIndex = this->SectionNames->findIndex(Section->Name); + Section->finalize(); + } + + for (auto &Segment : this->Segments) + Segment->finalize(); +} + +template size_t BinaryObject::totalSize() const { + return TotalSize; +} + +template +void BinaryObject::write(FileOutputBuffer &Out) const { + for (auto &Segment : this->Segments) { + if (Segment->Type == llvm::ELF::PT_LOAD) { + Segment->writeSegment(Out); + } + } +} + +template void BinaryObject::finalize() { + for (auto &Segment : this->Segments) + Segment->finalize(); + + // Put all segments in offset order. + auto CompareSegments = [](const SegPtr &A, const SegPtr &B) { + return A->Offset < B->Offset; + }; + std::sort(std::begin(this->Segments), std::end(this->Segments), + CompareSegments); + + uint64_t Offset = 0; + for (auto &Segment : this->Segments) { + if (Segment->Type == llvm::ELF::PT_LOAD) { + Offset = alignTo(Offset, Segment->Align); + Segment->Offset = Offset; + Offset += Segment->FileSize; + } + } + TotalSize = Offset; +} + +template class ELFObject; +template class ELFObject; +template class ELFObject; +template class ELFObject; + +template class BinaryObject; +template class BinaryObject; +template class BinaryObject; +template class BinaryObject; diff --git a/tools/llvm-objcopy/Object.h b/tools/llvm-objcopy/Object.h index d743efbde65..b505e60b382 100644 --- a/tools/llvm-objcopy/Object.h +++ b/tools/llvm-objcopy/Object.h @@ -58,6 +58,7 @@ private: }; std::set Sections; + llvm::ArrayRef Contents; public: uint64_t Align; @@ -70,6 +71,7 @@ public: 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 @@ public: } void addSection(const SectionBase *sec) { Sections.insert(sec); } template void writeHeader(llvm::FileOutputBuffer &Out) const; + void writeSegment(llvm::FileOutputBuffer &Out) const; }; class Section : public SectionBase { @@ -117,16 +120,16 @@ private: typedef typename ELFT::Ehdr Elf_Ehdr; typedef typename ELFT::Phdr Elf_Phdr; - StringTableSection *SectionNames; - std::vector Sections; - std::vector Segments; - - void sortSections(); - void assignOffsets(); 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 writeHeader(llvm::FileOutputBuffer &Out) const; void writeProgramHeaders(llvm::FileOutputBuffer &Out) const; void writeSectionData(llvm::FileOutputBuffer &Out) const; @@ -142,9 +145,43 @@ public: 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) const = 0; + virtual ~Object() = default; }; +template class ELFObject : 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: + ELFObject(const llvm::object::ELFObjectFile &Obj) : Object(Obj) {} + void finalize() override; + size_t totalSize() const override; + void write(llvm::FileOutputBuffer &Out) const override; +}; + +template class BinaryObject : public Object { +private: + typedef std::unique_ptr SecPtr; + typedef std::unique_ptr SegPtr; + + uint64_t TotalSize; + +public: + BinaryObject(const llvm::object::ELFObjectFile &Obj) + : Object(Obj) {} + void finalize() override; + size_t totalSize() const override; + void write(llvm::FileOutputBuffer &Out) const override; +}; #endif diff --git a/tools/llvm-objcopy/llvm-objcopy.cpp b/tools/llvm-objcopy/llvm-objcopy.cpp index 09edc4c3327..9b233951b8d 100644 --- a/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/tools/llvm-objcopy/llvm-objcopy.cpp @@ -53,13 +53,23 @@ LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, llvm::Error E) { 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 to one of the following:" + "\n\tbinary")); void CopyBinary(const ELFObjectFile &ObjFile) { std::unique_ptr Buffer; - Object Obj{ObjFile}; - Obj.finalize(); + std::unique_ptr> Obj; + if (!OutputFormat.empty() && OutputFormat != "binary") + error("invalid output format '" + OutputFormat + "'"); + + if (!OutputFormat.empty() && OutputFormat == "binary") + Obj = llvm::make_unique>(ObjFile); + else + Obj = llvm::make_unique>(ObjFile); + Obj->finalize(); ErrorOr> BufferOrErr = - FileOutputBuffer::create(OutputFilename, Obj.totalSize(), + FileOutputBuffer::create(OutputFilename, Obj->totalSize(), FileOutputBuffer::F_executable); if (BufferOrErr.getError()) error("failed to open " + OutputFilename); @@ -68,7 +78,7 @@ void CopyBinary(const ELFObjectFile &ObjFile) { std::error_code EC; if (EC) report_fatal_error(EC.message()); - Obj.write(*Buffer); + Obj->write(*Buffer); if (auto EC = Buffer->commit()) reportError(OutputFilename, EC); }