Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -61,6 +61,7 @@ llvm-modextract llvm-mt llvm-nm + llvm-objcopy llvm-objdump llvm-opt-report llvm-pdbutil Index: test/lit.cfg =================================================================== --- test/lit.cfg +++ test/lit.cfg @@ -302,6 +302,7 @@ r"\bllvm-mcmarkup\b", r"\bllvm-modextract\b", r"\bllvm-nm\b", + r"\bllvm-objcopy\b", r"\bllvm-objdump\b", r"\bllvm-pdbutil\b", r"\bllvm-profdata\b", Index: test/tools/llvm-objcopy/basic-copy.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/basic-copy.test @@ -0,0 +1,47 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-objcopy %t %t2 +# RUN: llvm-readobj -sections %t2 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000010 + Size: 64 + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: "00000000" + +# CHECK: Type: SHT_NULL + +# CHECK: Name: .bss +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: [[OFFSET:0x[0-9A-F]+]] +# CHECK-NEXT: Size: 64 + +# CHECK: Name: .text +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_EXECINSTR +# CHECK-NEXT: ] +# 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/empty-section.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/empty-section.test @@ -0,0 +1,55 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-objcopy %t %t2 +# RUN: llvm-readobj -sections %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 ] + Address: 0x0 + AddressAlign: 0x0000000000001000 + Content: "00000000" + - Name: .empty + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1000 + AddressAlign: 0x0000000000001000 + Content: "" + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1000 + AddressAlign: 0x0000000000001000 + Content: "00000000" + + +# CHECK: Name: .text +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_EXECINSTR +# CHECK-NEXT: ] + +# CHECK: Name: .empty +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x1000 +# CHECK-NEXT: Offset: 0x2000 +# CHECK-NEXT: Size: 0 + +# CHECK: Name: .data +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x1000 +# CHECK-NEXT: Offset: 0x2000 +# CHECK-NEXT: Size: 4 Index: test/tools/llvm-objcopy/program-headers.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/program-headers.test @@ -0,0 +1,71 @@ +# 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 ] + Address: 0xAAAA1000 + AddressAlign: 0x0000000000001000 + Content: "00000000" + - Name: .init + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Content: "00000000" + Address: 0xAAAA1010 + AddressAlign: 0x0000000000000010 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: "00000000" + Address: 0xAAAA2000 + 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/LLVMBuild.txt =================================================================== --- tools/LLVMBuild.txt +++ tools/LLVMBuild.txt @@ -40,6 +40,7 @@ llvm-modextract llvm-mt llvm-nm + llvm-objcopy llvm-objdump llvm-pdbutil llvm-profdata Index: tools/llvm-objcopy/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-objcopy/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS + Object + Support + MC + ) +add_llvm_tool(llvm-objcopy + llvm-objcopy.cpp + Object.cpp + ) Index: tools/llvm-objcopy/LLVMBuild.txt =================================================================== --- /dev/null +++ tools/llvm-objcopy/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./tools/llvm-objcopy/LLVMBuild.txt -----------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; +[component_0] +type = Tool +name = llvm-objcopy +parent = Tools +required_libraries = Object MC Index: tools/llvm-objcopy/Object.h =================================================================== --- /dev/null +++ tools/llvm-objcopy/Object.h @@ -0,0 +1,150 @@ +//===- Object.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJCOPY_OBJECT_H +#define LLVM_OBJCOPY_OBJECT_H + +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/FileOutputBuffer.h" + +#include +#include + +class Segment; + +class SectionBase { +public: + llvm::StringRef Name; + Segment *ParentSegment = nullptr; + uint64_t HeaderOffset; + uint64_t OriginalOffset; + uint32_t Index; + + uint64_t Addr = 0; + uint64_t Align = 1; + uint32_t EntrySize = 0; + uint64_t Flags = 0; + uint64_t Info = 0; + uint64_t Link = llvm::ELF::SHN_UNDEF; + uint64_t NameIndex = 0; + uint64_t Offset = 0; + uint64_t Size = 0; + uint64_t Type = llvm::ELF::SHT_NULL; + + virtual ~SectionBase() {} + virtual void finalize(); + template void writeHeader(llvm::FileOutputBuffer &Out) const; + virtual void writeSection(llvm::FileOutputBuffer &Out) const = 0; +}; + +class Segment { +private: + struct SectionCompare { + bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const { + // Some sections might have the same address if one of them is empty. To + // fix this we can use the lexicographic ordering on ->Addr and the + // address of the actully stored section. + if (Lhs->OriginalOffset == Rhs->OriginalOffset) + return Lhs < Rhs; + return Lhs->OriginalOffset < Rhs->OriginalOffset; + } + }; + + std::set Sections; + +public: + uint64_t Align; + uint64_t FileSize; + uint32_t Flags; + uint32_t Index; + uint64_t MemSize; + uint64_t Offset; + uint64_t PAddr; + uint64_t Type; + uint64_t VAddr; + + void finalize(); + const SectionBase *firstSection() const { + if (!Sections.empty()) + return *Sections.begin(); + return nullptr; + } + void addSection(const SectionBase *sec) { Sections.insert(sec); } + template void writeHeader(llvm::FileOutputBuffer &Out) const; +}; + +class Section : public SectionBase { +private: + llvm::ArrayRef Contents; + +public: + Section(llvm::ArrayRef Data) : Contents(Data) {} + void writeSection(llvm::FileOutputBuffer &Out) const override; +}; + +// This is just a wraper around a StringTableBuilder that implements SectionBase +class StringTableSection : public SectionBase { +private: + llvm::StringTableBuilder StrTabBuilder; + +public: + StringTableSection() : StrTabBuilder(llvm::StringTableBuilder::ELF) { + Type = llvm::ELF::SHT_STRTAB; + } + + void addString(llvm::StringRef Name); + uint32_t findIndex(llvm::StringRef Name) const; + void finalize() override; + void writeSection(llvm::FileOutputBuffer &Out) const override; + static bool classof(const SectionBase *S) { + return S->Type == llvm::ELF::SHT_STRTAB; + } +}; + +template class 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; + + 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); + 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]; + uint64_t Entry; + uint64_t SHOffset; + uint32_t Type; + uint32_t Machine; + uint32_t Version; + uint32_t Flags; + + Object(const llvm::object::ELFObjectFile &Obj); + size_t totalSize() const; + void finalize(); + void write(llvm::FileOutputBuffer &Out); +}; + +#endif Index: tools/llvm-objcopy/Object.cpp =================================================================== --- /dev/null +++ tools/llvm-objcopy/Object.cpp @@ -0,0 +1,341 @@ +//===- Object.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "Object.h" +#include "llvm-objcopy.h" + +using namespace llvm; +using namespace object; +using namespace ELF; + +template void Segment::writeHeader(FileOutputBuffer &Out) const { + typedef typename ELFT::Ehdr Elf_Ehdr; + typedef typename ELFT::Phdr Elf_Phdr; + + uint8_t *Buf = Out.getBufferStart(); + Buf += sizeof(Elf_Ehdr) + Index * sizeof(Elf_Phdr); + Elf_Phdr &Phdr = *reinterpret_cast(Buf); + Phdr.p_type = Type; + Phdr.p_flags = Flags; + Phdr.p_offset = Offset; + Phdr.p_vaddr = VAddr; + Phdr.p_paddr = PAddr; + Phdr.p_filesz = FileSize; + Phdr.p_memsz = MemSize; + Phdr.p_align = Align; +} + +void Segment::finalize() { + auto FirstSec = firstSection(); + if (FirstSec) { + // It is possible for a gap to be at the begining of a segment. Because of + // this we need to compute the new offset based on how large this gap was + // in the source file. Section layout should have already ensured that this + // space is not used for something else. + uint64_t OriginalOffset = Offset; + Offset = FirstSec->Offset - (FirstSec->OriginalOffset - OriginalOffset); + } +} + +void SectionBase::finalize() {} + +template +void SectionBase::writeHeader(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart(); + Buf += HeaderOffset; + typename ELFT::Shdr &Shdr = *reinterpret_cast(Buf); + Shdr.sh_name = NameIndex; + Shdr.sh_type = Type; + Shdr.sh_flags = Flags; + Shdr.sh_addr = Addr; + Shdr.sh_offset = Offset; + Shdr.sh_size = Size; + Shdr.sh_link = Link; + Shdr.sh_info = Info; + Shdr.sh_addralign = Align; + Shdr.sh_entsize = EntrySize; +} + +void Section::writeSection(FileOutputBuffer &Out) const { + if (Type == SHT_NOBITS) + return; + uint8_t *Buf = Out.getBufferStart() + Offset; + std::copy(std::begin(Contents), std::end(Contents), Buf); +} + +void StringTableSection::addString(StringRef Name) { + StrTabBuilder.add(Name); + Size = StrTabBuilder.getSize(); +} + +uint32_t StringTableSection::findIndex(StringRef Name) const { + return StrTabBuilder.getOffset(Name); +} + +void StringTableSection::finalize() { StrTabBuilder.finalize(); } + +void StringTableSection::writeSection(FileOutputBuffer &Out) const { + StrTabBuilder.write(Out.getBufferStart() + Offset); +} + +// Returns true IFF a section is wholly inside the range of a segment +static bool sectionWithinSegment(const SectionBase &Section, + const Segment &Segment) { + // If a section is empty it should be treated like it has a size of 1. This is + // to clarify the case when an empty section lies on a boundary between two + // segments and ensures that the section "belongs" to the second segment and + // not the first. + uint64_t SecSize = Section.Size ? Section.Size : 1; + return Segment.Offset <= Section.OriginalOffset && + Segment.Offset + Segment.FileSize >= Section.OriginalOffset + SecSize; +} + +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()); + Segment &Seg = *Segments.back(); + Seg.Type = Phdr.p_type; + Seg.Flags = Phdr.p_flags; + Seg.Offset = Phdr.p_offset; + Seg.VAddr = Phdr.p_vaddr; + Seg.PAddr = Phdr.p_paddr; + Seg.FileSize = Phdr.p_filesz; + Seg.MemSize = Phdr.p_memsz; + Seg.Align = Phdr.p_align; + Seg.Index = Index++; + for (auto &Section : Sections) { + if (sectionWithinSegment(*Section, Seg)) { + Seg.addSection(&*Section); + if (!Section->ParentSegment || + Section->ParentSegment->Offset > Seg.Offset) { + Section->ParentSegment = &Seg; + } + } + } + } +} + +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 llvm::make_unique(); + case SHT_NOBITS: + return llvm::make_unique
(Data); + default: + Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); + return llvm::make_unique
(Data); + }; +} + +template +void Object::readSectionHeaders(const ELFFile &ElfFile) { + uint32_t Index = 0; + for (const auto &Shdr : unwrapOrError(ElfFile.sections())) { + if (Index == 0) { + ++Index; + continue; + } + SecPtr Sec = makeSection(ElfFile, Shdr); + Sec->Name = unwrapOrError(ElfFile.getSectionName(&Shdr)); + Sec->Type = Shdr.sh_type; + Sec->Flags = Shdr.sh_flags; + Sec->Addr = Shdr.sh_addr; + Sec->Offset = Shdr.sh_offset; + Sec->OriginalOffset = Shdr.sh_offset; + Sec->Size = Shdr.sh_size; + Sec->Link = Shdr.sh_link; + Sec->Info = Shdr.sh_info; + Sec->Align = Shdr.sh_addralign; + Sec->EntrySize = Shdr.sh_entsize; + Sec->Index = Index++; + 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(); + + std::copy(Ehdr.e_ident, Ehdr.e_ident + 16, Ident); + Type = Ehdr.e_type; + Machine = Ehdr.e_machine; + Version = Ehdr.e_version; + Entry = Ehdr.e_entry; + Flags = Ehdr.e_flags; + + readSectionHeaders(ElfFile); + readProgramHeaders(ElfFile); + + SectionNames = + 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(); + 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::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 +void Object::writeSectionData(FileOutputBuffer &Out) const { + for (auto &Section : Sections) + Section->writeSection(Out); +} + +template void Object::write(FileOutputBuffer &Out) { + writeHeader(Out); + writeProgramHeaders(Out); + writeSectionData(Out); + writeSectionHeaders(Out); +} + +template class Object; +template class Object; +template class Object; +template class Object; Index: tools/llvm-objcopy/llvm-objcopy.h =================================================================== --- /dev/null +++ tools/llvm-objcopy/llvm-objcopy.h @@ -0,0 +1,32 @@ +//===- llvm-objcopy.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_OBJCOPY_H +#define LLVM_OBJCOPY_H + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Error.h" + +namespace llvm { + +LLVM_ATTRIBUTE_NORETURN extern void error(Twine Message); + +// This is taken from llvm-readobj. +// [see here](llvm/tools/llvm-readobj/llvm-readobj.h:38) +template T unwrapOrError(Expected EO) { + if (EO) + return *EO; + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(EO.takeError(), OS, ""); + OS.flush(); + error(Buf); +} +} + +#endif Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- /dev/null +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -0,0 +1,96 @@ +//===- llvm-objcopy.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm-objcopy.h" +#include "Object.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/ToolOutputFile.h" + +#include +#include +#include + +using namespace llvm; +using namespace object; +using namespace ELF; + +// The name this program was invoked as. +static StringRef ToolName; + +namespace llvm { + +LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { + errs() << ToolName << ": " << Message << ".\n"; + errs().flush(); + exit(1); +} + +LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { + assert(EC); + errs() << ToolName << ": '" << File << "': " << EC.message() << ".\n"; + exit(1); +} + +LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, llvm::Error E) { + assert(E); + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(std::move(E), OS, ""); + OS.flush(); + errs() << ToolName << ": '" << File << "': " << Buf; + exit(1); +} +} + +cl::opt InputFilename(cl::Positional, cl::desc("")); +cl::opt OutputFilename(cl::Positional, cl::desc(""), + cl::init("-")); + +void CopyBinary(const ELFObjectFile &ObjFile) { + std::unique_ptr Buffer; + Object Obj{ObjFile}; + Obj.finalize(); + ErrorOr> BufferOrErr = + FileOutputBuffer::create(OutputFilename, Obj.totalSize(), + FileOutputBuffer::F_executable); + if (BufferOrErr.getError()) + error("failed to open " + OutputFilename); + else + Buffer = std::move(*BufferOrErr); + std::error_code EC; + if (EC) + report_fatal_error(EC.message()); + Obj.write(*Buffer); + if (auto EC = Buffer->commit()) + reportError(OutputFilename, EC); +} + +int main(int argc, char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(argv[0]); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, "llvm objcopy utility\n"); + ToolName = argv[0]; + if (InputFilename.empty()) { + cl::PrintHelpMessage(); + return 2; + } + Expected> BinaryOrErr = createBinary(InputFilename); + if (!BinaryOrErr) + reportError(InputFilename, BinaryOrErr.takeError()); + Binary &Binary = *BinaryOrErr.get().getBinary(); + if (ELFObjectFile *o = dyn_cast>(&Binary)) { + CopyBinary(*o); + return 0; + } + reportError(InputFilename, object_error::invalid_file_type); +}