Index: test/tools/llvm-objcopy/basic-copy.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/basic-copy.test @@ -0,0 +1,43 @@ +# 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 ] + AddressAlign: 0x0000000000000010 + Content: "00000000" + +# 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: +# CHECK-NEXT: Size: 4 + +# CHECK: Type: SHT_NULL + +# CHECK: Name: .symtab +# CHECK-NEXT: Type: SHT_SYMTAB +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] + +# CHECK: Name: .strtab +# CHECK-NEXT: Type: SHT_STRTAB +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] + +# CHECK: Name: .shstrtab +# CHECK-NEXT: Type: SHT_STRTAB +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] Index: tools/LLVMBuild.txt =================================================================== --- tools/LLVMBuild.txt +++ tools/LLVMBuild.txt @@ -39,6 +39,7 @@ llvm-mcmarkup llvm-modextract llvm-nm + llvm-objcopy llvm-objdump llvm-pdbdump llvm-profdata Index: tools/llvm-objcopy/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-objcopy/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Core + Object + Support + ) + +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,22 @@ + +;===- ./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 Index: tools/llvm-objcopy/Object.h =================================================================== --- /dev/null +++ tools/llvm-objcopy/Object.h @@ -0,0 +1,132 @@ +//===- Object.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// 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/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/TargetSelect.h" + +class StringTableSection; + +class SectionBase { +public: + llvm::StringRef Name; + llvm::ELF::Elf64_Word NameIndex; + llvm::ELF::Elf64_Word Type; + llvm::ELF::Elf64_Xword Flags; + llvm::ELF::Elf64_Xword Size; + llvm::ELF::Elf64_Xword Link; + llvm::ELF::Elf64_Addr Addr; + llvm::ELF::Elf64_Off Offset; + llvm::ELF::Elf64_Off Info; + llvm::ELF::Elf64_Xword Align; + llvm::ELF::Elf64_Word EntrySize; + llvm::ELF::Elf64_Half Index; + llvm::ELF::Elf64_Off HeaderOffset; + + virtual ~SectionBase() {} + // TODO: Change initlize so that it allows for retriving link/info SectionBase + // pointers. + virtual void initlize( + const llvm::object::ELFFile &elfFile, + const llvm::object::ELF64LE::Shdr &) = 0; + virtual void finalize() = 0; + virtual void finalizeSize(); + void writeHeader(llvm::FileOutputBuffer &) const; + virtual void writeSection(llvm::FileOutputBuffer &) const = 0; +}; + +class Segment { +private: + std::vector Sections; + +public: + llvm::ELF::Elf64_Word Type; + llvm::ELF::Elf64_Off Offset; + llvm::ELF::Elf64_Addr VAddr; + llvm::ELF::Elf64_Xword FileSize; + llvm::ELF::Elf64_Xword MemSize; + llvm::ELF::Elf64_Word Flags; + llvm::ELF::Elf64_Xword Align; + uint32_t Index; + + void addSection(const SectionBase* sec) { Sections.push_back(sec); } + void writeHeader(llvm::FileOutputBuffer &) const; + void finalize(); +}; + +class Section : public SectionBase { +private: + llvm::ArrayRef Data; + +public: + virtual void initlize( + const llvm::object::ELFFile &, + const llvm::object::ELF64LE::Shdr &) override; + void finalize() override; + void writeSection(llvm::FileOutputBuffer &) const override; +}; + +class StringTableSection : public SectionBase { +private: + std::map Strings; + +public: + void addString(llvm::StringRef); + void removeString(llvm::StringRef); + llvm::ELF::Elf64_Word findIndex(llvm::StringRef) const; + virtual void initlize( + const llvm::object::ELFFile &, + const llvm::object::ELF64LE::Shdr &) override; + void finalizeSize() override; + void finalize() override; + void writeSection(llvm::FileOutputBuffer &) const override; + static bool classof(const SectionBase *S) { + return S->Type == llvm::ELF::SHT_STRTAB; + } +}; + +class Object { +private: + typedef std::unique_ptr SecPtr; + + StringTableSection *SectionNames; + std::vector Sections; + llvm::SmallVector Segments; + + SecPtr constructSection(llvm::ELF::Elf64_Word); + void readProgramHeaders(const llvm::object::ELFFile &); + void readSectionHeaders(const llvm::object::ELFFile &); + void writeHeader(llvm::FileOutputBuffer &) const; + void writeProgramHeaders(llvm::FileOutputBuffer &) const; + void writeSectionData(llvm::FileOutputBuffer &) const; + void writeSectionHeaders(llvm::FileOutputBuffer &) const; + +public: + + uint8_t Ident[16]; + llvm::ELF::Elf64_Half Type; + llvm::ELF::Elf64_Half Machine; + llvm::ELF::Elf64_Word Version; + llvm::ELF::Elf64_Addr Entry; + llvm::ELF::Elf64_Off SHOffset; + llvm::ELF::Elf64_Word Flags; + + Object(const llvm::object::ELFObjectFile&); + size_t totalSize() const; + void finalize(); + void write(llvm::FileOutputBuffer &); +}; + +#endif Index: tools/llvm-objcopy/Object.cpp =================================================================== --- /dev/null +++ tools/llvm-objcopy/Object.cpp @@ -0,0 +1,298 @@ +//===- Object.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// 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; + +void Segment::writeHeader(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart(); + Buf += sizeof(Elf64_Ehdr) + Index * sizeof(ELF64LE::Phdr); + ELF64LE::Phdr &Phdr = *reinterpret_cast(Buf); + Phdr.p_type = Type; + Phdr.p_flags = Flags; + Phdr.p_offset = Offset; + Phdr.p_vaddr = VAddr; + Phdr.p_paddr = VAddr; // TODO: add PAddr to Segment + Phdr.p_filesz = FileSize; + Phdr.p_memsz = MemSize; + Phdr.p_align = Align; +} + +void Segment::finalize() { + auto CompOffset = [](const SectionBase *a, const SectionBase *b) -> bool { + return a->Offset < b->Offset; + }; + auto MinElem = + std::min_element(std::begin(Sections), std::end(Sections), CompOffset); + Offset = (**MinElem).Offset; + FileSize = 0; + for (auto Section : Sections) + if (Section->Type != SHT_NOBITS) + FileSize += Section->Size; +} + +void SectionBase::finalizeSize() { } + +void SectionBase::writeHeader(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart(); + Buf += HeaderOffset; + ELF64LE::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::initlize( + const llvm::object::ELFFile &ElfFile, + const llvm::object::ELF64LE::Shdr &Shdr) { + Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); +} + +void Section::finalize() {} + +void Section::writeSection(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart() + Offset; + std::copy(std::begin(Data), std::end(Data), Buf); +} + +void StringTableSection::addString(StringRef Name) { Strings[Name] = 0; } +void StringTableSection::removeString(StringRef Name) { Strings.erase(Name); } +Elf64_Word StringTableSection::findIndex(StringRef Name) const { + auto Iter = Strings.find(Name); + if(Iter == std::end(Strings)) + error("Invalid string search: " + Name); + return Iter->second; +} +// This function has some warts to it. There's a raw while loop and a +// reinterpret_cast. +void StringTableSection::initlize( + const llvm::object::ELFFile & ElfFile, + const llvm::object::ELF64LE::Shdr &Shdr) { + ArrayRef Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); + auto Iter = std::begin(Data); + auto End = std::end(Data); + while (Iter < End) { + auto End = std::find(Iter, Data.end(), '\0'); + addString(StringRef(reinterpret_cast(&*Iter), End - Iter)); + ++End; + Iter = End; + } +} +void StringTableSection::finalizeSize() { + Size = 0; + for (auto &Name : Strings) { + Name.second = Size; + // We need to add one on for the null character + Size += Name.first.size() + 1; + } +} + +// Nothing needs to be done since finalizeSize was already called +void StringTableSection::finalize() {} + +void StringTableSection::writeSection(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart() + Offset; + for (const auto &Name : Strings) { + Buf = std::copy(std::begin(Name.first), std::end(Name.first), Buf); + // We need to set the null character and then increment the buffer past it + *Buf = 0; + Buf++; + } +} + +std::unique_ptr Object::constructSection(Elf64_Word Type) { + if (Type == SHT_STRTAB) + return make_unique(); + return make_unique
(); +} + +void Object::readProgramHeaders(const ELFFile &ElfFile) { + uint32_t Index = 0; + for (const auto &Phdr : unwrapOrError(ElfFile.program_headers())) { + Segment Seg; + Seg.Type = Phdr.p_type; + Seg.Flags = Phdr.p_flags; + Seg.Offset = Phdr.p_offset; + Seg.VAddr = Phdr.p_vaddr; + Seg.FileSize = Phdr.p_filesz; + Seg.MemSize = Phdr.p_memsz; + Seg.Align = Phdr.p_align; + Seg.Index = Index; + Index++; + Segments.push_back(Seg); + } +} + +void Object::readSectionHeaders(const ELFFile &ElfFile) { + Elf64_Word Index = 0; + for (const auto &Shdr : unwrapOrError(ElfFile.sections())) { + SecPtr Section = constructSection(Shdr.sh_type); + Section->Name = unwrapOrError(ElfFile.getSectionName(&Shdr)); + Section->Type = Shdr.sh_type; + Section->Flags = Shdr.sh_flags; + Section->Addr = Shdr.sh_addr; + Section->Offset = Shdr.sh_offset; + Section->Size = Shdr.sh_size; + Section->Link = Shdr.sh_link; + Section->Info = Shdr.sh_info; + Section->Align = Shdr.sh_addralign; + Section->EntrySize = Shdr.sh_entsize; + Section->Index = Index; + Index++; + Section->initlize(ElfFile, Shdr); + for (auto &Phdr : Segments) { + if (Phdr.Offset < Section->Offset && + Phdr.Offset + Phdr.FileSize > Section->Offset) { + Phdr.addSection(&*Section); + break; + } + } + Sections.push_back(std::move(Section)); + } +} + +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(ELF64LE::Shdr); +} + +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; + + readProgramHeaders(ElfFile); + readSectionHeaders(ElfFile); + + // If there is a .shstrtab section we have read it in by now. Now we want to + // set the appropriete pointer. + if (Ehdr.e_shstrndx != SHN_UNDEF) + SectionNames = dyn_cast(&*Sections[Ehdr.e_shstrndx]); + else + SectionNames = nullptr; +} + +void Object::finalize() { + // Put allocated sections first in address order. + // Maintain ordering of previous non-allocated sections. + auto CompareSections = [](const SecPtr &a, const SecPtr &b) { + if (a->Flags & SHF_ALLOC) { + if (b->Flags & SHF_ALLOC) + return a->Addr < b->Addr; + return true; + } + return a->Index < b->Index; + }; + std::sort(std::begin(Sections), std::end(Sections), CompareSections); + + // Ungracefully handle an annoying optimization. + for (const auto &Section : Sections) { + // Sometimes a bit of string compression occurs on section names that reuse + // part of a larger section name for a smaller section name. For instance + // ".got.plt" can be used for the name index of ".got.plt" and ".plt". + // The quickest fix for this is to add all the names before finalization. + SectionNames->addString(Section->Name); + } + + // The size of each section must be finalized before offsets and indexs can + // be decided. + for (auto &Section : Sections) + Section->finalizeSize(); + + // Decide file offsets and indexs + size_t PhdrSize = Segments.size() * sizeof(ELF64LE::Phdr); + // After the header and the program headers we can put section data. + Elf64_Off Offset = sizeof(ELF64LE::Ehdr) + PhdrSize; + Elf64_Word Index = 0; + for (auto &Section : Sections) { + Section->Offset = Offset; + Section->Index = Index; + if (Section->Type != SHT_NOBITS) + Offset += Section->Size; + Index++; + } + + // 'offset' should now be just after all the section data so we should set the + // section header table offset to be exactly here + SHOffset = Offset; + + // If there is a SectionNames finalize it first so that we can assign name + // indexes. + if(SectionNames) SectionNames->finalize(); + + // Finally now that all offsets and indexes have been set we can finalize any + // reamining issues. + for (auto &Section : Sections) { + Section->HeaderOffset = Offset; + Offset += sizeof(ELF64LE::Shdr); + if(SectionNames) + Section->NameIndex = SectionNames->findIndex(Section->Name); + Section->finalize(); + } +} + +void Object::writeHeader(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart(); + ELF64LE::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(ELF64LE::Ehdr); + Ehdr.e_shoff = SHOffset; + Ehdr.e_flags = Flags; + Ehdr.e_ehsize = sizeof(ELF64LE::Ehdr); + Ehdr.e_phentsize = sizeof(ELF64LE::Phdr); + Ehdr.e_phnum = Segments.size(); + Ehdr.e_shentsize = sizeof(ELF64LE::Shdr); + Ehdr.e_shnum = Sections.size(); + Ehdr.e_shstrndx = SectionNames->Index; +} + +void Object::writeProgramHeaders(FileOutputBuffer &Out) const { + for (auto &Phdr : Segments) + Phdr.writeHeader(Out); +} + +void Object::writeSectionHeaders(FileOutputBuffer &Out) const { + for (auto &Section : Sections) + Section->writeHeader(Out); +} + +void Object::writeSectionData(FileOutputBuffer &Out) const { + for (auto &Section : Sections) + Section->writeSection(Out); +} + +void Object::write(FileOutputBuffer &Out) { + writeHeader(Out); + writeProgramHeaders(Out); + writeSectionData(Out); + writeSectionHeaders(Out); +} Index: tools/llvm-objcopy/llvm-objcopy.h =================================================================== --- /dev/null +++ tools/llvm-objcopy/llvm-objcopy.h @@ -0,0 +1,31 @@ +//===- llvm-objcopy.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// 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 + +namespace llvm { + +extern LLVM_ATTRIBUTE_NORETURN 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,113 @@ +//===- llvm-objcopy.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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 "Object.h" +#include "llvm-objcopy.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 (auto EC = BufferOrErr.getError()) + error("failed to open " + OutputFilename); + else + Buffer = std::move(*BufferOrErr); + + std::error_code EC; + std::unique_ptr Out = + make_unique(OutputFilename.data(), EC, sys::fs::F_None); + if (EC) + report_fatal_error(EC.message()); + // now if the program fails for any reason the output file will be deleted + + Obj.write(*Buffer); + if (auto EC = Buffer->commit()) + reportError(OutputFilename, EC); + Out->keep(); +} + +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); + } else + reportError(InputFilename, object_error::invalid_file_type); + + return 0; +}