Index: test/tools/llvm-objcopy/basic-copy.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/basic-copy.test @@ -0,0 +1,33 @@ +# 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: Type: SHT_NULL + +# CHECK: Name: .shstrtab +# CHECK-NEXT: Type: SHT_STRTAB +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] + +# 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 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,142 @@ +//===- 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 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 { + return Lhs->Addr < Rhs->Addr; + } + }; + + std::set Sections; + +public: + uint64_t Align; + uint64_t FileSize; + uint32_t Flags; + uint32_t Index; + uint64_t MemSize; + uint64_t Offset; + uint64_t Type; + uint64_t VAddr; + + 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 &Out) const; +}; + +class Section : public SectionBase { +private: + llvm::ArrayRef Contents; + +public: + Section(llvm::ArrayRef Data) : Contents(Data) {} + void writeSection(llvm::FileOutputBuffer &Out) const override; +}; + +class StringTableSection : public SectionBase { +private: + llvm::StringMap Strings; + +public: + StringTableSection() { Type = llvm::ELF::SHT_STRTAB; } + + void addString(llvm::StringRef Name); + void removeString(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 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(); + 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,296 @@ +//===- 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 MinElem = + std::min_element(std::begin(Sections), std::end(Sections), + [](const SectionBase *a, const SectionBase *b) -> bool { + return a->Offset < b->Offset; + }); + 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.insert(std::make_pair(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)) + llvm_unreachable("Attempt to search for invalid string in string table"); + return Iter->second; +} + +void StringTableSection::finalize() { + uint32_t NameIndex = 0; + for (auto &Name : Strings) { + Name.getValue() = NameIndex; + NameIndex += Name.getKey().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.getKey()), std::end(Name.getKey()), 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++; + 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(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; + 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(Elf_Phdr); + // After the header and the program headers we can put section data. + uint64_t Offset = sizeof(Elf_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 += 4 - Offset % 4; + 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(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(); + 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(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 +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,34 @@ +//===- 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,101 @@ +//===- 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); + return 0; + } + reportError(InputFilename, object_error::invalid_file_type); +}