Index: test/tools/llvm-objcopy/basic-copy.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/basic-copy.test @@ -0,0 +1,37 @@ +# 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 @@ -38,6 +38,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,9 @@ +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,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 Index: tools/llvm-objcopy/Object.h =================================================================== --- /dev/null +++ tools/llvm-objcopy/Object.h @@ -0,0 +1,147 @@ +//===- 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/ADT/iterator.h" +#include "llvm/ADT/iterator_range.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" + +#include +#include + +class Segment; + +class SectionBase { +public: + llvm::StringRef Name; + Segment* ParrentSegment; + uint64_t HeaderOffset; + uint32_t Index; + + uint64_t NameIndex; + uint64_t Type; + uint64_t Flags; + uint64_t Size; + uint64_t Link; + uint64_t Addr; + uint64_t Offset; + uint64_t Info; + uint64_t Align; + uint32_t EntrySize; + + virtual ~SectionBase() {} + virtual void finalize(); + template + void writeHeader(llvm::FileOutputBuffer &) const; + virtual void writeSection(llvm::FileOutputBuffer &) const = 0; +}; + +class Segment { +private: + struct SectionCompare { + bool operator()(const SectionBase * lhs, const SectionBase * rhs) const { + return lhs->Addr < rhs->Addr; + } + }; + + std::set Sections; + +public: + uint64_t Type; + uint64_t Offset; + uint64_t VAddr; + uint64_t FileSize; + uint64_t MemSize; + uint64_t Align; + uint32_t Flags; + uint32_t Index; + + void finalize(); + const SectionBase * firstSection() const { + if(Sections.size()) + return *Sections.begin(); + return nullptr; + } + void addSection(const SectionBase *sec) { Sections.insert(sec); } + template + void writeHeader(llvm::FileOutputBuffer &) const; +}; + +class Section : public SectionBase { +private: + llvm::ArrayRef Contents; + +public: + Section(llvm::ArrayRef Data) : Contents(Data) {} + void writeSection(llvm::FileOutputBuffer &) const override; +}; + +class StringTableSection : public SectionBase { +private: + std::map Strings; + +public: + StringTableSection() { + Type = llvm::ELF::SHT_STRTAB; + Flags = 0; + Size = 0; + Link = 0; + Info = 0; + Align = 1; + EntrySize = 0; + } + + void addString(llvm::StringRef); + void removeString(llvm::StringRef); + uint32_t findIndex(llvm::StringRef) const; + void finalize() override; + void writeSection(llvm::FileOutputBuffer &) const override; + static bool classof(const SectionBase *S) { + return S->Type == llvm::ELF::SHT_STRTAB; + } +}; + +template +class Object { +private: + typedef std::unique_ptr SecPtr; + StringTableSection *SectionNames; + std::vector Sections; + std::vector Segments; + + void sortSections(); + void assignOffsets(); + 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]; + uint64_t Entry; + uint64_t SHOffset; + uint32_t Type; + uint32_t Machine; + uint32_t Version; + uint32_t 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,303 @@ +//===- 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; + +template +void Segment::writeHeader(FileOutputBuffer &Out) const { + uint8_t *Buf = Out.getBufferStart(); + Buf += sizeof(typename ELFT::Ehdr) + Index * sizeof(typename ELFT::Phdr); + typename ELFT::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::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 { + uint8_t *Buf = Out.getBufferStart() + Offset; + std::copy(std::begin(Contents), std::end(Contents), Buf); +} + +void StringTableSection::addString(StringRef Name) { + auto res = Strings.emplace(Name, 0); + // We need to account for the null character as well + if (res.second) + Size += Name.size() + 1; +} + +void StringTableSection::removeString(StringRef Name) { + size_t count = Strings.erase(Name); + // We need to account for the null character as well + if (count) + Size -= (Name.size() + 1); +} + +uint32_t StringTableSection::findIndex(StringRef Name) const { + auto Iter = Strings.find(Name); + if (Iter == std::end(Strings)) + error("Invalid string search: " + Name); + return Iter->second; +} + +void StringTableSection::finalize() { + uint32_t NameIndex = 0; + for (auto &Name : Strings) { + Name.second = NameIndex; + NameIndex += Name.first.size() + 1; + } +} + +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++; + } +} + +template +void Object::readProgramHeaders(const ELFFile &ElfFile) { + uint32_t Index = 0; + for (const auto &Phdr : unwrapOrError(ElfFile.program_headers())) { + Segments.emplace_back(); + 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.FileSize = Phdr.p_filesz; + Seg.MemSize = Phdr.p_memsz; + Seg.Align = Phdr.p_align; + Seg.Index = Index; + Index++; + for (auto &Section : Sections) { + if (Seg.Offset <= Section->Offset && + Seg.Offset + Seg.FileSize >= Section->Offset + Section->Size) { + Seg.addSection(&*Section); + Section->ParrentSegment = &Seg; + } + } + + } +} + +template +void Object::readSectionHeaders(const ELFFile &ElfFile) { + uint32_t Index = 0; + for (const auto &Shdr : unwrapOrError(ElfFile.sections())) { + if (Shdr.sh_type == SHT_STRTAB) + continue; + ArrayRef Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); + SecPtr Sec = make_unique
(Data); + 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->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; + 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(typename ELFT::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; + SectionNames = new StringTableSection(); + SectionNames->Name = ".shstrtab"; + SectionNames->addString(SectionNames->Name); + Sections.emplace_back(SectionNames); + + readSectionHeaders(ElfFile); + readProgramHeaders(ElfFile); +} + +template +void Object::sortSections() { + // Put allocated sections in address order. Maintain ordering as closely as + // possible while meeting that demand however. + auto CompareSections = [](const SecPtr &a, const SecPtr &b) { + if(a->Type == SHT_NULL) return true; + if (a->Flags & SHF_ALLOC && b->Flags & SHF_ALLOC) + return a->Addr < b->Addr; + return a->Index < b->Index; + }; + std::sort(std::begin(Sections), std::end(Sections), CompareSections); +} + +uint64_t align(uint64_t value, uint64_t multiple) { + if(!multiple || value % multiple == 0) return value; + return value + multiple - value % multiple; +} + +template +void Object::assignOffsets() { + // Decide file offsets and indexs + size_t PhdrSize = Segments.size() * sizeof(typename ELFT::Phdr); + // After the header and the program headers we can put section data. + uint64_t Offset = sizeof(typename ELFT::Ehdr) + PhdrSize; + uint64_t Index = 0; + for (auto &Section : 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); + } + Offset = align(Offset, Section->Align); + 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. 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, sizeof(ELFT::Elf64_Word)); + SHOffset = Offset; +} + +template +void Object::finalize() { + 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 + // reamining issues. + uint64_t Offset = SHOffset; + for (auto &Section : Sections) { + Section->HeaderOffset = Offset; + Offset += sizeof(typename ELFT::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(); + 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; + Ehdr.e_phoff = sizeof(typename ELFT::Ehdr); + Ehdr.e_shoff = SHOffset; + Ehdr.e_flags = Flags; + Ehdr.e_ehsize = sizeof(typename ELFT::Ehdr); + Ehdr.e_phentsize = sizeof(typename ELFT::Phdr); + Ehdr.e_phnum = Segments.size(); + Ehdr.e_shentsize = sizeof(typename ELFT::Shdr); + Ehdr.e_shnum = Sections.size(); + 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 { + 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,30 @@ +//===- 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 + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Error.h" +#include + +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 Linker +// +// 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 (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; +}