diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-20 11:41:25 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-20 11:41:25 +0000 |
| commit | d9484dd61cc151c4f34c31e07f693fefa66316b5 (patch) | |
| tree | ab0560b3da293f1fafd3269c59692e929418f5c2 /contrib/llvm/tools/llvm-objcopy | |
| parent | 79e0962d4c3cf1f0acf359a9d69cb3ac68c414c4 (diff) | |
| parent | d8e91e46262bc44006913e6796843909f1ac7bcd (diff) | |
Notes
Diffstat (limited to 'contrib/llvm/tools/llvm-objcopy')
20 files changed, 3176 insertions, 869 deletions
diff --git a/contrib/llvm/tools/llvm-objcopy/Buffer.cpp b/contrib/llvm/tools/llvm-objcopy/Buffer.cpp new file mode 100644 index 000000000000..8044b023aaad --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/Buffer.cpp @@ -0,0 +1,51 @@ +//===- Buffer.cpp ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Buffer.h" +#include "llvm-objcopy.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> + +namespace llvm { +namespace objcopy { + +Buffer::~Buffer() {} + +void FileBuffer::allocate(size_t Size) { + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable); + handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &E) { + error("failed to open " + getName() + ": " + E.message()); + }); + Buf = std::move(*BufferOrErr); +} + +Error FileBuffer::commit() { return Buf->commit(); } + +uint8_t *FileBuffer::getBufferStart() { + return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); +} + +void MemBuffer::allocate(size_t Size) { + Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName()); +} + +Error MemBuffer::commit() { return Error::success(); } + +uint8_t *MemBuffer::getBufferStart() { + return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); +} + +std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() { + return std::move(Buf); +} + +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/Buffer.h b/contrib/llvm/tools/llvm-objcopy/Buffer.h new file mode 100644 index 000000000000..e5b9c5b2d22b --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/Buffer.h @@ -0,0 +1,66 @@ +//===- Buffer.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_TOOLS_OBJCOPY_BUFFER_H +#define LLVM_TOOLS_OBJCOPY_BUFFER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> + +namespace llvm { +namespace objcopy { + +// The class Buffer abstracts out the common interface of FileOutputBuffer and +// WritableMemoryBuffer so that the hierarchy of Writers depends on this +// abstract interface and doesn't depend on a particular implementation. +// TODO: refactor the buffer classes in LLVM to enable us to use them here +// directly. +class Buffer { + StringRef Name; + +public: + virtual ~Buffer(); + virtual void allocate(size_t Size) = 0; + virtual uint8_t *getBufferStart() = 0; + virtual Error commit() = 0; + + explicit Buffer(StringRef Name) : Name(Name) {} + StringRef getName() const { return Name; } +}; + +class FileBuffer : public Buffer { + std::unique_ptr<FileOutputBuffer> Buf; + +public: + void allocate(size_t Size) override; + uint8_t *getBufferStart() override; + Error commit() override; + + explicit FileBuffer(StringRef FileName) : Buffer(FileName) {} +}; + +class MemBuffer : public Buffer { + std::unique_ptr<WritableMemoryBuffer> Buf; + +public: + void allocate(size_t Size) override; + uint8_t *getBufferStart() override; + Error commit() override; + + explicit MemBuffer(StringRef Name) : Buffer(Name) {} + + std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer(); +}; + +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_BUFFER_H diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp new file mode 100644 index 000000000000..6b386d29979c --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -0,0 +1,98 @@ +//===- COFFObjcopy.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "COFFObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "Object.h" +#include "Reader.h" +#include "Writer.h" +#include "llvm-objcopy.h" + +#include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Errc.h" +#include <cassert> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; +using namespace COFF; + +static Error handleArgs(const CopyConfig &Config, Object &Obj) { + // StripAll removes all symbols and thus also removes all relocations. + if (Config.StripAll || Config.StripAllGNU) + for (Section &Sec : Obj.Sections) + Sec.Relocs.clear(); + + // If we need to do per-symbol removals, initialize the Referenced field. + if (Config.StripUnneeded || Config.DiscardAll || + !Config.SymbolsToRemove.empty()) + if (Error E = Obj.markSymbols()) + return E; + + // Actually do removals of symbols. + Obj.removeSymbols([&](const Symbol &Sym) { + // For StripAll, all relocations have been stripped and we remove all + // symbols. + if (Config.StripAll || Config.StripAllGNU) + return true; + + if (is_contained(Config.SymbolsToRemove, Sym.Name)) { + // Explicitly removing a referenced symbol is an error. + if (Sym.Referenced) + reportError(Config.OutputFilename, + make_error<StringError>( + "not stripping symbol '" + Sym.Name + + "' because it is named in a relocation.", + llvm::errc::invalid_argument)); + return true; + } + + if (!Sym.Referenced) { + // With --strip-unneeded, GNU objcopy removes all unreferenced local + // symbols, and any unreferenced undefined external. + if (Config.StripUnneeded && + (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC || + Sym.Sym.SectionNumber == 0)) + return true; + + // GNU objcopy keeps referenced local symbols and external symbols + // if --discard-all is set, similar to what --strip-unneeded does, + // but undefined local symbols are kept when --discard-all is set. + if (Config.DiscardAll && Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && + Sym.Sym.SectionNumber != 0) + return true; + } + + return false; + }); + return Error::success(); +} + +void executeObjcopyOnBinary(const CopyConfig &Config, + object::COFFObjectFile &In, Buffer &Out) { + COFFReader Reader(In); + Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create(); + if (!ObjOrErr) + reportError(Config.InputFilename, ObjOrErr.takeError()); + Object *Obj = ObjOrErr->get(); + assert(Obj && "Unable to deserialize COFF object"); + if (Error E = handleArgs(Config, *Obj)) + reportError(Config.InputFilename, std::move(E)); + COFFWriter Writer(*Obj, Out); + if (Error E = Writer.write()) + reportError(Config.OutputFilename, std::move(E)); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h b/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h new file mode 100644 index 000000000000..bf70bd9b4d84 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h @@ -0,0 +1,31 @@ +//===- COFFObjcopy.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_TOOLS_OBJCOPY_COFFOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H + +namespace llvm { + +namespace object { +class COFFObjectFile; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace coff { +void executeObjcopyOnBinary(const CopyConfig &Config, + object::COFFObjectFile &In, Buffer &Out); + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Object.cpp b/contrib/llvm/tools/llvm-objcopy/COFF/Object.cpp new file mode 100644 index 000000000000..315d3a778623 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Object.cpp @@ -0,0 +1,70 @@ +//===- Object.cpp ---------------------------------------------------------===// +// +// 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 <algorithm> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; + +void Object::addSymbols(ArrayRef<Symbol> NewSymbols) { + for (Symbol S : NewSymbols) { + S.UniqueId = NextSymbolUniqueId++; + Symbols.emplace_back(S); + } + updateSymbols(); +} + +void Object::updateSymbols() { + SymbolMap = DenseMap<size_t, Symbol *>(Symbols.size()); + size_t RawSymIndex = 0; + for (Symbol &Sym : Symbols) { + SymbolMap[Sym.UniqueId] = &Sym; + Sym.RawIndex = RawSymIndex; + RawSymIndex += 1 + Sym.Sym.NumberOfAuxSymbols; + } +} + +const Symbol *Object::findSymbol(size_t UniqueId) const { + auto It = SymbolMap.find(UniqueId); + if (It == SymbolMap.end()) + return nullptr; + return It->second; +} + +void Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { + Symbols.erase( + std::remove_if(std::begin(Symbols), std::end(Symbols), + [ToRemove](const Symbol &Sym) { return ToRemove(Sym); }), + std::end(Symbols)); + updateSymbols(); +} + +Error Object::markSymbols() { + for (Symbol &Sym : Symbols) + Sym.Referenced = false; + for (const Section &Sec : Sections) { + for (const Relocation &R : Sec.Relocs) { + auto It = SymbolMap.find(R.Target); + if (It == SymbolMap.end()) + return make_error<StringError>("Relocation target " + Twine(R.Target) + + " not found", + object_error::invalid_symbol_index); + It->second->Referenced = true; + } + } + return Error::success(); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Object.h b/contrib/llvm/tools/llvm-objcopy/COFF/Object.h new file mode 100644 index 000000000000..7531fb4cf39e --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Object.h @@ -0,0 +1,148 @@ +//===- 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_TOOLS_OBJCOPY_COFF_OBJECT_H +#define LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include <cstddef> +#include <cstdint> +#include <vector> + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Relocation { + Relocation() {} + Relocation(const object::coff_relocation& R) : Reloc(R) {} + + object::coff_relocation Reloc; + size_t Target; + StringRef TargetName; // Used for diagnostics only +}; + +struct Section { + object::coff_section Header; + ArrayRef<uint8_t> Contents; + std::vector<Relocation> Relocs; + StringRef Name; +}; + +struct Symbol { + object::coff_symbol32 Sym; + StringRef Name; + ArrayRef<uint8_t> AuxData; + size_t UniqueId; + size_t RawIndex; + bool Referenced; +}; + +struct Object { + bool IsPE = false; + + object::dos_header DosHeader; + ArrayRef<uint8_t> DosStub; + + object::coff_file_header CoffFileHeader; + + bool Is64 = false; + object::pe32plus_header PeHeader; + uint32_t BaseOfData = 0; // pe32plus_header lacks this field. + + std::vector<object::data_directory> DataDirectories; + std::vector<Section> Sections; + + ArrayRef<Symbol> getSymbols() const { return Symbols; } + // This allows mutating individual Symbols, but not mutating the list + // of symbols itself. + iterator_range<std::vector<Symbol>::iterator> getMutableSymbols() { + return make_range(Symbols.begin(), Symbols.end()); + } + + const Symbol *findSymbol(size_t UniqueId) const; + + void addSymbols(ArrayRef<Symbol> NewSymbols); + void removeSymbols(function_ref<bool(const Symbol &)> ToRemove); + + // Set the Referenced field on all Symbols, based on relocations in + // all sections. + Error markSymbols(); + +private: + std::vector<Symbol> Symbols; + DenseMap<size_t, Symbol *> SymbolMap; + + size_t NextSymbolUniqueId = 0; + + // Update SymbolMap and RawIndex in each Symbol. + void updateSymbols(); +}; + +// Copy between coff_symbol16 and coff_symbol32. +// The source and destination files can use either coff_symbol16 or +// coff_symbol32, while we always store them as coff_symbol32 in the +// intermediate data structure. +template <class Symbol1Ty, class Symbol2Ty> +void copySymbol(Symbol1Ty &Dest, const Symbol2Ty &Src) { + static_assert(sizeof(Dest.Name.ShortName) == sizeof(Src.Name.ShortName), + "Mismatched name sizes"); + memcpy(Dest.Name.ShortName, Src.Name.ShortName, sizeof(Dest.Name.ShortName)); + Dest.Value = Src.Value; + Dest.SectionNumber = Src.SectionNumber; + Dest.Type = Src.Type; + Dest.StorageClass = Src.StorageClass; + Dest.NumberOfAuxSymbols = Src.NumberOfAuxSymbols; +} + +// Copy between pe32_header and pe32plus_header. +// We store the intermediate state in a pe32plus_header. +template <class PeHeader1Ty, class PeHeader2Ty> +void copyPeHeader(PeHeader1Ty &Dest, const PeHeader2Ty &Src) { + Dest.Magic = Src.Magic; + Dest.MajorLinkerVersion = Src.MajorLinkerVersion; + Dest.MinorLinkerVersion = Src.MinorLinkerVersion; + Dest.SizeOfCode = Src.SizeOfCode; + Dest.SizeOfInitializedData = Src.SizeOfInitializedData; + Dest.SizeOfUninitializedData = Src.SizeOfUninitializedData; + Dest.AddressOfEntryPoint = Src.AddressOfEntryPoint; + Dest.BaseOfCode = Src.BaseOfCode; + Dest.ImageBase = Src.ImageBase; + Dest.SectionAlignment = Src.SectionAlignment; + Dest.FileAlignment = Src.FileAlignment; + Dest.MajorOperatingSystemVersion = Src.MajorOperatingSystemVersion; + Dest.MinorOperatingSystemVersion = Src.MinorOperatingSystemVersion; + Dest.MajorImageVersion = Src.MajorImageVersion; + Dest.MinorImageVersion = Src.MinorImageVersion; + Dest.MajorSubsystemVersion = Src.MajorSubsystemVersion; + Dest.MinorSubsystemVersion = Src.MinorSubsystemVersion; + Dest.Win32VersionValue = Src.Win32VersionValue; + Dest.SizeOfImage = Src.SizeOfImage; + Dest.SizeOfHeaders = Src.SizeOfHeaders; + Dest.CheckSum = Src.CheckSum; + Dest.Subsystem = Src.Subsystem; + Dest.DLLCharacteristics = Src.DLLCharacteristics; + Dest.SizeOfStackReserve = Src.SizeOfStackReserve; + Dest.SizeOfStackCommit = Src.SizeOfStackCommit; + Dest.SizeOfHeapReserve = Src.SizeOfHeapReserve; + Dest.SizeOfHeapCommit = Src.SizeOfHeapCommit; + Dest.LoaderFlags = Src.LoaderFlags; + Dest.NumberOfRvaAndSize = Src.NumberOfRvaAndSize; +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/contrib/llvm/tools/llvm-objcopy/COFF/Reader.cpp new file mode 100644 index 000000000000..a01768392d7d --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Reader.cpp @@ -0,0 +1,171 @@ +//===- Reader.cpp ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Reader.h" +#include "Object.h" +#include "llvm-objcopy.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstddef> +#include <cstdint> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; + +Error COFFReader::readExecutableHeaders(Object &Obj) const { + const dos_header *DH = COFFObj.getDOSHeader(); + Obj.Is64 = COFFObj.is64(); + if (!DH) + return Error::success(); + + Obj.IsPE = true; + Obj.DosHeader = *DH; + if (DH->AddressOfNewExeHeader > sizeof(*DH)) + Obj.DosStub = ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&DH[1]), + DH->AddressOfNewExeHeader - sizeof(*DH)); + + if (COFFObj.is64()) { + const pe32plus_header *PE32Plus = nullptr; + if (auto EC = COFFObj.getPE32PlusHeader(PE32Plus)) + return errorCodeToError(EC); + Obj.PeHeader = *PE32Plus; + } else { + const pe32_header *PE32 = nullptr; + if (auto EC = COFFObj.getPE32Header(PE32)) + return errorCodeToError(EC); + copyPeHeader(Obj.PeHeader, *PE32); + // The pe32plus_header (stored in Object) lacks the BaseOfData field. + Obj.BaseOfData = PE32->BaseOfData; + } + + for (size_t I = 0; I < Obj.PeHeader.NumberOfRvaAndSize; I++) { + const data_directory *Dir; + if (auto EC = COFFObj.getDataDirectory(I, Dir)) + return errorCodeToError(EC); + Obj.DataDirectories.emplace_back(*Dir); + } + return Error::success(); +} + +Error COFFReader::readSections(Object &Obj) const { + // Section indexing starts from 1. + for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) { + const coff_section *Sec; + if (auto EC = COFFObj.getSection(I, Sec)) + return errorCodeToError(EC); + Obj.Sections.push_back(Section()); + Section &S = Obj.Sections.back(); + S.Header = *Sec; + if (auto EC = COFFObj.getSectionContents(Sec, S.Contents)) + return errorCodeToError(EC); + ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec); + for (const coff_relocation &R : Relocs) + S.Relocs.push_back(R); + if (auto EC = COFFObj.getSectionName(Sec, S.Name)) + return errorCodeToError(EC); + if (Sec->hasExtendedRelocations()) + return make_error<StringError>("Extended relocations not supported yet", + object_error::parse_failed); + } + return Error::success(); +} + +Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const { + std::vector<Symbol> Symbols; + Symbols.reserve(COFFObj.getRawNumberOfSymbols()); + for (uint32_t I = 0, E = COFFObj.getRawNumberOfSymbols(); I < E;) { + Expected<COFFSymbolRef> SymOrErr = COFFObj.getSymbol(I); + if (!SymOrErr) + return SymOrErr.takeError(); + COFFSymbolRef SymRef = *SymOrErr; + + Symbols.push_back(Symbol()); + Symbol &Sym = Symbols.back(); + // Copy symbols from the original form into an intermediate coff_symbol32. + if (IsBigObj) + copySymbol(Sym.Sym, + *reinterpret_cast<const coff_symbol32 *>(SymRef.getRawPtr())); + else + copySymbol(Sym.Sym, + *reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr())); + if (auto EC = COFFObj.getSymbolName(SymRef, Sym.Name)) + return errorCodeToError(EC); + Sym.AuxData = COFFObj.getSymbolAuxData(SymRef); + assert((Sym.AuxData.size() % + (IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16))) == 0); + I += 1 + SymRef.getNumberOfAuxSymbols(); + } + Obj.addSymbols(Symbols); + return Error::success(); +} + +Error COFFReader::setRelocTargets(Object &Obj) const { + std::vector<const Symbol *> RawSymbolTable; + for (const Symbol &Sym : Obj.getSymbols()) { + RawSymbolTable.push_back(&Sym); + for (size_t I = 0; I < Sym.Sym.NumberOfAuxSymbols; I++) + RawSymbolTable.push_back(nullptr); + } + for (Section &Sec : Obj.Sections) { + for (Relocation &R : Sec.Relocs) { + if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size()) + return make_error<StringError>("SymbolTableIndex out of range", + object_error::parse_failed); + const Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex]; + if (Sym == nullptr) + return make_error<StringError>("Invalid SymbolTableIndex", + object_error::parse_failed); + R.Target = Sym->UniqueId; + R.TargetName = Sym->Name; + } + } + return Error::success(); +} + +Expected<std::unique_ptr<Object>> COFFReader::create() const { + auto Obj = llvm::make_unique<Object>(); + + const coff_file_header *CFH = nullptr; + const coff_bigobj_file_header *CBFH = nullptr; + COFFObj.getCOFFHeader(CFH); + COFFObj.getCOFFBigObjHeader(CBFH); + bool IsBigObj = false; + if (CFH) { + Obj->CoffFileHeader = *CFH; + } else { + if (!CBFH) + return make_error<StringError>("No COFF file header returned", + object_error::parse_failed); + // Only copying the few fields from the bigobj header that we need + // and won't recreate in the end. + Obj->CoffFileHeader.Machine = CBFH->Machine; + Obj->CoffFileHeader.TimeDateStamp = CBFH->TimeDateStamp; + IsBigObj = true; + } + + if (Error E = readExecutableHeaders(*Obj)) + return std::move(E); + if (Error E = readSections(*Obj)) + return std::move(E); + if (Error E = readSymbols(*Obj, IsBigObj)) + return std::move(E); + if (Error E = setRelocTargets(*Obj)) + return std::move(E); + + return std::move(Obj); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Reader.h b/contrib/llvm/tools/llvm-objcopy/COFF/Reader.h new file mode 100644 index 000000000000..ca7057d08c9f --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Reader.h @@ -0,0 +1,43 @@ +//===- Reader.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_TOOLS_OBJCOPY_COFF_READER_H +#define LLVM_TOOLS_OBJCOPY_COFF_READER_H + +#include "Buffer.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Object; + +using object::COFFObjectFile; + +class COFFReader { + const COFFObjectFile &COFFObj; + + Error readExecutableHeaders(Object &Obj) const; + Error readSections(Object &Obj) const; + Error readSymbols(Object &Obj, bool IsBigObj) const; + Error setRelocTargets(Object &Obj) const; + +public: + explicit COFFReader(const COFFObjectFile &O) : COFFObj(O) {} + Expected<std::unique_ptr<Object>> create() const; +}; + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_READER_H diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/contrib/llvm/tools/llvm-objcopy/COFF/Writer.cpp new file mode 100644 index 000000000000..385d43b1bae5 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Writer.cpp @@ -0,0 +1,337 @@ +//===- Writer.cpp ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Writer.h" +#include "Object.h" +#include "llvm-objcopy.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstddef> +#include <cstdint> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; +using namespace COFF; + +Error COFFWriter::finalizeRelocTargets() { + for (Section &Sec : Obj.Sections) { + for (Relocation &R : Sec.Relocs) { + const Symbol *Sym = Obj.findSymbol(R.Target); + if (Sym == nullptr) + return make_error<StringError>("Relocation target " + R.TargetName + + " (" + Twine(R.Target) + + ") not found", + object_error::invalid_symbol_index); + R.Reloc.SymbolTableIndex = Sym->RawIndex; + } + } + return Error::success(); +} + +void COFFWriter::layoutSections() { + for (auto &S : Obj.Sections) { + if (S.Header.SizeOfRawData > 0) + S.Header.PointerToRawData = FileSize; + FileSize += S.Header.SizeOfRawData; // For executables, this is already + // aligned to FileAlignment. + S.Header.NumberOfRelocations = S.Relocs.size(); + S.Header.PointerToRelocations = + S.Header.NumberOfRelocations > 0 ? FileSize : 0; + FileSize += S.Relocs.size() * sizeof(coff_relocation); + FileSize = alignTo(FileSize, FileAlignment); + + if (S.Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) + SizeOfInitializedData += S.Header.SizeOfRawData; + } +} + +size_t COFFWriter::finalizeStringTable() { + for (auto &S : Obj.Sections) + if (S.Name.size() > COFF::NameSize) + StrTabBuilder.add(S.Name); + + for (const auto &S : Obj.getSymbols()) + if (S.Name.size() > COFF::NameSize) + StrTabBuilder.add(S.Name); + + StrTabBuilder.finalize(); + + for (auto &S : Obj.Sections) { + if (S.Name.size() > COFF::NameSize) { + snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d", + (int)StrTabBuilder.getOffset(S.Name)); + } else { + strncpy(S.Header.Name, S.Name.data(), COFF::NameSize); + } + } + for (auto &S : Obj.getMutableSymbols()) { + if (S.Name.size() > COFF::NameSize) { + S.Sym.Name.Offset.Zeroes = 0; + S.Sym.Name.Offset.Offset = StrTabBuilder.getOffset(S.Name); + } else { + strncpy(S.Sym.Name.ShortName, S.Name.data(), COFF::NameSize); + } + } + return StrTabBuilder.getSize(); +} + +template <class SymbolTy> +std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() { + size_t SymTabSize = Obj.getSymbols().size() * sizeof(SymbolTy); + for (const auto &S : Obj.getSymbols()) + SymTabSize += S.AuxData.size(); + return std::make_pair(SymTabSize, sizeof(SymbolTy)); +} + +Error COFFWriter::finalize(bool IsBigObj) { + if (Error E = finalizeRelocTargets()) + return E; + + size_t SizeOfHeaders = 0; + FileAlignment = 1; + size_t PeHeaderSize = 0; + if (Obj.IsPE) { + Obj.DosHeader.AddressOfNewExeHeader = + sizeof(Obj.DosHeader) + Obj.DosStub.size(); + SizeOfHeaders += Obj.DosHeader.AddressOfNewExeHeader + sizeof(PEMagic); + + FileAlignment = Obj.PeHeader.FileAlignment; + Obj.PeHeader.NumberOfRvaAndSize = Obj.DataDirectories.size(); + + PeHeaderSize = Obj.Is64 ? sizeof(pe32plus_header) : sizeof(pe32_header); + SizeOfHeaders += + PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); + } + Obj.CoffFileHeader.NumberOfSections = Obj.Sections.size(); + SizeOfHeaders += + IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header); + SizeOfHeaders += sizeof(coff_section) * Obj.Sections.size(); + SizeOfHeaders = alignTo(SizeOfHeaders, FileAlignment); + + Obj.CoffFileHeader.SizeOfOptionalHeader = + PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); + + FileSize = SizeOfHeaders; + SizeOfInitializedData = 0; + + layoutSections(); + + if (Obj.IsPE) { + Obj.PeHeader.SizeOfHeaders = SizeOfHeaders; + Obj.PeHeader.SizeOfInitializedData = SizeOfInitializedData; + + if (!Obj.Sections.empty()) { + const Section &S = Obj.Sections.back(); + Obj.PeHeader.SizeOfImage = + alignTo(S.Header.VirtualAddress + S.Header.VirtualSize, + Obj.PeHeader.SectionAlignment); + } + + // If the PE header had a checksum, clear it, since it isn't valid + // any longer. (We don't calculate a new one.) + Obj.PeHeader.CheckSum = 0; + } + + size_t StrTabSize = finalizeStringTable(); + size_t SymTabSize, SymbolSize; + std::tie(SymTabSize, SymbolSize) = IsBigObj + ? finalizeSymbolTable<coff_symbol32>() + : finalizeSymbolTable<coff_symbol16>(); + + size_t PointerToSymbolTable = FileSize; + // StrTabSize <= 4 is the size of an empty string table, only consisting + // of the length field. + if (SymTabSize == 0 && StrTabSize <= 4 && Obj.IsPE) { + // For executables, don't point to the symbol table and skip writing + // the length field, if both the symbol and string tables are empty. + PointerToSymbolTable = 0; + StrTabSize = 0; + } + + size_t NumRawSymbols = SymTabSize / SymbolSize; + Obj.CoffFileHeader.PointerToSymbolTable = PointerToSymbolTable; + Obj.CoffFileHeader.NumberOfSymbols = NumRawSymbols; + FileSize += SymTabSize + StrTabSize; + FileSize = alignTo(FileSize, FileAlignment); + + return Error::success(); +} + +void COFFWriter::writeHeaders(bool IsBigObj) { + uint8_t *Ptr = Buf.getBufferStart(); + if (Obj.IsPE) { + memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader)); + Ptr += sizeof(Obj.DosHeader); + memcpy(Ptr, Obj.DosStub.data(), Obj.DosStub.size()); + Ptr += Obj.DosStub.size(); + memcpy(Ptr, PEMagic, sizeof(PEMagic)); + Ptr += sizeof(PEMagic); + } + if (!IsBigObj) { + memcpy(Ptr, &Obj.CoffFileHeader, sizeof(Obj.CoffFileHeader)); + Ptr += sizeof(Obj.CoffFileHeader); + } else { + // Generate a coff_bigobj_file_header, filling it in with the values + // from Obj.CoffFileHeader. All extra fields that don't exist in + // coff_file_header can be set to hardcoded values. + coff_bigobj_file_header BigObjHeader; + BigObjHeader.Sig1 = IMAGE_FILE_MACHINE_UNKNOWN; + BigObjHeader.Sig2 = 0xffff; + BigObjHeader.Version = BigObjHeader::MinBigObjectVersion; + BigObjHeader.Machine = Obj.CoffFileHeader.Machine; + BigObjHeader.TimeDateStamp = Obj.CoffFileHeader.TimeDateStamp; + memcpy(BigObjHeader.UUID, BigObjMagic, sizeof(BigObjMagic)); + BigObjHeader.unused1 = 0; + BigObjHeader.unused2 = 0; + BigObjHeader.unused3 = 0; + BigObjHeader.unused4 = 0; + // The value in Obj.CoffFileHeader.NumberOfSections is truncated, thus + // get the original one instead. + BigObjHeader.NumberOfSections = Obj.Sections.size(); + BigObjHeader.PointerToSymbolTable = Obj.CoffFileHeader.PointerToSymbolTable; + BigObjHeader.NumberOfSymbols = Obj.CoffFileHeader.NumberOfSymbols; + + memcpy(Ptr, &BigObjHeader, sizeof(BigObjHeader)); + Ptr += sizeof(BigObjHeader); + } + if (Obj.IsPE) { + if (Obj.Is64) { + memcpy(Ptr, &Obj.PeHeader, sizeof(Obj.PeHeader)); + Ptr += sizeof(Obj.PeHeader); + } else { + pe32_header PeHeader; + copyPeHeader(PeHeader, Obj.PeHeader); + // The pe32plus_header (stored in Object) lacks the BaseOfData field. + PeHeader.BaseOfData = Obj.BaseOfData; + + memcpy(Ptr, &PeHeader, sizeof(PeHeader)); + Ptr += sizeof(PeHeader); + } + for (const auto &DD : Obj.DataDirectories) { + memcpy(Ptr, &DD, sizeof(DD)); + Ptr += sizeof(DD); + } + } + for (const auto &S : Obj.Sections) { + memcpy(Ptr, &S.Header, sizeof(S.Header)); + Ptr += sizeof(S.Header); + } +} + +void COFFWriter::writeSections() { + for (const auto &S : Obj.Sections) { + uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData; + std::copy(S.Contents.begin(), S.Contents.end(), Ptr); + + // For executable sections, pad the remainder of the raw data size with + // 0xcc, which is int3 on x86. + if ((S.Header.Characteristics & IMAGE_SCN_CNT_CODE) && + S.Header.SizeOfRawData > S.Contents.size()) + memset(Ptr + S.Contents.size(), 0xcc, + S.Header.SizeOfRawData - S.Contents.size()); + + Ptr += S.Header.SizeOfRawData; + for (const auto &R : S.Relocs) { + memcpy(Ptr, &R.Reloc, sizeof(R.Reloc)); + Ptr += sizeof(R.Reloc); + } + } +} + +template <class SymbolTy> void COFFWriter::writeSymbolStringTables() { + uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable; + for (const auto &S : Obj.getSymbols()) { + // Convert symbols back to the right size, from coff_symbol32. + copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr), + S.Sym); + Ptr += sizeof(SymbolTy); + std::copy(S.AuxData.begin(), S.AuxData.end(), Ptr); + Ptr += S.AuxData.size(); + } + if (StrTabBuilder.getSize() > 4 || !Obj.IsPE) { + // Always write a string table in object files, even an empty one. + StrTabBuilder.write(Ptr); + Ptr += StrTabBuilder.getSize(); + } +} + +Error COFFWriter::write(bool IsBigObj) { + if (Error E = finalize(IsBigObj)) + return E; + + Buf.allocate(FileSize); + + writeHeaders(IsBigObj); + writeSections(); + if (IsBigObj) + writeSymbolStringTables<coff_symbol32>(); + else + writeSymbolStringTables<coff_symbol16>(); + + if (Obj.IsPE) + if (Error E = patchDebugDirectory()) + return E; + + return Buf.commit(); +} + +// Locate which sections contain the debug directories, iterate over all +// the debug_directory structs in there, and set the PointerToRawData field +// in all of them, according to their new physical location in the file. +Error COFFWriter::patchDebugDirectory() { + if (Obj.DataDirectories.size() < DEBUG_DIRECTORY) + return Error::success(); + const data_directory *Dir = &Obj.DataDirectories[DEBUG_DIRECTORY]; + if (Dir->Size <= 0) + return Error::success(); + for (const auto &S : Obj.Sections) { + if (Dir->RelativeVirtualAddress >= S.Header.VirtualAddress && + Dir->RelativeVirtualAddress < + S.Header.VirtualAddress + S.Header.SizeOfRawData) { + if (Dir->RelativeVirtualAddress + Dir->Size > + S.Header.VirtualAddress + S.Header.SizeOfRawData) + return make_error<StringError>( + "Debug directory extends past end of section", + object_error::parse_failed); + + size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress; + uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset; + uint8_t *End = Ptr + Dir->Size; + while (Ptr < End) { + debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr); + Debug->PointerToRawData = + S.Header.PointerToRawData + Offset + sizeof(debug_directory); + Ptr += sizeof(debug_directory) + Debug->SizeOfData; + Offset += sizeof(debug_directory) + Debug->SizeOfData; + } + // Debug directory found and patched, all done. + return Error::success(); + } + } + return make_error<StringError>("Debug directory not found", + object_error::parse_failed); +} + +Error COFFWriter::write() { + bool IsBigObj = Obj.Sections.size() > MaxNumberOfSections16; + if (IsBigObj && Obj.IsPE) + return make_error<StringError>("Too many sections for executable", + object_error::parse_failed); + return write(IsBigObj); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Writer.h b/contrib/llvm/tools/llvm-objcopy/COFF/Writer.h new file mode 100644 index 000000000000..ab66e0cc1134 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Writer.h @@ -0,0 +1,61 @@ +//===- Writer.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_TOOLS_OBJCOPY_COFF_WRITER_H +#define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H + +#include "Buffer.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Support/Error.h" +#include <cstddef> +#include <utility> + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Object; + +class COFFWriter { + Object &Obj; + Buffer &Buf; + + size_t FileSize; + size_t FileAlignment; + size_t SizeOfInitializedData; + StringTableBuilder StrTabBuilder; + + Error finalizeRelocTargets(); + void layoutSections(); + size_t finalizeStringTable(); + template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable(); + + Error finalize(bool IsBigObj); + + void writeHeaders(bool IsBigObj); + void writeSections(); + template <class SymbolTy> void writeSymbolStringTables(); + + Error write(bool IsBigObj); + + Error patchDebugDirectory(); + +public: + virtual ~COFFWriter() {} + Error write(); + + COFFWriter(Object &Obj, Buffer &Buf) + : Obj(Obj), Buf(Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {} +}; + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_WRITER_H diff --git a/contrib/llvm/tools/llvm-objcopy/CopyConfig.cpp b/contrib/llvm/tools/llvm-objcopy/CopyConfig.cpp new file mode 100644 index 000000000000..3737f571ae61 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -0,0 +1,474 @@ +//===- CopyConfig.cpp -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CopyConfig.h" +#include "llvm-objcopy.h" + +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> +#include <string> + +namespace llvm { +namespace objcopy { + +namespace { +enum ObjcopyID { + OBJCOPY_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OBJCOPY_##ID, +#include "ObjcopyOpts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE; +#include "ObjcopyOpts.inc" +#undef PREFIX + +static const opt::OptTable::Info ObjcopyInfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {OBJCOPY_##PREFIX, \ + NAME, \ + HELPTEXT, \ + METAVAR, \ + OBJCOPY_##ID, \ + opt::Option::KIND##Class, \ + PARAM, \ + FLAGS, \ + OBJCOPY_##GROUP, \ + OBJCOPY_##ALIAS, \ + ALIASARGS, \ + VALUES}, +#include "ObjcopyOpts.inc" +#undef OPTION +}; + +class ObjcopyOptTable : public opt::OptTable { +public: + ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {} +}; + +enum StripID { + STRIP_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + STRIP_##ID, +#include "StripOpts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE; +#include "StripOpts.inc" +#undef PREFIX + +static const opt::OptTable::Info StripInfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {STRIP_##PREFIX, NAME, HELPTEXT, \ + METAVAR, STRIP_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, STRIP_##GROUP, \ + STRIP_##ALIAS, ALIASARGS, VALUES}, +#include "StripOpts.inc" +#undef OPTION +}; + +class StripOptTable : public opt::OptTable { +public: + StripOptTable() : OptTable(StripInfoTable) {} +}; + +enum SectionFlag { + SecNone = 0, + SecAlloc = 1 << 0, + SecLoad = 1 << 1, + SecNoload = 1 << 2, + SecReadonly = 1 << 3, + SecDebug = 1 << 4, + SecCode = 1 << 5, + SecData = 1 << 6, + SecRom = 1 << 7, + SecMerge = 1 << 8, + SecStrings = 1 << 9, + SecContents = 1 << 10, + SecShare = 1 << 11, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare) +}; + +} // namespace + +static SectionFlag parseSectionRenameFlag(StringRef SectionName) { + return llvm::StringSwitch<SectionFlag>(SectionName) + .Case("alloc", SectionFlag::SecAlloc) + .Case("load", SectionFlag::SecLoad) + .Case("noload", SectionFlag::SecNoload) + .Case("readonly", SectionFlag::SecReadonly) + .Case("debug", SectionFlag::SecDebug) + .Case("code", SectionFlag::SecCode) + .Case("data", SectionFlag::SecData) + .Case("rom", SectionFlag::SecRom) + .Case("merge", SectionFlag::SecMerge) + .Case("strings", SectionFlag::SecStrings) + .Case("contents", SectionFlag::SecContents) + .Case("share", SectionFlag::SecShare) + .Default(SectionFlag::SecNone); +} + +static SectionRename parseRenameSectionValue(StringRef FlagValue) { + if (!FlagValue.contains('=')) + error("Bad format for --rename-section: missing '='"); + + // Initial split: ".foo" = ".bar,f1,f2,..." + auto Old2New = FlagValue.split('='); + SectionRename SR; + SR.OriginalName = Old2New.first; + + // Flags split: ".bar" "f1" "f2" ... + SmallVector<StringRef, 6> NameAndFlags; + Old2New.second.split(NameAndFlags, ','); + SR.NewName = NameAndFlags[0]; + + if (NameAndFlags.size() > 1) { + SectionFlag Flags = SectionFlag::SecNone; + for (size_t I = 1, Size = NameAndFlags.size(); I < Size; ++I) { + SectionFlag Flag = parseSectionRenameFlag(NameAndFlags[I]); + if (Flag == SectionFlag::SecNone) + error("Unrecognized section flag '" + NameAndFlags[I] + + "'. Flags supported for GNU compatibility: alloc, load, noload, " + "readonly, debug, code, data, rom, share, contents, merge, " + "strings."); + Flags |= Flag; + } + + SR.NewFlags = 0; + if (Flags & SectionFlag::SecAlloc) + *SR.NewFlags |= ELF::SHF_ALLOC; + if (!(Flags & SectionFlag::SecReadonly)) + *SR.NewFlags |= ELF::SHF_WRITE; + if (Flags & SectionFlag::SecCode) + *SR.NewFlags |= ELF::SHF_EXECINSTR; + if (Flags & SectionFlag::SecMerge) + *SR.NewFlags |= ELF::SHF_MERGE; + if (Flags & SectionFlag::SecStrings) + *SR.NewFlags |= ELF::SHF_STRINGS; + } + + return SR; +} + +static const StringMap<MachineInfo> ArchMap{ + // Name, {EMachine, 64bit, LittleEndian} + {"aarch64", {ELF::EM_AARCH64, true, true}}, + {"arm", {ELF::EM_ARM, false, true}}, + {"i386", {ELF::EM_386, false, true}}, + {"i386:x86-64", {ELF::EM_X86_64, true, true}}, + {"powerpc:common64", {ELF::EM_PPC64, true, true}}, + {"sparc", {ELF::EM_SPARC, false, true}}, + {"x86-64", {ELF::EM_X86_64, true, true}}, +}; + +static const MachineInfo &getMachineInfo(StringRef Arch) { + auto Iter = ArchMap.find(Arch); + if (Iter == std::end(ArchMap)) + error("Invalid architecture: '" + Arch + "'"); + return Iter->getValue(); +} + +static const StringMap<MachineInfo> OutputFormatMap{ + // Name, {EMachine, 64bit, LittleEndian} + {"elf32-i386", {ELF::EM_386, false, true}}, + {"elf32-powerpcle", {ELF::EM_PPC, false, true}}, + {"elf32-x86-64", {ELF::EM_X86_64, false, true}}, + {"elf64-powerpcle", {ELF::EM_PPC64, true, true}}, + {"elf64-x86-64", {ELF::EM_X86_64, true, true}}, +}; + +static const MachineInfo &getOutputFormatMachineInfo(StringRef Format) { + auto Iter = OutputFormatMap.find(Format); + if (Iter == std::end(OutputFormatMap)) + error("Invalid output format: '" + Format + "'"); + return Iter->getValue(); +} + +static void addGlobalSymbolsFromFile(std::vector<std::string> &Symbols, + StringRef Filename) { + SmallVector<StringRef, 16> Lines; + auto BufOrErr = MemoryBuffer::getFile(Filename); + if (!BufOrErr) + reportError(Filename, BufOrErr.getError()); + + BufOrErr.get()->getBuffer().split(Lines, '\n'); + for (StringRef Line : Lines) { + // Ignore everything after '#', trim whitespace, and only add the symbol if + // it's not empty. + auto TrimmedLine = Line.split('#').first.trim(); + if (!TrimmedLine.empty()) + Symbols.push_back(TrimmedLine.str()); + } +} + +// ParseObjcopyOptions returns the config and sets the input arguments. If a +// help flag is set then ParseObjcopyOptions will print the help messege and +// exit. +DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { + ObjcopyOptTable T; + unsigned MissingArgumentIndex, MissingArgumentCount; + llvm::opt::InputArgList InputArgs = + T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + + if (InputArgs.size() == 0) { + T.PrintHelp(errs(), "llvm-objcopy input [output]", "objcopy tool"); + exit(1); + } + + if (InputArgs.hasArg(OBJCOPY_help)) { + T.PrintHelp(outs(), "llvm-objcopy input [output]", "objcopy tool"); + exit(0); + } + + if (InputArgs.hasArg(OBJCOPY_version)) { + outs() << "llvm-objcopy, compatible with GNU objcopy\n"; + cl::PrintVersionMessage(); + exit(0); + } + + SmallVector<const char *, 2> Positional; + + for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN)) + error("unknown argument '" + Arg->getAsString(InputArgs) + "'"); + + for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT)) + Positional.push_back(Arg->getValue()); + + if (Positional.empty()) + error("No input file specified"); + + if (Positional.size() > 2) + error("Too many positional arguments"); + + CopyConfig Config; + Config.InputFilename = Positional[0]; + Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1]; + if (InputArgs.hasArg(OBJCOPY_target) && + (InputArgs.hasArg(OBJCOPY_input_target) || + InputArgs.hasArg(OBJCOPY_output_target))) + error("--target cannot be used with --input-target or --output-target"); + + if (InputArgs.hasArg(OBJCOPY_target)) { + Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); + Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target); + } else { + Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target); + Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); + } + if (Config.InputFormat == "binary") { + auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture); + if (BinaryArch.empty()) + error("Specified binary input without specifiying an architecture"); + Config.BinaryArch = getMachineInfo(BinaryArch); + } + if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") + Config.OutputArch = getOutputFormatMachineInfo(Config.OutputFormat); + + if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections, + OBJCOPY_compress_debug_sections_eq)) { + Config.CompressionType = DebugCompressionType::Z; + + if (Arg->getOption().getID() == OBJCOPY_compress_debug_sections_eq) { + Config.CompressionType = + StringSwitch<DebugCompressionType>( + InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)) + .Case("zlib-gnu", DebugCompressionType::GNU) + .Case("zlib", DebugCompressionType::Z) + .Default(DebugCompressionType::None); + if (Config.CompressionType == DebugCompressionType::None) + error("Invalid or unsupported --compress-debug-sections format: " + + InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)); + if (!zlib::isAvailable()) + error("LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress."); + } + } + + Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); + Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); + if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) + Config.BuildIdLinkInput = + InputArgs.getLastArgValue(OBJCOPY_build_id_link_input); + if (InputArgs.hasArg(OBJCOPY_build_id_link_output)) + Config.BuildIdLinkOutput = + InputArgs.getLastArgValue(OBJCOPY_build_id_link_output); + Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); + Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols); + + for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { + if (!StringRef(Arg->getValue()).contains('=')) + error("Bad format for --redefine-sym"); + auto Old2New = StringRef(Arg->getValue()).split('='); + if (!Config.SymbolsToRename.insert(Old2New).second) + error("Multiple redefinition of symbol " + Old2New.first); + } + + for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) { + SectionRename SR = parseRenameSectionValue(StringRef(Arg->getValue())); + if (!Config.SectionsToRename.try_emplace(SR.OriginalName, SR).second) + error("Multiple renames of section " + SR.OriginalName); + } + + for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) + Config.ToRemove.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section)) + Config.KeepSection.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_only_section)) + Config.OnlySection.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) + Config.AddSection.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) + Config.DumpSection.push_back(Arg->getValue()); + Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); + Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu); + Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug); + Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo); + Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections); + Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc); + Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded); + Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo); + Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); + Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); + Config.DiscardAll = InputArgs.hasArg(OBJCOPY_discard_all); + Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); + Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); + Config.DecompressDebugSections = + InputArgs.hasArg(OBJCOPY_decompress_debug_sections); + for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) + Config.SymbolsToLocalize.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) + Config.SymbolsToKeepGlobal.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) + addGlobalSymbolsFromFile(Config.SymbolsToKeepGlobal, Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) + Config.SymbolsToGlobalize.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) + Config.SymbolsToWeaken.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) + Config.SymbolsToRemove.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) + Config.SymbolsToKeep.push_back(Arg->getValue()); + + Config.DeterministicArchives = InputArgs.hasFlag( + OBJCOPY_enable_deterministic_archives, + OBJCOPY_disable_deterministic_archives, /*default=*/true); + + Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates); + + if (Config.DecompressDebugSections && + Config.CompressionType != DebugCompressionType::None) { + error("Cannot specify --compress-debug-sections at the same time as " + "--decompress-debug-sections at the same time"); + } + + if (Config.DecompressDebugSections && !zlib::isAvailable()) + error("LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress."); + + DriverConfig DC; + DC.CopyConfigs.push_back(std::move(Config)); + return DC; +} + +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. +DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr) { + StripOptTable T; + unsigned MissingArgumentIndex, MissingArgumentCount; + llvm::opt::InputArgList InputArgs = + T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + + if (InputArgs.size() == 0) { + T.PrintHelp(errs(), "llvm-strip [options] file...", "strip tool"); + exit(1); + } + + if (InputArgs.hasArg(STRIP_help)) { + T.PrintHelp(outs(), "llvm-strip [options] file...", "strip tool"); + exit(0); + } + + if (InputArgs.hasArg(STRIP_version)) { + outs() << "llvm-strip, compatible with GNU strip\n"; + cl::PrintVersionMessage(); + exit(0); + } + + SmallVector<const char *, 2> Positional; + for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN)) + error("unknown argument '" + Arg->getAsString(InputArgs) + "'"); + for (auto Arg : InputArgs.filtered(STRIP_INPUT)) + Positional.push_back(Arg->getValue()); + + if (Positional.empty()) + error("No input file specified"); + + if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output)) + error("Multiple input files cannot be used in combination with -o"); + + CopyConfig Config; + Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); + + Config.DiscardAll = InputArgs.hasArg(STRIP_discard_all); + Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded); + Config.StripAll = InputArgs.hasArg(STRIP_strip_all); + Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu); + + if (!Config.StripDebug && !Config.StripUnneeded && !Config.DiscardAll && + !Config.StripAllGNU) + Config.StripAll = true; + + for (auto Arg : InputArgs.filtered(STRIP_keep_section)) + Config.KeepSection.push_back(Arg->getValue()); + + for (auto Arg : InputArgs.filtered(STRIP_remove_section)) + Config.ToRemove.push_back(Arg->getValue()); + + for (auto Arg : InputArgs.filtered(STRIP_keep_symbol)) + Config.SymbolsToKeep.push_back(Arg->getValue()); + + Config.DeterministicArchives = + InputArgs.hasFlag(STRIP_enable_deterministic_archives, + STRIP_disable_deterministic_archives, /*default=*/true); + + Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates); + + DriverConfig DC; + if (Positional.size() == 1) { + Config.InputFilename = Positional[0]; + Config.OutputFilename = + InputArgs.getLastArgValue(STRIP_output, Positional[0]); + DC.CopyConfigs.push_back(std::move(Config)); + } else { + for (const char *Filename : Positional) { + Config.InputFilename = Filename; + Config.OutputFilename = Filename; + DC.CopyConfigs.push_back(Config); + } + } + + return DC; +} + +} // namespace objcopy +} // namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/CopyConfig.h b/contrib/llvm/tools/llvm-objcopy/CopyConfig.h new file mode 100644 index 000000000000..71a2423ae1c8 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/CopyConfig.h @@ -0,0 +1,119 @@ +//===- CopyConfig.h -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +// Necessary for llvm::DebugCompressionType::None +#include "llvm/Target/TargetOptions.h" +#include <string> +#include <vector> + +namespace llvm { +namespace objcopy { + +// This type keeps track of the machine info for various architectures. This +// lets us map architecture names to ELF types and the e_machine value of the +// ELF file. +struct MachineInfo { + uint16_t EMachine; + bool Is64Bit; + bool IsLittleEndian; +}; + +struct SectionRename { + StringRef OriginalName; + StringRef NewName; + Optional<uint64_t> NewFlags; +}; + +// Configuration for copying/stripping a single file. +struct CopyConfig { + // Main input/output options + StringRef InputFilename; + StringRef InputFormat; + StringRef OutputFilename; + StringRef OutputFormat; + + // Only applicable for --input-format=binary + MachineInfo BinaryArch; + // Only applicable when --output-format!=binary (e.g. elf64-x86-64). + Optional<MachineInfo> OutputArch; + + // Advanced options + StringRef AddGnuDebugLink; + StringRef BuildIdLinkDir; + Optional<StringRef> BuildIdLinkInput; + Optional<StringRef> BuildIdLinkOutput; + StringRef SplitDWO; + StringRef SymbolsPrefix; + + // Repeated options + std::vector<StringRef> AddSection; + std::vector<StringRef> DumpSection; + std::vector<StringRef> KeepSection; + std::vector<StringRef> OnlySection; + std::vector<StringRef> SymbolsToGlobalize; + std::vector<StringRef> SymbolsToKeep; + std::vector<StringRef> SymbolsToLocalize; + std::vector<StringRef> SymbolsToRemove; + std::vector<StringRef> SymbolsToWeaken; + std::vector<StringRef> ToRemove; + std::vector<std::string> SymbolsToKeepGlobal; + + // Map options + StringMap<SectionRename> SectionsToRename; + StringMap<StringRef> SymbolsToRename; + + // Boolean options + bool DeterministicArchives = true; + bool DiscardAll = false; + bool ExtractDWO = false; + bool KeepFileSymbols = false; + bool LocalizeHidden = false; + bool OnlyKeepDebug = false; + bool PreserveDates = false; + bool StripAll = false; + bool StripAllGNU = false; + bool StripDWO = false; + bool StripDebug = false; + bool StripNonAlloc = false; + bool StripSections = false; + bool StripUnneeded = false; + bool Weaken = false; + bool DecompressDebugSections = false; + DebugCompressionType CompressionType = DebugCompressionType::None; +}; + +// Configuration for the overall invocation of this tool. When invoked as +// objcopy, will always contain exactly one CopyConfig. When invoked as strip, +// will contain one or more CopyConfigs. +struct DriverConfig { + SmallVector<CopyConfig, 1> CopyConfigs; +}; + +// ParseObjcopyOptions returns the config and sets the input arguments. If a +// help flag is set then ParseObjcopyOptions will print the help messege and +// exit. +DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr); + +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. +DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr); + +} // namespace objcopy +} // namespace llvm + +#endif diff --git a/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp new file mode 100644 index 000000000000..f5ab8e708267 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -0,0 +1,584 @@ +//===- ELFObjcopy.cpp -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ELFObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "Object.h" +#include "llvm-objcopy.h" + +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/Error.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstdlib> +#include <functional> +#include <iterator> +#include <memory> +#include <string> +#include <system_error> +#include <utility> + +namespace llvm { +namespace objcopy { +namespace elf { + +using namespace object; +using namespace ELF; +using SectionPred = std::function<bool(const SectionBase &Sec)>; + +static bool isDebugSection(const SectionBase &Sec) { + return StringRef(Sec.Name).startswith(".debug") || + StringRef(Sec.Name).startswith(".zdebug") || Sec.Name == ".gdb_index"; +} + +static bool isDWOSection(const SectionBase &Sec) { + return StringRef(Sec.Name).endswith(".dwo"); +} + +static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { + // We can't remove the section header string table. + if (&Sec == Obj.SectionNames) + return false; + // Short of keeping the string table we want to keep everything that is a DWO + // section and remove everything else. + return !isDWOSection(Sec); +} + +static ElfType getOutputElfType(const Binary &Bin) { + // Infer output ELF type from the input ELF object + if (isa<ELFObjectFile<ELF32LE>>(Bin)) + return ELFT_ELF32LE; + if (isa<ELFObjectFile<ELF64LE>>(Bin)) + return ELFT_ELF64LE; + if (isa<ELFObjectFile<ELF32BE>>(Bin)) + return ELFT_ELF32BE; + if (isa<ELFObjectFile<ELF64BE>>(Bin)) + return ELFT_ELF64BE; + llvm_unreachable("Invalid ELFType"); +} + +static ElfType getOutputElfType(const MachineInfo &MI) { + // Infer output ELF type from the binary arch specified + if (MI.Is64Bit) + return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE; + else + return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; +} + +static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, + Object &Obj, Buffer &Buf, + ElfType OutputElfType) { + if (Config.OutputFormat == "binary") { + return llvm::make_unique<BinaryWriter>(Obj, Buf); + } + // Depending on the initial ELFT and OutputFormat we need a different Writer. + switch (OutputElfType) { + case ELFT_ELF32LE: + return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, + !Config.StripSections); + case ELFT_ELF64LE: + return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, + !Config.StripSections); + case ELFT_ELF32BE: + return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, + !Config.StripSections); + case ELFT_ELF64BE: + return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, + !Config.StripSections); + } + llvm_unreachable("Invalid output format"); +} + +template <class ELFT> +static Expected<ArrayRef<uint8_t>> +findBuildID(const object::ELFFile<ELFT> &In) { + for (const auto &Phdr : unwrapOrError(In.program_headers())) { + if (Phdr.p_type != PT_NOTE) + continue; + Error Err = Error::success(); + for (const auto &Note : In.notes(Phdr, Err)) + if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU) + return Note.getDesc(); + if (Err) + return std::move(Err); + } + return createStringError(llvm::errc::invalid_argument, + "Could not find build ID."); +} + +static Expected<ArrayRef<uint8_t>> +findBuildID(const object::ELFObjectFileBase &In) { + if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In)) + return findBuildID(*O->getELFFile()); + else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In)) + return findBuildID(*O->getELFFile()); + else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In)) + return findBuildID(*O->getELFFile()); + else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In)) + return findBuildID(*O->getELFFile()); + + llvm_unreachable("Bad file format"); +} + +static void linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink, + StringRef Suffix, ArrayRef<uint8_t> BuildIdBytes) { + SmallString<128> Path = Config.BuildIdLinkDir; + sys::path::append(Path, llvm::toHex(BuildIdBytes[0], /*LowerCase*/ true)); + if (auto EC = sys::fs::create_directories(Path)) + error("cannot create build ID link directory " + Path + ": " + + EC.message()); + + sys::path::append(Path, + llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true)); + Path += Suffix; + if (auto EC = sys::fs::create_hard_link(ToLink, Path)) { + // Hard linking failed, try to remove the file first if it exists. + if (sys::fs::exists(Path)) + sys::fs::remove(Path); + EC = sys::fs::create_hard_link(ToLink, Path); + if (EC) + error("cannot link " + ToLink + " to " + Path + ": " + EC.message()); + } +} + +static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader, + StringRef File, ElfType OutputElfType) { + auto DWOFile = Reader.create(); + DWOFile->removeSections( + [&](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); }); + if (Config.OutputArch) + DWOFile->Machine = Config.OutputArch.getValue().EMachine; + FileBuffer FB(File); + auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType); + Writer->finalize(); + Writer->write(); +} + +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (auto &Sec : Obj.sections()) { + if (Sec.Name == SecName) { + if (Sec.OriginalData.empty()) + return make_error<StringError>("Can't dump section \"" + SecName + + "\": it has no contents", + object_error::parse_failed); + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(Filename, Sec.OriginalData.size()); + if (!BufferOrErr) + return BufferOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); + std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), + Buf->getBufferStart()); + if (Error E = Buf->commit()) + return E; + return Error::success(); + } + } + return make_error<StringError>("Section not found", + object_error::parse_failed); +} + +static bool isCompressed(const SectionBase &Section) { + const char *Magic = "ZLIB"; + return StringRef(Section.Name).startswith(".zdebug") || + (Section.OriginalData.size() > strlen(Magic) && + !strncmp(reinterpret_cast<const char *>(Section.OriginalData.data()), + Magic, strlen(Magic))) || + (Section.Flags & ELF::SHF_COMPRESSED); +} + +static bool isCompressable(const SectionBase &Section) { + return !isCompressed(Section) && isDebugSection(Section) && + Section.Name != ".gdb_index"; +} + +static void replaceDebugSections( + const CopyConfig &Config, Object &Obj, SectionPred &RemovePred, + function_ref<bool(const SectionBase &)> shouldReplace, + function_ref<SectionBase *(const SectionBase *)> addSection) { + SmallVector<SectionBase *, 13> ToReplace; + SmallVector<RelocationSection *, 13> RelocationSections; + for (auto &Sec : Obj.sections()) { + if (RelocationSection *R = dyn_cast<RelocationSection>(&Sec)) { + if (shouldReplace(*R->getSection())) + RelocationSections.push_back(R); + continue; + } + + if (shouldReplace(Sec)) + ToReplace.push_back(&Sec); + } + + for (SectionBase *S : ToReplace) { + SectionBase *NewSection = addSection(S); + + for (RelocationSection *RS : RelocationSections) { + if (RS->getSection() == S) + RS->setSection(NewSection); + } + } + + RemovePred = [shouldReplace, RemovePred](const SectionBase &Sec) { + return shouldReplace(Sec) || RemovePred(Sec); + }; +} + +// This function handles the high level operations of GNU objcopy including +// handling command line options. It's important to outline certain properties +// we expect to hold of the command line operations. Any operation that "keeps" +// should keep regardless of a remove. Additionally any removal should respect +// any previous removals. Lastly whether or not something is removed shouldn't +// depend a) on the order the options occur in or b) on some opaque priority +// system. The only priority is that keeps/copies overrule removes. +static void handleArgs(const CopyConfig &Config, Object &Obj, + const Reader &Reader, ElfType OutputElfType) { + + if (!Config.SplitDWO.empty()) { + splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType); + } + if (Config.OutputArch) + Obj.Machine = Config.OutputArch.getValue().EMachine; + + // TODO: update or remove symbols only if there is an option that affects + // them. + if (Obj.SymbolTable) { + Obj.SymbolTable->updateSymbols([&](Symbol &Sym) { + if (!Sym.isCommon() && + ((Config.LocalizeHidden && + (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || + is_contained(Config.SymbolsToLocalize, Sym.Name))) + Sym.Binding = STB_LOCAL; + + // Note: these two globalize flags have very similar names but different + // meanings: + // + // --globalize-symbol: promote a symbol to global + // --keep-global-symbol: all symbols except for these should be made local + // + // If --globalize-symbol is specified for a given symbol, it will be + // global in the output file even if it is not included via + // --keep-global-symbol. Because of that, make sure to check + // --globalize-symbol second. + if (!Config.SymbolsToKeepGlobal.empty() && + !is_contained(Config.SymbolsToKeepGlobal, Sym.Name) && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_LOCAL; + + if (is_contained(Config.SymbolsToGlobalize, Sym.Name) && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_GLOBAL; + + if (is_contained(Config.SymbolsToWeaken, Sym.Name) && + Sym.Binding == STB_GLOBAL) + Sym.Binding = STB_WEAK; + + if (Config.Weaken && Sym.Binding == STB_GLOBAL && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_WEAK; + + const auto I = Config.SymbolsToRename.find(Sym.Name); + if (I != Config.SymbolsToRename.end()) + Sym.Name = I->getValue(); + + if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION) + Sym.Name = (Config.SymbolsPrefix + Sym.Name).str(); + }); + + // The purpose of this loop is to mark symbols referenced by sections + // (like GroupSection or RelocationSection). This way, we know which + // symbols are still 'needed' and which are not. + if (Config.StripUnneeded) { + for (auto &Section : Obj.sections()) + Section.markSymbols(); + } + + Obj.removeSymbols([&](const Symbol &Sym) { + if (is_contained(Config.SymbolsToKeep, Sym.Name) || + (Config.KeepFileSymbols && Sym.Type == STT_FILE)) + return false; + + if (Config.DiscardAll && Sym.Binding == STB_LOCAL && + Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE && + Sym.Type != STT_SECTION) + return true; + + if (Config.StripAll || Config.StripAllGNU) + return true; + + if (is_contained(Config.SymbolsToRemove, Sym.Name)) + return true; + + if (Config.StripUnneeded && !Sym.Referenced && + (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) && + Sym.Type != STT_FILE && Sym.Type != STT_SECTION) + return true; + + return false; + }); + } + + SectionPred RemovePred = [](const SectionBase &) { return false; }; + + // Removes: + if (!Config.ToRemove.empty()) { + RemovePred = [&Config](const SectionBase &Sec) { + return is_contained(Config.ToRemove, Sec.Name); + }; + } + + if (Config.StripDWO || !Config.SplitDWO.empty()) + RemovePred = [RemovePred](const SectionBase &Sec) { + return isDWOSection(Sec) || RemovePred(Sec); + }; + + if (Config.ExtractDWO) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + return onlyKeepDWOPred(Obj, Sec) || RemovePred(Sec); + }; + + if (Config.StripAllGNU) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if ((Sec.Flags & SHF_ALLOC) != 0) + return false; + if (&Sec == Obj.SectionNames) + return false; + switch (Sec.Type) { + case SHT_SYMTAB: + case SHT_REL: + case SHT_RELA: + case SHT_STRTAB: + return true; + } + return isDebugSection(Sec); + }; + + if (Config.StripSections) { + RemovePred = [RemovePred](const SectionBase &Sec) { + return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0; + }; + } + + if (Config.StripDebug) { + RemovePred = [RemovePred](const SectionBase &Sec) { + return RemovePred(Sec) || isDebugSection(Sec); + }; + } + + if (Config.StripNonAlloc) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (&Sec == Obj.SectionNames) + return false; + return (Sec.Flags & SHF_ALLOC) == 0; + }; + + if (Config.StripAll) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (&Sec == Obj.SectionNames) + return false; + if (StringRef(Sec.Name).startswith(".gnu.warning")) + return false; + return (Sec.Flags & SHF_ALLOC) == 0; + }; + + // Explicit copies: + if (!Config.OnlySection.empty()) { + RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (is_contained(Config.OnlySection, Sec.Name)) + return false; + + // Allow all implicit removes. + if (RemovePred(Sec)) + return true; + + // Keep special sections. + if (Obj.SectionNames == &Sec) + return false; + if (Obj.SymbolTable == &Sec || + (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec)) + return false; + + // Remove everything else. + return true; + }; + } + + if (!Config.KeepSection.empty()) { + RemovePred = [&Config, RemovePred](const SectionBase &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (is_contained(Config.KeepSection, Sec.Name)) + return false; + // Otherwise defer to RemovePred. + return RemovePred(Sec); + }; + } + + // This has to be the last predicate assignment. + // If the option --keep-symbol has been specified + // and at least one of those symbols is present + // (equivalently, the updated symbol table is not empty) + // the symbol table and the string table should not be removed. + if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) && + Obj.SymbolTable && !Obj.SymbolTable->empty()) { + RemovePred = [&Obj, RemovePred](const SectionBase &Sec) { + if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab()) + return false; + return RemovePred(Sec); + }; + } + + if (Config.CompressionType != DebugCompressionType::None) + replaceDebugSections(Config, Obj, RemovePred, isCompressable, + [&Config, &Obj](const SectionBase *S) { + return &Obj.addSection<CompressedSection>( + *S, Config.CompressionType); + }); + else if (Config.DecompressDebugSections) + replaceDebugSections( + Config, Obj, RemovePred, + [](const SectionBase &S) { return isa<CompressedSection>(&S); }, + [&Obj](const SectionBase *S) { + auto CS = cast<CompressedSection>(S); + return &Obj.addSection<DecompressedSection>(*CS); + }); + + Obj.removeSections(RemovePred); + + if (!Config.SectionsToRename.empty()) { + for (auto &Sec : Obj.sections()) { + const auto Iter = Config.SectionsToRename.find(Sec.Name); + if (Iter != Config.SectionsToRename.end()) { + const SectionRename &SR = Iter->second; + Sec.Name = SR.NewName; + if (SR.NewFlags.hasValue()) { + // Preserve some flags which should not be dropped when setting flags. + // Also, preserve anything OS/processor dependant. + const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE | + ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | + ELF::SHF_MASKOS | ELF::SHF_MASKPROC | + ELF::SHF_TLS | ELF::SHF_INFO_LINK; + Sec.Flags = (Sec.Flags & PreserveMask) | + (SR.NewFlags.getValue() & ~PreserveMask); + } + } + } + } + + if (!Config.AddSection.empty()) { + for (const auto &Flag : Config.AddSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(File); + if (!BufOrErr) + reportError(File, BufOrErr.getError()); + std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); + ArrayRef<uint8_t> Data( + reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), + Buf->getBufferSize()); + OwnedDataSection &NewSection = + Obj.addSection<OwnedDataSection>(SecName, Data); + if (SecName.startswith(".note") && SecName != ".note.GNU-stack") + NewSection.Type = SHT_NOTE; + } + } + + if (!Config.DumpSection.empty()) { + for (const auto &Flag : Config.DumpSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + if (Error E = dumpSectionToFile(SecName, File, Obj)) + reportError(Config.InputFilename, std::move(E)); + } + } + + if (!Config.AddGnuDebugLink.empty()) + Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink); +} + +void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + BinaryReader Reader(Config.BinaryArch, &In); + std::unique_ptr<Object> Obj = Reader.create(); + + // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch + // (-B<arch>). + const ElfType OutputElfType = getOutputElfType( + Config.OutputArch ? Config.OutputArch.getValue() : Config.BinaryArch); + handleArgs(Config, *Obj, Reader, OutputElfType); + std::unique_ptr<Writer> Writer = + createWriter(Config, *Obj, Out, OutputElfType); + Writer->finalize(); + Writer->write(); +} + +void executeObjcopyOnBinary(const CopyConfig &Config, + object::ELFObjectFileBase &In, Buffer &Out) { + ELFReader Reader(&In); + std::unique_ptr<Object> Obj = Reader.create(); + // Prefer OutputArch (-O<format>) if set, otherwise infer it from the input. + const ElfType OutputElfType = + Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) + : getOutputElfType(In); + ArrayRef<uint8_t> BuildIdBytes; + + if (!Config.BuildIdLinkDir.empty()) { + BuildIdBytes = unwrapOrError(findBuildID(In)); + if (BuildIdBytes.size() < 2) + error("build ID in file '" + Config.InputFilename + + "' is smaller than two bytes"); + } + + if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) { + linkToBuildIdDir(Config, Config.InputFilename, + Config.BuildIdLinkInput.getValue(), BuildIdBytes); + } + handleArgs(Config, *Obj, Reader, OutputElfType); + std::unique_ptr<Writer> Writer = + createWriter(Config, *Obj, Out, OutputElfType); + Writer->finalize(); + Writer->write(); + if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) { + linkToBuildIdDir(Config, Config.OutputFilename, + Config.BuildIdLinkOutput.getValue(), BuildIdBytes); + } +} + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h b/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h new file mode 100644 index 000000000000..43f41c00ce5b --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h @@ -0,0 +1,34 @@ +//===- ELFObjcopy.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_TOOLS_OBJCOPY_ELFOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H + +namespace llvm { +class MemoryBuffer; + +namespace object { +class ELFObjectFileBase; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace elf { +void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out); +void executeObjcopyOnBinary(const CopyConfig &Config, + object::ELFObjectFileBase &In, Buffer &Out); + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H diff --git a/contrib/llvm/tools/llvm-objcopy/Object.cpp b/contrib/llvm/tools/llvm-objcopy/ELF/Object.cpp index 7e88f5263a39..3d3e029c09eb 100644 --- a/contrib/llvm/tools/llvm-objcopy/Object.cpp +++ b/contrib/llvm/tools/llvm-objcopy/ELF/Object.cpp @@ -15,7 +15,9 @@ #include "llvm/ADT/Twine.h" #include "llvm/ADT/iterator_range.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCTargetOptions.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Path.h" @@ -26,45 +28,14 @@ #include <utility> #include <vector> -using namespace llvm; -using namespace llvm::objcopy; +namespace llvm { +namespace objcopy { +namespace elf { + using namespace object; using namespace ELF; -Buffer::~Buffer() {} - -void FileBuffer::allocate(size_t Size) { - Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable); - handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &E) { - error("failed to open " + getName() + ": " + E.message()); - }); - Buf = std::move(*BufferOrErr); -} - -Error FileBuffer::commit() { return Buf->commit(); } - -uint8_t *FileBuffer::getBufferStart() { - return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); -} - -void MemBuffer::allocate(size_t Size) { - Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName()); -} - -Error MemBuffer::commit() { return Error::success(); } - -uint8_t *MemBuffer::getBufferStart() { - return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); -} - -std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() { - return std::move(Buf); -} - template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) { - using Elf_Phdr = typename ELFT::Phdr; - uint8_t *B = Buf.getBufferStart(); B += Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr); Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B); @@ -87,7 +58,7 @@ void SectionBase::markSymbols() {} template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) { uint8_t *B = Buf.getBufferStart(); B += Sec.HeaderOffset; - typename ELFT::Shdr &Shdr = *reinterpret_cast<typename ELFT::Shdr *>(B); + Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B); Shdr.sh_name = Sec.NameIndex; Shdr.sh_type = Sec.Type; Shdr.sh_flags = Sec.Flags; @@ -100,7 +71,46 @@ template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) { Shdr.sh_entsize = Sec.EntrySize; } -SectionVisitor::~SectionVisitor() {} +template <class ELFT> void ELFSectionSizer<ELFT>::visit(Section &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(OwnedDataSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(StringTableSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(DynamicRelocationSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(SymbolTableSection &Sec) { + Sec.EntrySize = sizeof(Elf_Sym); + Sec.Size = Sec.Symbols.size() * Sec.EntrySize; + // Align to the largest field in Elf_Sym. + Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word); +} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(RelocationSection &Sec) { + Sec.EntrySize = Sec.Type == SHT_REL ? sizeof(Elf_Rel) : sizeof(Elf_Rela); + Sec.Size = Sec.Relocations.size() * Sec.EntrySize; + // Align to the largest field in Elf_Rel(a). + Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word); +} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(GnuDebugLinkSection &Sec) {} + +template <class ELFT> void ELFSectionSizer<ELFT>::visit(GroupSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(SectionIndexSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(CompressedSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(DecompressedSection &Sec) {} void BinarySectionWriter::visit(const SectionIndexSection &Sec) { error("Cannot write symbol section index table '" + Sec.Name + "' "); @@ -126,20 +136,169 @@ void SectionWriter::visit(const Section &Sec) { if (Sec.Type == SHT_NOBITS) return; uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - std::copy(std::begin(Sec.Contents), std::end(Sec.Contents), Buf); + llvm::copy(Sec.Contents, Buf); } void Section::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void Section::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); } + void SectionWriter::visit(const OwnedDataSection &Sec) { uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - std::copy(std::begin(Sec.Data), std::end(Sec.Data), Buf); + llvm::copy(Sec.Data, Buf); +} + +static const std::vector<uint8_t> ZlibGnuMagic = {'Z', 'L', 'I', 'B'}; + +static bool isDataGnuCompressed(ArrayRef<uint8_t> Data) { + return Data.size() > ZlibGnuMagic.size() && + std::equal(ZlibGnuMagic.begin(), ZlibGnuMagic.end(), Data.data()); +} + +template <class ELFT> +static std::tuple<uint64_t, uint64_t> +getDecompressedSizeAndAlignment(ArrayRef<uint8_t> Data) { + const bool IsGnuDebug = isDataGnuCompressed(Data); + const uint64_t DecompressedSize = + IsGnuDebug + ? support::endian::read64be(reinterpret_cast<const uint64_t *>( + Data.data() + ZlibGnuMagic.size())) + : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data())->ch_size; + const uint64_t DecompressedAlign = + IsGnuDebug ? 1 + : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data()) + ->ch_addralign; + + return std::make_tuple(DecompressedSize, DecompressedAlign); +} + +template <class ELFT> +void ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) { + uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + + if (!zlib::isAvailable()) { + std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); + return; + } + + const size_t DataOffset = isDataGnuCompressed(Sec.OriginalData) + ? (ZlibGnuMagic.size() + sizeof(Sec.Size)) + : sizeof(Elf_Chdr_Impl<ELFT>); + + StringRef CompressedContent( + reinterpret_cast<const char *>(Sec.OriginalData.data()) + DataOffset, + Sec.OriginalData.size() - DataOffset); + + SmallVector<char, 128> DecompressedContent; + if (Error E = zlib::uncompress(CompressedContent, DecompressedContent, + static_cast<size_t>(Sec.Size))) + reportError(Sec.Name, std::move(E)); + + std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); +} + +void BinarySectionWriter::visit(const DecompressedSection &Sec) { + error("Cannot write compressed section '" + Sec.Name + "' "); +} + +void DecompressedSection::accept(SectionVisitor &Visitor) const { + Visitor.visit(*this); +} + +void DecompressedSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); } void OwnedDataSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void OwnedDataSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + +void BinarySectionWriter::visit(const CompressedSection &Sec) { + error("Cannot write compressed section '" + Sec.Name + "' "); +} + +template <class ELFT> +void ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) { + uint8_t *Buf = Out.getBufferStart(); + Buf += Sec.Offset; + + if (Sec.CompressionType == DebugCompressionType::None) { + std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); + return; + } + + if (Sec.CompressionType == DebugCompressionType::GNU) { + const char *Magic = "ZLIB"; + memcpy(Buf, Magic, strlen(Magic)); + Buf += strlen(Magic); + const uint64_t DecompressedSize = + support::endian::read64be(&Sec.DecompressedSize); + memcpy(Buf, &DecompressedSize, sizeof(DecompressedSize)); + Buf += sizeof(DecompressedSize); + } else { + Elf_Chdr_Impl<ELFT> Chdr; + Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB; + Chdr.ch_size = Sec.DecompressedSize; + Chdr.ch_addralign = Sec.DecompressedAlign; + memcpy(Buf, &Chdr, sizeof(Chdr)); + Buf += sizeof(Chdr); + } + + std::copy(Sec.CompressedData.begin(), Sec.CompressedData.end(), Buf); +} + +CompressedSection::CompressedSection(const SectionBase &Sec, + DebugCompressionType CompressionType) + : SectionBase(Sec), CompressionType(CompressionType), + DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) { + + if (!zlib::isAvailable()) { + CompressionType = DebugCompressionType::None; + return; + } + + if (Error E = zlib::compress( + StringRef(reinterpret_cast<const char *>(OriginalData.data()), + OriginalData.size()), + CompressedData)) + reportError(Name, std::move(E)); + + size_t ChdrSize; + if (CompressionType == DebugCompressionType::GNU) { + Name = ".z" + Sec.Name.substr(1); + ChdrSize = sizeof("ZLIB") - 1 + sizeof(uint64_t); + } else { + Flags |= ELF::SHF_COMPRESSED; + ChdrSize = + std::max(std::max(sizeof(object::Elf_Chdr_Impl<object::ELF64LE>), + sizeof(object::Elf_Chdr_Impl<object::ELF64BE>)), + std::max(sizeof(object::Elf_Chdr_Impl<object::ELF32LE>), + sizeof(object::Elf_Chdr_Impl<object::ELF32BE>))); + } + Size = ChdrSize + CompressedData.size(); + Align = 8; +} + +CompressedSection::CompressedSection(ArrayRef<uint8_t> CompressedData, + uint64_t DecompressedSize, + uint64_t DecompressedAlign) + : CompressionType(DebugCompressionType::None), + DecompressedSize(DecompressedSize), DecompressedAlign(DecompressedAlign) { + OriginalData = CompressedData; +} + +void CompressedSection::accept(SectionVisitor &Visitor) const { + Visitor.visit(*this); +} + +void CompressedSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + void StringTableSection::addString(StringRef Name) { StrTabBuilder.add(Name); Size = StrTabBuilder.getSize(); @@ -159,11 +318,15 @@ void StringTableSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void StringTableSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + template <class ELFT> void ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) { uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - auto *IndexesBuffer = reinterpret_cast<typename ELFT::Word *>(Buf); - std::copy(std::begin(Sec.Indexes), std::end(Sec.Indexes), IndexesBuffer); + auto *IndexesBuffer = reinterpret_cast<Elf_Word *>(Buf); + llvm::copy(Sec.Indexes, IndexesBuffer); } void SectionIndexSection::initialize(SectionTableRef SecTable) { @@ -182,6 +345,10 @@ void SectionIndexSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void SectionIndexSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) { switch (Index) { case SHN_ABS: @@ -226,18 +393,20 @@ uint16_t Symbol::getShndx() const { llvm_unreachable("Symbol with invalid ShndxType encountered"); } +bool Symbol::isCommon() const { return getShndx() == SHN_COMMON; } + void SymbolTableSection::assignIndices() { uint32_t Index = 0; for (auto &Sym : Symbols) Sym->Index = Index++; } -void SymbolTableSection::addSymbol(StringRef Name, uint8_t Bind, uint8_t Type, +void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility, uint16_t Shndx, - uint64_t Sz) { + uint64_t Size) { Symbol Sym; - Sym.Name = Name; + Sym.Name = Name.str(); Sym.Binding = Bind; Sym.Type = Type; Sym.DefinedIn = DefinedIn; @@ -251,7 +420,7 @@ void SymbolTableSection::addSymbol(StringRef Name, uint8_t Bind, uint8_t Type, } Sym.Value = Value; Sym.Visibility = Visibility; - Sym.Size = Sz; + Sym.Size = Size; Sym.Index = Symbols.size(); Symbols.emplace_back(llvm::make_unique<Symbol>(Sym)); Size += this->EntrySize; @@ -344,7 +513,7 @@ template <class ELFT> void ELFSectionWriter<ELFT>::visit(const SymbolTableSection &Sec) { uint8_t *Buf = Out.getBufferStart(); Buf += Sec.Offset; - typename ELFT::Sym *Sym = reinterpret_cast<typename ELFT::Sym *>(Buf); + Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Buf); // Loop though symbols setting each entry of the symbol table. for (auto &Symbol : Sec.Symbols) { Sym->st_name = Symbol->NameIndex; @@ -362,6 +531,10 @@ void SymbolTableSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void SymbolTableSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + template <class SymTabType> void RelocSectionWithSymtabBase<SymTabType>::removeSectionReferences( const SectionBase *Sec) { @@ -377,11 +550,13 @@ void RelocSectionWithSymtabBase<SymTabType>::removeSectionReferences( template <class SymTabType> void RelocSectionWithSymtabBase<SymTabType>::initialize( SectionTableRef SecTable) { - setSymTab(SecTable.getSectionOfType<SymTabType>( - Link, - "Link field value " + Twine(Link) + " in section " + Name + " is invalid", - "Link field value " + Twine(Link) + " in section " + Name + - " is not a symbol table")); + if (Link != SHN_UNDEF) + setSymTab(SecTable.getSectionOfType<SymTabType>( + Link, + "Link field value " + Twine(Link) + " in section " + Name + + " is invalid", + "Link field value " + Twine(Link) + " in section " + Name + + " is not a symbol table")); if (Info != SHN_UNDEF) setSection(SecTable.getSection(Info, "Info field value " + Twine(Info) + @@ -393,7 +568,8 @@ void RelocSectionWithSymtabBase<SymTabType>::initialize( template <class SymTabType> void RelocSectionWithSymtabBase<SymTabType>::finalize() { - this->Link = Symbols->Index; + this->Link = Symbols ? Symbols->Index : 0; + if (SecToApplyRel != nullptr) this->Info = SecToApplyRel->Index; } @@ -429,11 +605,15 @@ void RelocationSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void RelocationSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + void RelocationSection::removeSymbols( function_ref<bool(const Symbol &)> ToRemove) { for (const Relocation &Reloc : Relocations) if (ToRemove(*Reloc.RelocSymbol)) - error("not stripping symbol `" + Reloc.RelocSymbol->Name + + error("not stripping symbol '" + Reloc.RelocSymbol->Name + "' because it is named in a relocation"); } @@ -443,7 +623,7 @@ void RelocationSection::markSymbols() { } void SectionWriter::visit(const DynamicRelocationSection &Sec) { - std::copy(std::begin(Sec.Contents), std::end(Sec.Contents), + llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset); } @@ -451,6 +631,10 @@ void DynamicRelocationSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void DynamicRelocationSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + void Section::removeSectionReferences(const SectionBase *Sec) { if (LinkSection == Sec) { error("Section " + LinkSection->Name + @@ -506,12 +690,12 @@ void GnuDebugLinkSection::init(StringRef File, StringRef Data) { // establish the order that sections should go in. By using the maximum // possible offset we cause this section to wind up at the end. OriginalOffset = std::numeric_limits<uint64_t>::max(); - JamCRC crc; - crc.update(ArrayRef<char>(Data.data(), Data.size())); + JamCRC CRC; + CRC.update(ArrayRef<char>(Data.data(), Data.size())); // The CRC32 value needs to be complemented because the JamCRC dosn't // finalize the CRC32 value. It also dosn't negate the initial CRC32 value // but it starts by default at 0xFFFFFFFF which is the complement of zero. - CRC32 = ~crc.getCRC(); + CRC32 = ~CRC.getCRC(); } GnuDebugLinkSection::GnuDebugLinkSection(StringRef File) : FileName(File) { @@ -530,13 +714,17 @@ void ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) { Elf_Word *CRC = reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word)); *CRC = Sec.CRC32; - std::copy(std::begin(Sec.FileName), std::end(Sec.FileName), File); + llvm::copy(Sec.FileName, File); } void GnuDebugLinkSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void GnuDebugLinkSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + template <class ELFT> void ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) { ELF::Elf32_Word *Buf = @@ -550,6 +738,10 @@ void GroupSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void GroupSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + // Returns true IFF a section is wholly inside the range of a segment static bool sectionWithinSegment(const SectionBase &Section, const Segment &Segment) { @@ -589,6 +781,79 @@ static bool compareSegmentsByPAddr(const Segment *A, const Segment *B) { return A->Index < B->Index; } +void BinaryELFBuilder::initFileHeader() { + Obj->Flags = 0x0; + Obj->Type = ET_REL; + Obj->OSABI = ELFOSABI_NONE; + Obj->ABIVersion = 0; + Obj->Entry = 0x0; + Obj->Machine = EMachine; + Obj->Version = 1; +} + +void BinaryELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; } + +StringTableSection *BinaryELFBuilder::addStrTab() { + auto &StrTab = Obj->addSection<StringTableSection>(); + StrTab.Name = ".strtab"; + + Obj->SectionNames = &StrTab; + return &StrTab; +} + +SymbolTableSection *BinaryELFBuilder::addSymTab(StringTableSection *StrTab) { + auto &SymTab = Obj->addSection<SymbolTableSection>(); + + SymTab.Name = ".symtab"; + SymTab.Link = StrTab->Index; + + // The symbol table always needs a null symbol + SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0); + + Obj->SymbolTable = &SymTab; + return &SymTab; +} + +void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { + auto Data = ArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(MemBuf->getBufferStart()), + MemBuf->getBufferSize()); + auto &DataSection = Obj->addSection<Section>(Data); + DataSection.Name = ".data"; + DataSection.Type = ELF::SHT_PROGBITS; + DataSection.Size = Data.size(); + DataSection.Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE; + + std::string SanitizedFilename = MemBuf->getBufferIdentifier().str(); + std::replace_if(std::begin(SanitizedFilename), std::end(SanitizedFilename), + [](char C) { return !isalnum(C); }, '_'); + Twine Prefix = Twine("_binary_") + SanitizedFilename; + + SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection, + /*Value=*/0, STV_DEFAULT, 0, 0); + SymTab->addSymbol(Prefix + "_end", STB_GLOBAL, STT_NOTYPE, &DataSection, + /*Value=*/DataSection.Size, STV_DEFAULT, 0, 0); + SymTab->addSymbol(Prefix + "_size", STB_GLOBAL, STT_NOTYPE, nullptr, + /*Value=*/DataSection.Size, STV_DEFAULT, SHN_ABS, 0); +} + +void BinaryELFBuilder::initSections() { + for (auto &Section : Obj->sections()) { + Section.initialize(Obj->sections()); + } +} + +std::unique_ptr<Object> BinaryELFBuilder::build() { + initFileHeader(); + initHeaderSegment(); + StringTableSection *StrTab = addStrTab(); + SymbolTableSection *SymTab = addSymTab(StrTab); + initSections(); + addData(SymTab); + + return std::move(Obj); +} + template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) { for (auto &Parent : Obj.segments()) { // Every segment will overlap with itself but we don't want a segment to @@ -633,15 +898,6 @@ template <class ELFT> void ELFBuilder<ELFT>::readProgramHeaders() { } auto &ElfHdr = Obj.ElfHdrSegment; - // Creating multiple PT_PHDR segments technically is not valid, but PT_LOAD - // segments must not overlap, and other types fit even less. - ElfHdr.Type = PT_PHDR; - ElfHdr.Flags = 0; - ElfHdr.OriginalOffset = ElfHdr.Offset = 0; - ElfHdr.VAddr = 0; - ElfHdr.PAddr = 0; - ElfHdr.FileSize = ElfHdr.MemSize = sizeof(Elf_Ehdr); - ElfHdr.Align = 0; ElfHdr.Index = Index++; const auto &Ehdr = *ElfFile.getHeader(); @@ -725,8 +981,7 @@ void ELFBuilder<ELFT>::initSymbolTable(SymbolTableSection *SymTab) { Elf_Word Index = ShndxData[&Sym - Symbols.begin()]; DefSection = Obj.sections().getSection( Index, - "Symbol '" + Name + "' has invalid section index " + - Twine(Index)); + "Symbol '" + Name + "' has invalid section index " + Twine(Index)); } else if (Sym.st_shndx >= SHN_LORESERVE) { if (!isValidReservedSectionIndex(Sym.st_shndx, Obj.Machine)) { error( @@ -828,10 +1083,20 @@ SectionBase &ELFBuilder<ELFT>::makeSection(const Elf_Shdr &Shdr) { } case SHT_NOBITS: return Obj.addSection<Section>(Data); - default: + default: { Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); + + if (isDataGnuCompressed(Data) || (Shdr.sh_flags & ELF::SHF_COMPRESSED)) { + uint64_t DecompressedSize, DecompressedAlign; + std::tie(DecompressedSize, DecompressedAlign) = + getDecompressedSizeAndAlignment<ELFT>(Data); + return Obj.addSection<CompressedSection>(Data, DecompressedSize, + DecompressedAlign); + } + return Obj.addSection<Section>(Data); } + } } template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { @@ -854,6 +1119,9 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { Sec.Align = Shdr.sh_addralign; Sec.EntrySize = Shdr.sh_entsize; Sec.Index = Index++; + Sec.OriginalData = + ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset, + (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size); } // If a section index table exists we'll need to initialize it before we @@ -894,7 +1162,8 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { template <class ELFT> void ELFBuilder<ELFT>::build() { const auto &Ehdr = *ElfFile.getHeader(); - std::copy(Ehdr.e_ident, Ehdr.e_ident + 16, Obj.Ident); + Obj.OSABI = Ehdr.e_ident[EI_OSABI]; + Obj.ABIVersion = Ehdr.e_ident[EI_ABIVERSION]; Obj.Type = Ehdr.e_type; Obj.Machine = Ehdr.e_machine; Obj.Version = Ehdr.e_version; @@ -926,34 +1195,26 @@ Writer::~Writer() {} Reader::~Reader() {} -ElfType ELFReader::getElfType() const { - if (isa<ELFObjectFile<ELF32LE>>(Bin)) - return ELFT_ELF32LE; - if (isa<ELFObjectFile<ELF64LE>>(Bin)) - return ELFT_ELF64LE; - if (isa<ELFObjectFile<ELF32BE>>(Bin)) - return ELFT_ELF32BE; - if (isa<ELFObjectFile<ELF64BE>>(Bin)) - return ELFT_ELF64BE; - llvm_unreachable("Invalid ELFType"); +std::unique_ptr<Object> BinaryReader::create() const { + return BinaryELFBuilder(MInfo.EMachine, MemBuf).build(); } std::unique_ptr<Object> ELFReader::create() const { auto Obj = llvm::make_unique<Object>(); - if (auto *o = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { - ELFBuilder<ELF32LE> Builder(*o, *Obj); + if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { + ELFBuilder<ELF32LE> Builder(*O, *Obj); Builder.build(); return Obj; - } else if (auto *o = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { - ELFBuilder<ELF64LE> Builder(*o, *Obj); + } else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { + ELFBuilder<ELF64LE> Builder(*O, *Obj); Builder.build(); return Obj; - } else if (auto *o = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { - ELFBuilder<ELF32BE> Builder(*o, *Obj); + } else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { + ELFBuilder<ELF32BE> Builder(*O, *Obj); Builder.build(); return Obj; - } else if (auto *o = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { - ELFBuilder<ELF64BE> Builder(*o, *Obj); + } else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { + ELFBuilder<ELF64BE> Builder(*O, *Obj); Builder.build(); return Obj; } @@ -963,18 +1224,31 @@ std::unique_ptr<Object> ELFReader::create() const { template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { uint8_t *B = Buf.getBufferStart(); Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(B); - std::copy(Obj.Ident, Obj.Ident + 16, Ehdr.e_ident); + std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0); + Ehdr.e_ident[EI_MAG0] = 0x7f; + Ehdr.e_ident[EI_MAG1] = 'E'; + Ehdr.e_ident[EI_MAG2] = 'L'; + Ehdr.e_ident[EI_MAG3] = 'F'; + Ehdr.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; + Ehdr.e_ident[EI_DATA] = + ELFT::TargetEndianness == support::big ? ELFDATA2MSB : ELFDATA2LSB; + Ehdr.e_ident[EI_VERSION] = EV_CURRENT; + Ehdr.e_ident[EI_OSABI] = Obj.OSABI; + Ehdr.e_ident[EI_ABIVERSION] = Obj.ABIVersion; + Ehdr.e_type = Obj.Type; Ehdr.e_machine = Obj.Machine; Ehdr.e_version = Obj.Version; Ehdr.e_entry = Obj.Entry; - Ehdr.e_phoff = Obj.ProgramHdrSegment.Offset; + // We have to use the fully-qualified name llvm::size + // since some compilers complain on ambiguous resolution. + Ehdr.e_phnum = llvm::size(Obj.segments()); + Ehdr.e_phoff = (Ehdr.e_phnum != 0) ? Obj.ProgramHdrSegment.Offset : 0; + Ehdr.e_phentsize = (Ehdr.e_phnum != 0) ? sizeof(Elf_Phdr) : 0; Ehdr.e_flags = Obj.Flags; Ehdr.e_ehsize = sizeof(Elf_Ehdr); - Ehdr.e_phentsize = sizeof(Elf_Phdr); - Ehdr.e_phnum = size(Obj.segments()); - Ehdr.e_shentsize = sizeof(Elf_Shdr); - if (WriteSectionHeaders) { + if (WriteSectionHeaders && size(Obj.sections()) != 0) { + Ehdr.e_shentsize = sizeof(Elf_Shdr); Ehdr.e_shoff = Obj.SHOffset; // """ // If the number of sections is greater than or equal to @@ -998,6 +1272,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { else Ehdr.e_shstrndx = Obj.SectionNames->Index; } else { + Ehdr.e_shentsize = 0; Ehdr.e_shoff = 0; Ehdr.e_shnum = 0; Ehdr.e_shstrndx = 0; @@ -1106,7 +1381,7 @@ static uint64_t alignToAddr(uint64_t Offset, uint64_t Addr, uint64_t Align) { } // Orders segments such that if x = y->ParentSegment then y comes before x. -static void OrderSegments(std::vector<Segment *> &Segments) { +static void orderSegments(std::vector<Segment *> &Segments) { std::stable_sort(std::begin(Segments), std::end(Segments), compareSegmentsByOffset); } @@ -1148,7 +1423,7 @@ static uint64_t LayoutSegments(std::vector<Segment *> &Segments, // sections had a ParentSegment or an offset one past the last section if there // was a section that didn't have a ParentSegment. template <class Range> -static uint64_t LayoutSections(Range Sections, uint64_t Offset) { +static uint64_t layoutSections(Range Sections, uint64_t Offset) { // Now the offset of every segment has been set we can assign the offsets // of each section. For sections that are covered by a segment we should use // the segment's original offset and the section's original offset to compute @@ -1172,6 +1447,17 @@ static uint64_t LayoutSections(Range Sections, uint64_t Offset) { return Offset; } +template <class ELFT> void ELFWriter<ELFT>::initEhdrSegment() { + auto &ElfHdr = Obj.ElfHdrSegment; + ElfHdr.Type = PT_PHDR; + ElfHdr.Flags = 0; + ElfHdr.OriginalOffset = ElfHdr.Offset = 0; + ElfHdr.VAddr = 0; + ElfHdr.PAddr = 0; + ElfHdr.FileSize = ElfHdr.MemSize = sizeof(Elf_Ehdr); + ElfHdr.Align = 0; +} + template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { // We need a temporary list of segments that has a special order to it // so that we know that anytime ->ParentSegment is set that segment has @@ -1181,17 +1467,17 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { OrderedSegments.push_back(&Segment); OrderedSegments.push_back(&Obj.ElfHdrSegment); OrderedSegments.push_back(&Obj.ProgramHdrSegment); - OrderSegments(OrderedSegments); + orderSegments(OrderedSegments); // Offset is used as the start offset of the first segment to be laid out. // Since the ELF Header (ElfHdrSegment) must be at the start of the file, // we start at offset 0. uint64_t Offset = 0; Offset = LayoutSegments(OrderedSegments, Offset); - Offset = LayoutSections(Obj.sections(), Offset); + Offset = layoutSections(Obj.sections(), Offset); // If we need to write the section header table out then we need to align the // Offset so that SHOffset is valid. if (WriteSectionHeaders) - Offset = alignTo(Offset, sizeof(typename ELFT::Addr)); + Offset = alignTo(Offset, sizeof(Elf_Addr)); Obj.SHOffset = Offset; } @@ -1263,10 +1549,17 @@ template <class ELFT> void ELFWriter<ELFT>::finalize() { Obj.SectionNames->addString(Section.Name); } + initEhdrSegment(); + // Before we can prepare for layout the indexes need to be finalized. + // Also, the output arch may not be the same as the input arch, so fix up + // size-related fields before doing layout calculations. uint64_t Index = 0; - for (auto &Sec : Obj.sections()) + auto SecSizer = llvm::make_unique<ELFSectionSizer<ELFT>>(); + for (auto &Sec : Obj.sections()) { Sec.Index = Index++; + Sec.accept(*SecSizer); + } // The symbol table does not update all other sections on update. For // instance, symbol names are not added as new symbols are added. This means @@ -1324,10 +1617,10 @@ void BinaryWriter::finalize() { // loading and physical addresses are intended for ROM loading. // However, if no segment has a physical address, we'll fallback to using // virtual addresses for all. - if (std::all_of(std::begin(OrderedSegments), std::end(OrderedSegments), - [](const Segment *Segment) { return Segment->PAddr == 0; })) - for (const auto &Segment : OrderedSegments) - Segment->PAddr = Segment->VAddr; + if (all_of(OrderedSegments, + [](const Segment *Seg) { return Seg->PAddr == 0; })) + for (Segment *Seg : OrderedSegments) + Seg->PAddr = Seg->VAddr; std::stable_sort(std::begin(OrderedSegments), std::end(OrderedSegments), compareSegmentsByPAddr); @@ -1342,8 +1635,8 @@ void BinaryWriter::finalize() { uint64_t Offset = 0; // Modify the first segment so that there is no gap at the start. This allows - // our layout algorithm to proceed as expected while not out writing out the - // gap at the start. + // our layout algorithm to proceed as expected while not writing out the gap + // at the start. if (!OrderedSegments.empty()) { auto Seg = OrderedSegments[0]; auto Sec = Seg->firstSection(); @@ -1371,7 +1664,7 @@ void BinaryWriter::finalize() { continue; AllocatedSections.push_back(&Section); } - LayoutSections(make_pointee_range(AllocatedSections), Offset); + layoutSections(make_pointee_range(AllocatedSections), Offset); // Now that every section has been laid out we just need to compute the total // file size. This might not be the same as the offset returned by @@ -1387,9 +1680,6 @@ void BinaryWriter::finalize() { SecWriter = llvm::make_unique<BinarySectionWriter>(Buf); } -namespace llvm { -namespace objcopy { - template class ELFBuilder<ELF64LE>; template class ELFBuilder<ELF64BE>; template class ELFBuilder<ELF32LE>; @@ -1399,5 +1689,7 @@ template class ELFWriter<ELF64LE>; template class ELFWriter<ELF64BE>; template class ELFWriter<ELF32LE>; template class ELFWriter<ELF32BE>; + +} // end namespace elf } // end namespace objcopy } // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/Object.h b/contrib/llvm/tools/llvm-objcopy/ELF/Object.h index 76748d5fc641..e5730cd543ee 100644 --- a/contrib/llvm/tools/llvm-objcopy/Object.h +++ b/contrib/llvm/tools/llvm-objcopy/ELF/Object.h @@ -10,6 +10,8 @@ #ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H #define LLVM_TOOLS_OBJCOPY_OBJECT_H +#include "Buffer.h" +#include "CopyConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -26,9 +28,10 @@ #include <vector> namespace llvm { +enum class DebugCompressionType; namespace objcopy { +namespace elf { -class Buffer; class SectionBase; class Section; class OwnedDataSection; @@ -39,6 +42,8 @@ class DynamicRelocationSection; class GnuDebugLinkSection; class GroupSection; class SectionIndexSection; +class CompressedSection; +class DecompressedSection; class Segment; class Object; struct Symbol; @@ -66,7 +71,7 @@ enum ElfType { ELFT_ELF32LE, ELFT_ELF64LE, ELFT_ELF32BE, ELFT_ELF64BE }; class SectionVisitor { public: - virtual ~SectionVisitor(); + virtual ~SectionVisitor() = default; virtual void visit(const Section &Sec) = 0; virtual void visit(const OwnedDataSection &Sec) = 0; @@ -77,6 +82,25 @@ public: virtual void visit(const GnuDebugLinkSection &Sec) = 0; virtual void visit(const GroupSection &Sec) = 0; virtual void visit(const SectionIndexSection &Sec) = 0; + virtual void visit(const CompressedSection &Sec) = 0; + virtual void visit(const DecompressedSection &Sec) = 0; +}; + +class MutableSectionVisitor { +public: + virtual ~MutableSectionVisitor() = default; + + virtual void visit(Section &Sec) = 0; + virtual void visit(OwnedDataSection &Sec) = 0; + virtual void visit(StringTableSection &Sec) = 0; + virtual void visit(SymbolTableSection &Sec) = 0; + virtual void visit(RelocationSection &Sec) = 0; + virtual void visit(DynamicRelocationSection &Sec) = 0; + virtual void visit(GnuDebugLinkSection &Sec) = 0; + virtual void visit(GroupSection &Sec) = 0; + virtual void visit(SectionIndexSection &Sec) = 0; + virtual void visit(CompressedSection &Sec) = 0; + virtual void visit(DecompressedSection &Sec) = 0; }; class SectionWriter : public SectionVisitor { @@ -95,6 +119,8 @@ public: virtual void visit(const GnuDebugLinkSection &Sec) override = 0; virtual void visit(const GroupSection &Sec) override = 0; virtual void visit(const SectionIndexSection &Sec) override = 0; + virtual void visit(const CompressedSection &Sec) override = 0; + virtual void visit(const DecompressedSection &Sec) override = 0; explicit SectionWriter(Buffer &Buf) : Out(Buf) {} }; @@ -104,6 +130,7 @@ private: using Elf_Word = typename ELFT::Word; using Elf_Rel = typename ELFT::Rel; using Elf_Rela = typename ELFT::Rela; + using Elf_Sym = typename ELFT::Sym; public: virtual ~ELFSectionWriter() {} @@ -112,13 +139,38 @@ public: void visit(const GnuDebugLinkSection &Sec) override; void visit(const GroupSection &Sec) override; void visit(const SectionIndexSection &Sec) override; + void visit(const CompressedSection &Sec) override; + void visit(const DecompressedSection &Sec) override; explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {} }; +template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor { +private: + using Elf_Rel = typename ELFT::Rel; + using Elf_Rela = typename ELFT::Rela; + using Elf_Sym = typename ELFT::Sym; + using Elf_Word = typename ELFT::Word; + using Elf_Xword = typename ELFT::Xword; + +public: + void visit(Section &Sec) override; + void visit(OwnedDataSection &Sec) override; + void visit(StringTableSection &Sec) override; + void visit(DynamicRelocationSection &Sec) override; + void visit(SymbolTableSection &Sec) override; + void visit(RelocationSection &Sec) override; + void visit(GnuDebugLinkSection &Sec) override; + void visit(GroupSection &Sec) override; + void visit(SectionIndexSection &Sec) override; + void visit(CompressedSection &Sec) override; + void visit(DecompressedSection &Sec) override; +}; + #define MAKE_SEC_WRITER_FRIEND \ friend class SectionWriter; \ - template <class ELFT> friend class ELFSectionWriter; + template <class ELFT> friend class ELFSectionWriter; \ + template <class ELFT> friend class ELFSectionSizer; class BinarySectionWriter : public SectionWriter { public: @@ -129,52 +181,12 @@ public: void visit(const GnuDebugLinkSection &Sec) override; void visit(const GroupSection &Sec) override; void visit(const SectionIndexSection &Sec) override; + void visit(const CompressedSection &Sec) override; + void visit(const DecompressedSection &Sec) override; explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {} }; -// The class Buffer abstracts out the common interface of FileOutputBuffer and -// WritableMemoryBuffer so that the hierarchy of Writers depends on this -// abstract interface and doesn't depend on a particular implementation. -// TODO: refactor the buffer classes in LLVM to enable us to use them here -// directly. -class Buffer { - StringRef Name; - -public: - virtual ~Buffer(); - virtual void allocate(size_t Size) = 0; - virtual uint8_t *getBufferStart() = 0; - virtual Error commit() = 0; - - explicit Buffer(StringRef Name) : Name(Name) {} - StringRef getName() const { return Name; } -}; - -class FileBuffer : public Buffer { - std::unique_ptr<FileOutputBuffer> Buf; - -public: - void allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit FileBuffer(StringRef FileName) : Buffer(FileName) {} -}; - -class MemBuffer : public Buffer { - std::unique_ptr<WritableMemoryBuffer> Buf; - -public: - void allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit MemBuffer(StringRef Name) : Buffer(Name) {} - - std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer(); -}; - class Writer { protected: Object &Obj; @@ -190,10 +202,13 @@ public: template <class ELFT> class ELFWriter : public Writer { private: + using Elf_Addr = typename ELFT::Addr; using Elf_Shdr = typename ELFT::Shdr; using Elf_Phdr = typename ELFT::Phdr; using Elf_Ehdr = typename ELFT::Ehdr; + void initEhdrSegment(); + void writeEhdr(); void writePhdr(const Segment &Seg); void writeShdr(const SectionBase &Sec); @@ -233,7 +248,7 @@ public: class SectionBase { public: - StringRef Name; + std::string Name; Segment *ParentSegment = nullptr; uint64_t HeaderOffset; uint64_t OriginalOffset = std::numeric_limits<uint64_t>::max(); @@ -250,6 +265,10 @@ public: uint64_t Offset = 0; uint64_t Size = 0; uint64_t Type = ELF::SHT_NULL; + ArrayRef<uint8_t> OriginalData; + + SectionBase() = default; + SectionBase(const SectionBase &) = default; virtual ~SectionBase() = default; @@ -258,6 +277,7 @@ public: virtual void removeSectionReferences(const SectionBase *Sec); virtual void removeSymbols(function_ref<bool(const Symbol &)> ToRemove); virtual void accept(SectionVisitor &Visitor) const = 0; + virtual void accept(MutableSectionVisitor &Visitor) = 0; virtual void markSymbols(); }; @@ -275,21 +295,21 @@ private: }; std::set<const SectionBase *, SectionCompare> Sections; - ArrayRef<uint8_t> Contents; public: - uint64_t Align; - uint64_t FileSize; + uint32_t Type; uint32_t Flags; - uint32_t Index; - uint64_t MemSize; uint64_t Offset; - uint64_t PAddr; - uint64_t Type; uint64_t VAddr; + uint64_t PAddr; + uint64_t FileSize; + uint64_t MemSize; + uint64_t Align; + uint32_t Index; uint64_t OriginalOffset; Segment *ParentSegment = nullptr; + ArrayRef<uint8_t> Contents; explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {} Segment() {} @@ -314,6 +334,7 @@ public: explicit Section(ArrayRef<uint8_t> Data) : Contents(Data) {} void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; @@ -327,13 +348,57 @@ class OwnedDataSection : public SectionBase { public: OwnedDataSection(StringRef SecName, ArrayRef<uint8_t> Data) : Data(std::begin(Data), std::end(Data)) { - Name = SecName; + Name = SecName.str(); Type = ELF::SHT_PROGBITS; Size = Data.size(); OriginalOffset = std::numeric_limits<uint64_t>::max(); } void accept(SectionVisitor &Sec) const override; + void accept(MutableSectionVisitor &Visitor) override; +}; + +class CompressedSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + + DebugCompressionType CompressionType; + uint64_t DecompressedSize; + uint64_t DecompressedAlign; + SmallVector<char, 128> CompressedData; + +public: + CompressedSection(const SectionBase &Sec, + DebugCompressionType CompressionType); + CompressedSection(ArrayRef<uint8_t> CompressedData, uint64_t DecompressedSize, + uint64_t DecompressedAlign); + + uint64_t getDecompressedSize() const { return DecompressedSize; } + uint64_t getDecompressedAlign() const { return DecompressedAlign; } + + void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; + + static bool classof(const SectionBase *S) { + return (S->Flags & ELF::SHF_COMPRESSED) || + (StringRef(S->Name).startswith(".zdebug")); + } +}; + +class DecompressedSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + +public: + explicit DecompressedSection(const CompressedSection &Sec) + : SectionBase(Sec) { + Size = Sec.getDecompressedSize(); + Align = Sec.getDecompressedAlign(); + Flags = (Flags & ~ELF::SHF_COMPRESSED); + if (StringRef(Name).startswith(".zdebug")) + Name = "." + Name.substr(2); + } + + void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; }; // There are two types of string tables that can exist, dynamic and not dynamic. @@ -358,6 +423,7 @@ public: uint32_t findIndex(StringRef Name) const; void finalize() override; void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; static bool classof(const SectionBase *S) { if (S->Flags & ELF::SHF_ALLOC) @@ -386,7 +452,7 @@ struct Symbol { SectionBase *DefinedIn = nullptr; SymbolShndxType ShndxType; uint32_t Index; - StringRef Name; + std::string Name; uint32_t NameIndex; uint64_t Size; uint8_t Type; @@ -395,6 +461,7 @@ struct Symbol { bool Referenced = false; uint16_t getShndx() const; + bool isCommon() const; }; class SectionIndexSection : public SectionBase { @@ -414,6 +481,7 @@ public: void initialize(SectionTableRef SecTable) override; void finalize() override; void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; SectionIndexSection() { Name = ".symtab_shndx"; @@ -437,9 +505,11 @@ protected: using SymPtr = std::unique_ptr<Symbol>; public: - void addSymbol(StringRef Name, uint8_t Bind, uint8_t Type, - SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility, - uint16_t Shndx, uint64_t Sz); + SymbolTableSection() { Type = ELF::SHT_SYMTAB; } + + void addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn, + uint64_t Value, uint8_t Visibility, uint16_t Shndx, + uint64_t Size); void prepareForLayout(); // An 'empty' symbol table still contains a null symbol. bool empty() const { return Symbols.size() == 1; } @@ -456,6 +526,7 @@ public: void initialize(SectionTableRef SecTable) override; void finalize() override; void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; static bool classof(const SectionBase *S) { @@ -517,6 +588,7 @@ class RelocationSection public: void addRelocation(Relocation Rel) { Relocations.push_back(Rel); } void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; void markSymbols() override; @@ -549,8 +621,8 @@ public: void setFlagWord(ELF::Elf32_Word W) { FlagWord = W; } void addMember(SectionBase *Sec) { GroupMembers.push_back(Sec); } - void initialize(SectionTableRef SecTable) override{}; void accept(SectionVisitor &) const override; + void accept(MutableSectionVisitor &Visitor) override; void finalize() override; void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; void markSymbols() override; @@ -589,6 +661,7 @@ public: explicit DynamicRelocationSection(ArrayRef<uint8_t> Data) : Contents(Data) {} void accept(SectionVisitor &) const override; + void accept(MutableSectionVisitor &Visitor) override; static bool classof(const SectionBase *S) { if (!(S->Flags & ELF::SHF_ALLOC)) @@ -610,6 +683,7 @@ public: // If we add this section from an external source we can use this ctor. explicit GnuDebugLinkSection(StringRef File); void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; }; class Reader { @@ -623,11 +697,29 @@ using object::ELFFile; using object::ELFObjectFile; using object::OwningBinary; +class BinaryELFBuilder { + uint16_t EMachine; + MemoryBuffer *MemBuf; + std::unique_ptr<Object> Obj; + + void initFileHeader(); + void initHeaderSegment(); + StringTableSection *addStrTab(); + SymbolTableSection *addSymTab(StringTableSection *StrTab); + void addData(SymbolTableSection *SymTab); + void initSections(); + +public: + BinaryELFBuilder(uint16_t EM, MemoryBuffer *MB) + : EMachine(EM), MemBuf(MB), Obj(llvm::make_unique<Object>()) {} + + std::unique_ptr<Object> build(); +}; + template <class ELFT> class ELFBuilder { private: using Elf_Addr = typename ELFT::Addr; using Elf_Shdr = typename ELFT::Shdr; - using Elf_Ehdr = typename ELFT::Ehdr; using Elf_Word = typename ELFT::Word; const ELFFile<ELFT> &ElfFile; @@ -647,13 +739,22 @@ public: void build(); }; +class BinaryReader : public Reader { + const MachineInfo &MInfo; + MemoryBuffer *MemBuf; + +public: + BinaryReader(const MachineInfo &MI, MemoryBuffer *MB) + : MInfo(MI), MemBuf(MB) {} + std::unique_ptr<Object> create() const override; +}; + class ELFReader : public Reader { Binary *Bin; public: - ElfType getElfType() const; std::unique_ptr<Object> create() const override; - explicit ELFReader(Binary *B) : Bin(B){}; + explicit ELFReader(Binary *B) : Bin(B) {} }; class Object { @@ -682,7 +783,8 @@ public: Segment ElfHdrSegment; Segment ProgramHdrSegment; - uint8_t Ident[16]; + uint8_t OSABI; + uint8_t ABIVersion; uint64_t Entry; uint64_t SHOffset; uint32_t Type; @@ -708,6 +810,7 @@ public: auto Sec = llvm::make_unique<T>(std::forward<Ts>(Args)...); auto Ptr = Sec.get(); Sections.emplace_back(std::move(Sec)); + Ptr->Index = Sections.size(); return *Ptr; } Segment &addSegment(ArrayRef<uint8_t> Data) { @@ -715,6 +818,8 @@ public: return *Segments.back(); } }; + +} // end namespace elf } // end namespace objcopy } // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td index 2af2108d98d3..1f7e64e4091c 100644 --- a/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -1,55 +1,98 @@ include "llvm/Option/OptParser.td" -multiclass Eq<string name> { - def NAME: Separate<["--", "-"], name>; - def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>; +multiclass Eq<string name, string help> { + def NAME : Separate<["--", "-"], name>; + def NAME #_eq : Joined<["--", "-"], name #"=">, + Alias<!cast<Separate>(NAME)>, + HelpText<help>; } def help : Flag<["-", "--"], "help">; -defm binary_architecture : Eq<"binary-architecture">, - HelpText<"Used when transforming an architecture-less format (such as binary) to another format">; -def B : JoinedOrSeparate<["-"], "B">, - Alias<binary_architecture>; -defm input_target : Eq<"input-target">, - HelpText<"Format of the input file">, + +defm binary_architecture + : Eq<"binary-architecture", "Used when transforming an architecture-less " + "format (such as binary) to another format">; +def B : JoinedOrSeparate<["-"], "B">, Alias<binary_architecture>; + +defm target : Eq<"target", "Format of the input and output file">, + Values<"binary">; +def F : JoinedOrSeparate<["-"], "F">, Alias<target>; + +defm input_target : Eq<"input-target", "Format of the input file">, Values<"binary">; -defm output_target : Eq<"output-target">, - HelpText<"Format of the output file">, +def I : JoinedOrSeparate<["-"], "I">, Alias<input_target>; + +defm output_target : Eq<"output-target", "Format of the output file">, Values<"binary">; -def O : JoinedOrSeparate<["-"], "O">, - Alias<output_target>; -defm split_dwo : Eq<"split-dwo">, - MetaVarName<"dwo-file">, - HelpText<"Equivalent to extract-dwo on the input file to <dwo-file>, then strip-dwo on the input file">; -defm add_gnu_debuglink : Eq<"add-gnu-debuglink">, - MetaVarName<"debug-file">, - HelpText<"Add a .gnu_debuglink for <debug-file>">; -defm remove_section : Eq<"remove-section">, - MetaVarName<"section">, - HelpText<"Remove <section>">; -defm rename_section : Eq<"rename-section">, - MetaVarName<"old=new">, - HelpText<"Renames a section from old to new">; -defm redefine_symbol : Eq<"redefine-sym">, - MetaVarName<"old=new">, - HelpText<"Change the name of a symbol old to new">; -def R : JoinedOrSeparate<["-"], "R">, - Alias<remove_section>; -defm keep : Eq<"keep">, - MetaVarName<"section">, - HelpText<"Keep <section>">; -defm only_keep : Eq<"only-keep">, - MetaVarName<"section">, - HelpText<"Remove all but <section>">; -def j : JoinedOrSeparate<["-"], "j">, - Alias<only_keep>; -defm add_section : Eq<"add-section">, - MetaVarName<"section=file">, - HelpText<"Make a section named <section> with the contents of <file>.">; -def strip_all : Flag<["-", "--"], "strip-all">, - HelpText<"Remove non-allocated sections other than .gnu.warning* sections">; +def O : JoinedOrSeparate<["-"], "O">, Alias<output_target>; + +def compress_debug_sections : Flag<["--", "-"], "compress-debug-sections">; +def compress_debug_sections_eq + : Joined<["--", "-"], "compress-debug-sections=">, + MetaVarName<"[ zlib | zlib-gnu ]">, + HelpText<"Compress DWARF debug sections using specified style. Supported " + "styles: 'zlib-gnu' and 'zlib'">; +def decompress_debug_sections : Flag<["-", "--"], "decompress-debug-sections">, + HelpText<"Decompress DWARF debug sections.">; +defm split_dwo + : Eq<"split-dwo", "Equivalent to extract-dwo on the input file to " + "<dwo-file>, then strip-dwo on the input file">, + MetaVarName<"dwo-file">; + +def enable_deterministic_archives + : Flag<["-", "--"], "enable-deterministic-archives">, + HelpText<"Enable deterministic mode when copying archives (use zero for " + "UIDs, GIDs, and timestamps).">; +def D : Flag<["-"], "D">, + Alias<enable_deterministic_archives>, + HelpText<"Alias for --enable-deterministic-archives">; + +def disable_deterministic_archives + : Flag<["-", "--"], "disable-deterministic-archives">, + HelpText<"Disable deterministic mode when copying archives (use real " + "values for UIDs, GIDs, and timestamps).">; +def U : Flag<["-"], "U">, + Alias<disable_deterministic_archives>, + HelpText<"Alias for --disable-deterministic-archives">; + +def preserve_dates : Flag<["-", "--"], "preserve-dates">, + HelpText<"Preserve access and modification timestamps">; +def p : Flag<["-"], "p">, Alias<preserve_dates>; + +defm add_gnu_debuglink + : Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for <debug-file>">, + MetaVarName<"debug-file">; + +defm remove_section : Eq<"remove-section", "Remove <section>">, + MetaVarName<"section">; +def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>; + +defm rename_section + : Eq<"rename-section", + "Renames a section from old to new, optionally with specified flags. " + "Flags supported for GNU compatibility: alloc, load, noload, " + "readonly, debug, code, data, rom, share, contents, merge, strings.">, + MetaVarName<"old=new[,flag1,...]">; +defm redefine_symbol + : Eq<"redefine-sym", "Change the name of a symbol old to new">, + MetaVarName<"old=new">; +defm keep_section : Eq<"keep-section", "Keep <section>">, + MetaVarName<"section">; +defm only_section : Eq<"only-section", "Remove all but <section>">, + MetaVarName<"section">; +def j : JoinedOrSeparate<["-"], "j">, Alias<only_section>; +defm add_section + : Eq<"add-section", + "Make a section named <section> with the contents of <file>.">, + MetaVarName<"section=file">; + +def strip_all + : Flag<["-", "--"], "strip-all">, + HelpText< + "Remove non-allocated sections other than .gnu.warning* sections">; +def S : Flag<["-"], "S">, Alias<strip_all>; def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">, - HelpText<"Compaitable with GNU objcopy's --strip-all">; + HelpText<"Compatible with GNU objcopy's --strip-all">; def strip_debug : Flag<["-", "--"], "strip-debug">, HelpText<"Remove all debug information">; def strip_dwo : Flag<["-", "--"], "strip-dwo">, @@ -58,42 +101,80 @@ def strip_sections : Flag<["-", "--"], "strip-sections">, HelpText<"Remove all section headers">; def strip_non_alloc : Flag<["-", "--"], "strip-non-alloc">, HelpText<"Remove all non-allocated sections">; -def extract_dwo : Flag<["-", "--"], "extract-dwo">, - HelpText<"Remove all sections that are not DWARF .dwo sections from file">; -def localize_hidden : Flag<["-", "--"], "localize-hidden">, - HelpText<"Mark all symbols that have hidden or internal visibility as local">; -defm localize_symbol : Eq<"localize-symbol">, - MetaVarName<"symbol">, - HelpText<"Mark <symbol> as local">; -def L : JoinedOrSeparate<["-"], "L">, - Alias<localize_symbol>; -defm globalize_symbol : Eq<"globalize-symbol">, - MetaVarName<"symbol">, - HelpText<"Mark <symbol> as global">; -defm weaken_symbol : Eq<"weaken-symbol">, - MetaVarName<"symbol">, - HelpText<"Mark <symbol> as weak">; -def W : JoinedOrSeparate<["-"], "W">, - Alias<weaken_symbol>; -def weaken : Flag<["-", "--"], "weaken">, - HelpText<"Mark all global symbols as weak">; -def discard_all : Flag<["-", "--"], "discard-all">, - HelpText<"Remove all local symbols except file and section symbols">; -def x : Flag<["-"], "x">, - Alias<discard_all>; -defm strip_symbol : Eq<"strip-symbol">, - MetaVarName<"symbol">, - HelpText<"Remove symbol <symbol>">; -def N : JoinedOrSeparate<["-"], "N">, - Alias<strip_symbol>; -defm keep_symbol : Eq<"keep-symbol">, - MetaVarName<"symbol">, - HelpText<"Do not remove symbol <symbol>">; -def K : JoinedOrSeparate<["-"], "K">, - Alias<keep_symbol>; -def only_keep_debug : Flag<["-", "--"], "only-keep-debug">, - HelpText<"Currently ignored. Only for compaitability with GNU objcopy.">; def strip_unneeded : Flag<["-", "--"], "strip-unneeded">, - HelpText<"Remove all symbols not needed by relocations">; + HelpText<"Remove all symbols not needed by relocations">; + +def extract_dwo + : Flag<["-", "--"], "extract-dwo">, + HelpText< + "Remove all sections that are not DWARF .dwo sections from file">; + +def localize_hidden + : Flag<["-", "--"], "localize-hidden">, + HelpText< + "Mark all symbols that have hidden or internal visibility as local">; +defm localize_symbol : Eq<"localize-symbol", "Mark <symbol> as local">, + MetaVarName<"symbol">; +def L : JoinedOrSeparate<["-"], "L">, Alias<localize_symbol>; + +defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">, + MetaVarName<"symbol">; +defm keep_global_symbol + : Eq<"keep-global-symbol", + "Convert all symbols except <symbol> to local. May be repeated to " + "convert all except a set of symbols to local.">, + MetaVarName<"symbol">; +def G : JoinedOrSeparate<["-"], "G">, Alias<keep_global_symbol>; + +defm keep_global_symbols + : Eq<"keep-global-symbols", + "Reads a list of symbols from <filename> and runs as if " + "--keep-global-symbol=<symbol> is set for each one. <filename> " + "contains one symbol per line and may contain comments beginning with " + "'#'. Leading and trailing whitespace is stripped from each line. May " + "be repeated to read symbols from many files.">, + MetaVarName<"filename">; + +defm weaken_symbol : Eq<"weaken-symbol", "Mark <symbol> as weak">, + MetaVarName<"symbol">; +def W : JoinedOrSeparate<["-"], "W">, Alias<weaken_symbol>; +def weaken : Flag<["-", "--"], "weaken">, + HelpText<"Mark all global symbols as weak">; +def discard_all + : Flag<["-", "--"], "discard-all">, + HelpText<"Remove all local symbols except file and section symbols">; +def x : Flag<["-"], "x">, Alias<discard_all>; +defm strip_symbol : Eq<"strip-symbol", "Remove symbol <symbol>">, + MetaVarName<"symbol">; +def N : JoinedOrSeparate<["-"], "N">, Alias<strip_symbol>; +defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, + MetaVarName<"symbol">; +def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>; +def only_keep_debug + : Flag<["-", "--"], "only-keep-debug">, + HelpText<"Currently ignored. Only for compatibility with GNU objcopy.">; def keep_file_symbols : Flag<["-", "--"], "keep-file-symbols">, - HelpText<"Do not remove file symbols">; + HelpText<"Do not remove file symbols">; +defm dump_section + : Eq<"dump-section", + "Dump contents of section named <section> into file <file>">, + MetaVarName<"section=file">; +defm prefix_symbols + : Eq<"prefix-symbols", "Add <prefix> to the start of every symbol name">, + MetaVarName<"prefix">; + +def version : Flag<["-", "--"], "version">, + HelpText<"Print the version and exit.">; +def V : Flag<["-"], "V">, Alias<version>; +defm build_id_link_dir + : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and " + "--build-id-link-output to <dir>">, + MetaVarName<"dir">; +defm build_id_link_input + : Eq<"build-id-link-input", "Hard-link the input to <dir>/xx/xxx<suffix> " + "name derived from hex build ID">, + MetaVarName<"suffix">; +defm build_id_link_output + : Eq<"build-id-link-output", "Hard-link the output to <dir>/xx/xxx<suffix> " + "name derived from hex build ID">, + MetaVarName<"suffix">; diff --git a/contrib/llvm/tools/llvm-objcopy/StripOpts.td b/contrib/llvm/tools/llvm-objcopy/StripOpts.td index 333b0d288efa..fa98e27e9321 100644 --- a/contrib/llvm/tools/llvm-objcopy/StripOpts.td +++ b/contrib/llvm/tools/llvm-objcopy/StripOpts.td @@ -1,49 +1,67 @@ include "llvm/Option/OptParser.td" -multiclass Eq<string name> { - def NAME: Separate<["--", "-"], name>; - def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>; +multiclass Eq<string name, string help> { + def NAME : Separate<["--", "-"], name>; + def NAME #_eq : Joined<["--", "-"], name #"=">, + Alias<!cast<Separate>(NAME)>, + HelpText<help>; } def help : Flag<["-", "--"], "help">; -defm output : Eq<"o">, - MetaVarName<"output">, - HelpText<"Write output to <file>">; +def enable_deterministic_archives + : Flag<["-", "--"], "enable-deterministic-archives">, + HelpText<"Enable deterministic mode when stripping archives (use zero " + "for UIDs, GIDs, and timestamps).">; +def D : Flag<["-"], "D">, + Alias<enable_deterministic_archives>, + HelpText<"Alias for --enable-deterministic-archives">; -def strip_all : Flag<["-", "--"], "strip-all">, - HelpText<"Remove non-allocated sections other than .gnu.warning* sections">; +def disable_deterministic_archives + : Flag<["-", "--"], "disable-deterministic-archives">, + HelpText<"Disable deterministic mode when stripping archives (use real " + "values for UIDs, GIDs, and timestamps).">; +def U : Flag<["-"], "U">, + Alias<disable_deterministic_archives>, + HelpText<"Alias for --disable-deterministic-archives">; -def strip_debug : Flag<["-", "--"], "strip-debug">, - HelpText<"Remove debugging symbols only">; - -def d : Flag<["-"], "d">, - Alias<strip_debug>; - -def g : Flag<["-"], "g">, - Alias<strip_debug>; +defm output : Eq<"o", "Write output to <file>">, MetaVarName<"output">; -def S : Flag<["-"], "S">, - Alias<strip_debug>; +def preserve_dates : Flag<["-", "--"], "preserve-dates">, + HelpText<"Preserve access and modification timestamps">; +def p : Flag<["-"], "p">, Alias<preserve_dates>; -defm remove_section : Eq<"remove-section">, - MetaVarName<"section">, - HelpText<"Remove <section>">; +def strip_all + : Flag<["-", "--"], "strip-all">, + HelpText< + "Remove non-allocated sections other than .gnu.warning* sections">; +def s : Flag<["-"], "s">, Alias<strip_all>; -def R : JoinedOrSeparate<["-"], "R">, - Alias<remove_section>; +def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">, + HelpText<"Compatible with GNU strip's --strip-all">; +def strip_debug : Flag<["-", "--"], "strip-debug">, + HelpText<"Remove debugging symbols only">; +def d : Flag<["-"], "d">, Alias<strip_debug>; +def g : Flag<["-"], "g">, Alias<strip_debug>; +def S : Flag<["-"], "S">, Alias<strip_debug>; +def strip_unneeded : Flag<["-", "--"], "strip-unneeded">, + HelpText<"Remove all symbols not needed by relocations">; -defm keep_symbol : Eq<"keep-symbol">, - MetaVarName<"symbol">, - HelpText<"Do not remove symbol <symbol>">; +defm remove_section : Eq<"remove-section", "Remove <section>">, + MetaVarName<"section">; +def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>; -def K : JoinedOrSeparate<["-"], "K">, - Alias<keep_symbol>; +defm keep_section : Eq<"keep-section", "Keep <section>">, + MetaVarName<"section">; +defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, + MetaVarName<"symbol">; +def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>; -def discard_all : Flag<["-", "--"], "discard-all">, - HelpText<"Remove all local symbols except file and section symbols">; -def x : Flag<["-"], "x">, - Alias<discard_all>; +def discard_all + : Flag<["-", "--"], "discard-all">, + HelpText<"Remove all local symbols except file and section symbols">; +def x : Flag<["-"], "x">, Alias<discard_all>; -def strip_unneeded : Flag<["-", "--"], "strip-unneeded">, - HelpText<"Remove all symbols not needed by relocations">; +def version : Flag<["-", "--"], "version">, + HelpText<"Print the version and exit.">; +def V : Flag<["-"], "V">, Alias<version>; diff --git a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp index 21a1622db765..fb1ff18b015b 100644 --- a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -8,14 +8,19 @@ //===----------------------------------------------------------------------===// #include "llvm-objcopy.h" -#include "Object.h" +#include "Buffer.h" +#include "COFF/COFFObjcopy.h" +#include "CopyConfig.h" +#include "ELF/ELFObjcopy.h" + #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" -#include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ELFTypes.h" #include "llvm/Object/Error.h" @@ -23,137 +28,23 @@ #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" -#include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> #include <cstdlib> -#include <functional> -#include <iterator> #include <memory> #include <string> #include <system_error> #include <utility> -using namespace llvm; -using namespace llvm::objcopy; -using namespace object; -using namespace ELF; - -namespace { - -enum ObjcopyID { - OBJCOPY_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - OBJCOPY_##ID, -#include "ObjcopyOpts.inc" -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE; -#include "ObjcopyOpts.inc" -#undef PREFIX - -static const opt::OptTable::Info ObjcopyInfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - {OBJCOPY_##PREFIX, \ - NAME, \ - HELPTEXT, \ - METAVAR, \ - OBJCOPY_##ID, \ - opt::Option::KIND##Class, \ - PARAM, \ - FLAGS, \ - OBJCOPY_##GROUP, \ - OBJCOPY_##ALIAS, \ - ALIASARGS, \ - VALUES}, -#include "ObjcopyOpts.inc" -#undef OPTION -}; - -class ObjcopyOptTable : public opt::OptTable { -public: - ObjcopyOptTable() : OptTable(ObjcopyInfoTable, true) {} -}; - -enum StripID { - STRIP_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - STRIP_##ID, -#include "StripOpts.inc" -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE; -#include "StripOpts.inc" -#undef PREFIX - -static const opt::OptTable::Info StripInfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - {STRIP_##PREFIX, NAME, HELPTEXT, \ - METAVAR, STRIP_##ID, opt::Option::KIND##Class, \ - PARAM, FLAGS, STRIP_##GROUP, \ - STRIP_##ALIAS, ALIASARGS, VALUES}, -#include "StripOpts.inc" -#undef OPTION -}; - -class StripOptTable : public opt::OptTable { -public: - StripOptTable() : OptTable(StripInfoTable, true) {} -}; - -struct CopyConfig { - StringRef OutputFilename; - StringRef InputFilename; - StringRef OutputFormat; - StringRef InputFormat; - StringRef BinaryArch; - - StringRef SplitDWO; - StringRef AddGnuDebugLink; - std::vector<StringRef> ToRemove; - std::vector<StringRef> Keep; - std::vector<StringRef> OnlyKeep; - std::vector<StringRef> AddSection; - std::vector<StringRef> SymbolsToLocalize; - std::vector<StringRef> SymbolsToGlobalize; - std::vector<StringRef> SymbolsToWeaken; - std::vector<StringRef> SymbolsToRemove; - std::vector<StringRef> SymbolsToKeep; - StringMap<StringRef> SectionsToRename; - StringMap<StringRef> SymbolsToRename; - bool StripAll = false; - bool StripAllGNU = false; - bool StripDebug = false; - bool StripSections = false; - bool StripNonAlloc = false; - bool StripDWO = false; - bool StripUnneeded = false; - bool ExtractDWO = false; - bool LocalizeHidden = false; - bool Weaken = false; - bool DiscardAll = false; - bool OnlyKeepDebug = false; - bool KeepFileSymbols = false; -}; - -using SectionPred = std::function<bool(const SectionBase &Sec)>; - -} // namespace - namespace llvm { namespace objcopy { @@ -161,14 +52,15 @@ namespace objcopy { StringRef ToolName; LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { - errs() << ToolName << ": " << Message << ".\n"; + WithColor::error(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"; + WithColor::error(errs(), ToolName) + << "'" << File << "': " << EC.message() << ".\n"; exit(1); } @@ -176,304 +68,18 @@ LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { assert(E); std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS, ""); + logAllUnhandledErrors(std::move(E), OS); OS.flush(); - errs() << ToolName << ": '" << File << "': " << Buf; + WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; exit(1); } } // end namespace objcopy } // end namespace llvm -static bool IsDebugSection(const SectionBase &Sec) { - return Sec.Name.startswith(".debug") || Sec.Name.startswith(".zdebug") || - Sec.Name == ".gdb_index"; -} - -static bool IsDWOSection(const SectionBase &Sec) { - return Sec.Name.endswith(".dwo"); -} - -static bool OnlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { - // We can't remove the section header string table. - if (&Sec == Obj.SectionNames) - return false; - // Short of keeping the string table we want to keep everything that is a DWO - // section and remove everything else. - return !IsDWOSection(Sec); -} - -static std::unique_ptr<Writer> CreateWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, - ElfType OutputElfType) { - if (Config.OutputFormat == "binary") { - return llvm::make_unique<BinaryWriter>(Obj, Buf); - } - // Depending on the initial ELFT and OutputFormat we need a different Writer. - switch (OutputElfType) { - case ELFT_ELF32LE: - return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, - !Config.StripSections); - case ELFT_ELF64LE: - return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, - !Config.StripSections); - case ELFT_ELF32BE: - return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, - !Config.StripSections); - case ELFT_ELF64BE: - return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, - !Config.StripSections); - } - llvm_unreachable("Invalid output format"); -} - -static void SplitDWOToFile(const CopyConfig &Config, const Reader &Reader, - StringRef File, ElfType OutputElfType) { - auto DWOFile = Reader.create(); - DWOFile->removeSections( - [&](const SectionBase &Sec) { return OnlyKeepDWOPred(*DWOFile, Sec); }); - FileBuffer FB(File); - auto Writer = CreateWriter(Config, *DWOFile, FB, OutputElfType); - Writer->finalize(); - Writer->write(); -} - -// This function handles the high level operations of GNU objcopy including -// handling command line options. It's important to outline certain properties -// we expect to hold of the command line operations. Any operation that "keeps" -// should keep regardless of a remove. Additionally any removal should respect -// any previous removals. Lastly whether or not something is removed shouldn't -// depend a) on the order the options occur in or b) on some opaque priority -// system. The only priority is that keeps/copies overrule removes. -static void HandleArgs(const CopyConfig &Config, Object &Obj, - const Reader &Reader, ElfType OutputElfType) { - - if (!Config.SplitDWO.empty()) { - SplitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType); - } - - // TODO: update or remove symbols only if there is an option that affects - // them. - if (Obj.SymbolTable) { - Obj.SymbolTable->updateSymbols([&](Symbol &Sym) { - if ((Config.LocalizeHidden && - (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || - (!Config.SymbolsToLocalize.empty() && - is_contained(Config.SymbolsToLocalize, Sym.Name))) - Sym.Binding = STB_LOCAL; - - if (!Config.SymbolsToGlobalize.empty() && - is_contained(Config.SymbolsToGlobalize, Sym.Name)) - Sym.Binding = STB_GLOBAL; - - if (!Config.SymbolsToWeaken.empty() && - is_contained(Config.SymbolsToWeaken, Sym.Name) && - Sym.Binding == STB_GLOBAL) - Sym.Binding = STB_WEAK; - - if (Config.Weaken && Sym.Binding == STB_GLOBAL && - Sym.getShndx() != SHN_UNDEF) - Sym.Binding = STB_WEAK; - - const auto I = Config.SymbolsToRename.find(Sym.Name); - if (I != Config.SymbolsToRename.end()) - Sym.Name = I->getValue(); - }); - - // The purpose of this loop is to mark symbols referenced by sections - // (like GroupSection or RelocationSection). This way, we know which - // symbols are still 'needed' and wich are not. - if (Config.StripUnneeded) { - for (auto &Section : Obj.sections()) - Section.markSymbols(); - } - - Obj.removeSymbols([&](const Symbol &Sym) { - if ((!Config.SymbolsToKeep.empty() && - is_contained(Config.SymbolsToKeep, Sym.Name)) || - (Config.KeepFileSymbols && Sym.Type == STT_FILE)) - return false; - - if (Config.DiscardAll && Sym.Binding == STB_LOCAL && - Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE && - Sym.Type != STT_SECTION) - return true; - - if (Config.StripAll || Config.StripAllGNU) - return true; - - if (!Config.SymbolsToRemove.empty() && - is_contained(Config.SymbolsToRemove, Sym.Name)) { - return true; - } - - if (Config.StripUnneeded && !Sym.Referenced && - (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) && - Sym.Type != STT_FILE && Sym.Type != STT_SECTION) - return true; - - return false; - }); - } - - SectionPred RemovePred = [](const SectionBase &) { return false; }; - - // Removes: - if (!Config.ToRemove.empty()) { - RemovePred = [&Config](const SectionBase &Sec) { - return find(Config.ToRemove, Sec.Name) != Config.ToRemove.end(); - }; - } - - if (Config.StripDWO || !Config.SplitDWO.empty()) - RemovePred = [RemovePred](const SectionBase &Sec) { - return IsDWOSection(Sec) || RemovePred(Sec); - }; - - if (Config.ExtractDWO) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - return OnlyKeepDWOPred(Obj, Sec) || RemovePred(Sec); - }; - - if (Config.StripAllGNU) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if ((Sec.Flags & SHF_ALLOC) != 0) - return false; - if (&Sec == Obj.SectionNames) - return false; - switch (Sec.Type) { - case SHT_SYMTAB: - case SHT_REL: - case SHT_RELA: - case SHT_STRTAB: - return true; - } - return IsDebugSection(Sec); - }; - - if (Config.StripSections) { - RemovePred = [RemovePred](const SectionBase &Sec) { - return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0; - }; - } - - if (Config.StripDebug) { - RemovePred = [RemovePred](const SectionBase &Sec) { - return RemovePred(Sec) || IsDebugSection(Sec); - }; - } - - if (Config.StripNonAlloc) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if (&Sec == Obj.SectionNames) - return false; - return (Sec.Flags & SHF_ALLOC) == 0; - }; - - if (Config.StripAll) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if (&Sec == Obj.SectionNames) - return false; - if (Sec.Name.startswith(".gnu.warning")) - return false; - return (Sec.Flags & SHF_ALLOC) == 0; - }; - - // Explicit copies: - if (!Config.OnlyKeep.empty()) { - RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { - // Explicitly keep these sections regardless of previous removes. - if (find(Config.OnlyKeep, Sec.Name) != Config.OnlyKeep.end()) - return false; - - // Allow all implicit removes. - if (RemovePred(Sec)) - return true; - - // Keep special sections. - if (Obj.SectionNames == &Sec) - return false; - if (Obj.SymbolTable == &Sec || - (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec)) - return false; - - // Remove everything else. - return true; - }; - } - - if (!Config.Keep.empty()) { - RemovePred = [Config, RemovePred](const SectionBase &Sec) { - // Explicitly keep these sections regardless of previous removes. - if (find(Config.Keep, Sec.Name) != Config.Keep.end()) - return false; - // Otherwise defer to RemovePred. - return RemovePred(Sec); - }; - } - - // This has to be the last predicate assignment. - // If the option --keep-symbol has been specified - // and at least one of those symbols is present - // (equivalently, the updated symbol table is not empty) - // the symbol table and the string table should not be removed. - if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) && - Obj.SymbolTable && !Obj.SymbolTable->empty()) { - RemovePred = [&Obj, RemovePred](const SectionBase &Sec) { - if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab()) - return false; - return RemovePred(Sec); - }; - } - - Obj.removeSections(RemovePred); - - if (!Config.SectionsToRename.empty()) { - for (auto &Sec : Obj.sections()) { - const auto Iter = Config.SectionsToRename.find(Sec.Name); - if (Iter != Config.SectionsToRename.end()) - Sec.Name = Iter->second; - } - } - - if (!Config.AddSection.empty()) { - for (const auto &Flag : Config.AddSection) { - auto SecPair = Flag.split("="); - auto SecName = SecPair.first; - auto File = SecPair.second; - auto BufOrErr = MemoryBuffer::getFile(File); - if (!BufOrErr) - reportError(File, BufOrErr.getError()); - auto Buf = std::move(*BufOrErr); - auto BufPtr = reinterpret_cast<const uint8_t *>(Buf->getBufferStart()); - auto BufSize = Buf->getBufferSize(); - Obj.addSection<OwnedDataSection>(SecName, - ArrayRef<uint8_t>(BufPtr, BufSize)); - } - } - - if (!Config.AddGnuDebugLink.empty()) - Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink); -} - -static void ExecuteElfObjcopyOnBinary(const CopyConfig &Config, Binary &Binary, - Buffer &Out) { - ELFReader Reader(&Binary); - std::unique_ptr<Object> Obj = Reader.create(); - - HandleArgs(Config, *Obj, Reader, Reader.getElfType()); - - std::unique_ptr<Writer> Writer = - CreateWriter(Config, *Obj, Out, Reader.getElfType()); - Writer->finalize(); - Writer->write(); -} +using namespace llvm; +using namespace llvm::object; +using namespace llvm::objcopy; // For regular archives this function simply calls llvm::writeArchive, // For thin archives it writes the archive file itself as well as its members. @@ -504,22 +110,48 @@ static Error deepWriteArchive(StringRef ArcName, return Error::success(); } -static void ExecuteElfObjcopyOnArchive(const CopyConfig &Config, const Archive &Ar) { +/// The function executeObjcopyOnRawBinary does the dispatch based on the format +/// of the output specified by the command line options. +static void executeObjcopyOnRawBinary(const CopyConfig &Config, + MemoryBuffer &In, Buffer &Out) { + // TODO: llvm-objcopy should parse CopyConfig.OutputFormat to recognize + // formats other than ELF / "binary" and invoke + // elf::executeObjcopyOnRawBinary, macho::executeObjcopyOnRawBinary or + // coff::executeObjcopyOnRawBinary accordingly. + return elf::executeObjcopyOnRawBinary(Config, In, Out); +} + +/// The function executeObjcopyOnBinary does the dispatch based on the format +/// of the input binary (ELF, MachO or COFF). +static void executeObjcopyOnBinary(const CopyConfig &Config, object::Binary &In, + Buffer &Out) { + if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) + return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); + else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) + return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); + else + error("Unsupported object file format"); +} + +static void executeObjcopyOnArchive(const CopyConfig &Config, + const Archive &Ar) { std::vector<NewArchiveMember> NewArchiveMembers; Error Err = Error::success(); for (const Archive::Child &Child : Ar.children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); if (!ChildOrErr) reportError(Ar.getFileName(), ChildOrErr.takeError()); + Binary *Bin = ChildOrErr->get(); + Expected<StringRef> ChildNameOrErr = Child.getName(); if (!ChildNameOrErr) reportError(Ar.getFileName(), ChildNameOrErr.takeError()); MemBuffer MB(ChildNameOrErr.get()); - ExecuteElfObjcopyOnBinary(Config, **ChildOrErr, MB); + executeObjcopyOnBinary(Config, *Bin, MB); Expected<NewArchiveMember> Member = - NewArchiveMember::getOldMember(Child, true); + NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); if (!Member) reportError(Ar.getFileName(), Member.takeError()); Member->Buf = MB.releaseMemoryBuffer(); @@ -529,180 +161,72 @@ static void ExecuteElfObjcopyOnArchive(const CopyConfig &Config, const Archive & if (Err) reportError(Config.InputFilename, std::move(Err)); - if (Error E = - deepWriteArchive(Config.OutputFilename, NewArchiveMembers, - Ar.hasSymbolTable(), Ar.kind(), true, Ar.isThin())) + if (Error E = deepWriteArchive(Config.OutputFilename, NewArchiveMembers, + Ar.hasSymbolTable(), Ar.kind(), + Config.DeterministicArchives, Ar.isThin())) reportError(Config.OutputFilename, std::move(E)); } -static void ExecuteElfObjcopy(const CopyConfig &Config) { - Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = - createBinary(Config.InputFilename); - if (!BinaryOrErr) - reportError(Config.InputFilename, BinaryOrErr.takeError()); - - if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) - return ExecuteElfObjcopyOnArchive(Config, *Ar); - - FileBuffer FB(Config.OutputFilename); - ExecuteElfObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB); -} - -// ParseObjcopyOptions returns the config and sets the input arguments. If a -// help flag is set then ParseObjcopyOptions will print the help messege and -// exit. -static CopyConfig ParseObjcopyOptions(ArrayRef<const char *> ArgsArr) { - ObjcopyOptTable T; - unsigned MissingArgumentIndex, MissingArgumentCount; - llvm::opt::InputArgList InputArgs = - T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - - if (InputArgs.size() == 0) { - T.PrintHelp(errs(), "llvm-objcopy <input> [ <output> ]", "objcopy tool"); - exit(1); - } - - if (InputArgs.hasArg(OBJCOPY_help)) { - T.PrintHelp(outs(), "llvm-objcopy <input> [ <output> ]", "objcopy tool"); - exit(0); - } - - SmallVector<const char *, 2> Positional; - - for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN)) - error("unknown argument '" + Arg->getAsString(InputArgs) + "'"); - - for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT)) - Positional.push_back(Arg->getValue()); - - if (Positional.empty()) - error("No input file specified"); - - if (Positional.size() > 2) - error("Too many positional arguments"); - - CopyConfig Config; - Config.InputFilename = Positional[0]; - Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1]; - Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target); - Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); - Config.BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture); - - Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); - Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); - - for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { - if (!StringRef(Arg->getValue()).contains('=')) - error("Bad format for --redefine-sym"); - auto Old2New = StringRef(Arg->getValue()).split('='); - if (!Config.SymbolsToRename.insert(Old2New).second) - error("Multiple redefinition of symbol " + Old2New.first); - } - - for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) { - if (!StringRef(Arg->getValue()).contains('=')) - error("Bad format for --rename-section"); - auto Old2New = StringRef(Arg->getValue()).split('='); - if (!Config.SectionsToRename.insert(Old2New).second) - error("Already have a section rename for " + Old2New.first); - } - - for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) - Config.ToRemove.push_back(Arg->getValue()); - for (auto Arg : InputArgs.filtered(OBJCOPY_keep)) - Config.Keep.push_back(Arg->getValue()); - for (auto Arg : InputArgs.filtered(OBJCOPY_only_keep)) - Config.OnlyKeep.push_back(Arg->getValue()); - for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) - Config.AddSection.push_back(Arg->getValue()); - Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); - Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu); - Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug); - Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo); - Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections); - Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc); - Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded); - Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo); - Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); - Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); - Config.DiscardAll = InputArgs.hasArg(OBJCOPY_discard_all); - Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); - Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); - for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) - Config.SymbolsToLocalize.push_back(Arg->getValue()); - for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) - Config.SymbolsToGlobalize.push_back(Arg->getValue()); - for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) - Config.SymbolsToWeaken.push_back(Arg->getValue()); - for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) - Config.SymbolsToRemove.push_back(Arg->getValue()); - for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) - Config.SymbolsToKeep.push_back(Arg->getValue()); - - return Config; -} - -// ParseStripOptions returns the config and sets the input arguments. If a -// help flag is set then ParseStripOptions will print the help messege and -// exit. -static CopyConfig ParseStripOptions(ArrayRef<const char *> ArgsArr) { - StripOptTable T; - unsigned MissingArgumentIndex, MissingArgumentCount; - llvm::opt::InputArgList InputArgs = - T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - - if (InputArgs.size() == 0) { - T.PrintHelp(errs(), "llvm-strip <input> [ <output> ]", "strip tool"); - exit(1); +static void restoreDateOnFile(StringRef Filename, + const sys::fs::file_status &Stat) { + int FD; + + if (auto EC = + sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) + reportError(Filename, EC); + + if (auto EC = sys::fs::setLastAccessAndModificationTime( + FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) + reportError(Filename, EC); + + if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) + reportError(Filename, EC); +} + +/// The function executeObjcopy does the higher level dispatch based on the type +/// of input (raw binary, archive or single object file) and takes care of the +/// format-agnostic modifications, i.e. preserving dates. +static void executeObjcopy(const CopyConfig &Config) { + sys::fs::file_status Stat; + if (Config.PreserveDates) + if (auto EC = sys::fs::status(Config.InputFilename, Stat)) + reportError(Config.InputFilename, EC); + + if (Config.InputFormat == "binary") { + auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename); + if (!BufOrErr) + reportError(Config.InputFilename, BufOrErr.getError()); + FileBuffer FB(Config.OutputFilename); + executeObjcopyOnRawBinary(Config, *BufOrErr->get(), FB); + } else { + Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = + createBinary(Config.InputFilename); + if (!BinaryOrErr) + reportError(Config.InputFilename, BinaryOrErr.takeError()); + + if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { + executeObjcopyOnArchive(Config, *Ar); + } else { + FileBuffer FB(Config.OutputFilename); + executeObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB); + } } - if (InputArgs.hasArg(STRIP_help)) { - T.PrintHelp(outs(), "llvm-strip <input> [ <output> ]", "strip tool"); - exit(0); + if (Config.PreserveDates) { + restoreDateOnFile(Config.OutputFilename, Stat); + if (!Config.SplitDWO.empty()) + restoreDateOnFile(Config.SplitDWO, Stat); } - - SmallVector<const char *, 2> Positional; - for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN)) - error("unknown argument '" + Arg->getAsString(InputArgs) + "'"); - for (auto Arg : InputArgs.filtered(STRIP_INPUT)) - Positional.push_back(Arg->getValue()); - - if (Positional.empty()) - error("No input file specified"); - - if (Positional.size() > 2) - error("Support for multiple input files is not implemented yet"); - - CopyConfig Config; - Config.InputFilename = Positional[0]; - Config.OutputFilename = - InputArgs.getLastArgValue(STRIP_output, Positional[0]); - - Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); - - Config.DiscardAll = InputArgs.hasArg(STRIP_discard_all); - Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded); - Config.StripAll = InputArgs.hasArg(STRIP_strip_all); - - if (!Config.StripDebug && !Config.StripUnneeded && !Config.DiscardAll) - Config.StripAll = true; - - for (auto Arg : InputArgs.filtered(STRIP_remove_section)) - Config.ToRemove.push_back(Arg->getValue()); - - for (auto Arg : InputArgs.filtered(STRIP_keep_symbol)) - Config.SymbolsToKeep.push_back(Arg->getValue()); - - return Config; } int main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0]; - CopyConfig Config; - if (sys::path::stem(ToolName).endswith_lower("strip")) - Config = ParseStripOptions(makeArrayRef(argv + 1, argc)); + DriverConfig DriverConfig; + if (sys::path::stem(ToolName).contains("strip")) + DriverConfig = parseStripOptions(makeArrayRef(argv + 1, argc)); else - Config = ParseObjcopyOptions(makeArrayRef(argv + 1, argc)); - ExecuteElfObjcopy(Config); + DriverConfig = parseObjcopyOptions(makeArrayRef(argv + 1, argc)); + for (const CopyConfig &CopyConfig : DriverConfig.CopyConfigs) + executeObjcopy(CopyConfig); } diff --git a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h index e222b65dc78f..d8edf3e29ee0 100644 --- a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h +++ b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h @@ -31,7 +31,7 @@ template <class T> T unwrapOrError(Expected<T> EO) { return *EO; std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(EO.takeError(), OS, ""); + logAllUnhandledErrors(EO.takeError(), OS); OS.flush(); error(Buf); } |
