Index: test/tools/llvm-objcopy/COFF/X86/lit.local.cfg =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/COFF/X86/lit.local.cfg @@ -0,0 +1,4 @@ +# These tests require a registered x86 backend. + +if not 'X86' in config.root.targets: + config.unsupported = True Index: test/tools/llvm-objcopy/COFF/X86/remove-local-symbols.s =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/COFF/X86/remove-local-symbols.s @@ -0,0 +1,37 @@ +# RUN: llvm-mc -triple=x86_64-windows-gnu %s -filetype=obj -o %t.o + +# RUN: llvm-readobj -relocations %t.o | FileCheck %s --check-prefix=RELOCS +# RUN: llvm-nm %t.o | FileCheck %s --check-prefix=SYMBOLS-PRE +# RUN: llvm-strip -x %t.o +# RUN: llvm-readobj -relocations %t.o | FileCheck %s --check-prefix=RELOCS +# RUN: llvm-nm %t.o | FileCheck %s --check-prefix=SYMBOLS-POST + +# RELOCS: Relocations [ +# RELOCS-NEXT: Section (1) .text { +# RELOCS-NEXT: 0x2 IMAGE_REL_AMD64_REL32 ptr +# RELOCS-NEXT: } +# RELOCS-NEXT: ] + +# SYMBOLS-PRE: 00000000 T mainfunc +# SYMBOLS-PRE: 0000000c t otherfunc +# SYMBOLS-PRE: 00000000 d ptr +# SYMBOLS-PRE-EMPTY: + +# SYMBOLS-POST: 00000000 T mainfunc +# SYMBOLS-POST: 00000000 d ptr +# SYMBOLS-POST-EMPTY: + + + .text + .globl mainfunc +mainfunc: + movl ptr(%rip), %eax + # This doesn't produce any relocation, and otherfunc can be dropped. + call otherfunc + ret +otherfunc: + ret + + .data +ptr: + .long 42 Index: test/tools/llvm-objcopy/COFF/X86/remove-symbol1.s =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/COFF/X86/remove-symbol1.s @@ -0,0 +1,43 @@ +# RUN: llvm-mc -triple=x86_64-windows-gnu %s -filetype=obj -o %t.o + +# RUN: llvm-readobj -relocations %t.o | FileCheck %s --check-prefix=RELOCS +# RUN: llvm-nm %t.o | FileCheck %s --check-prefix=SYMBOLS-PRE +# RUN: llvm-objcopy -N otherfunc %t.o +# RUN: llvm-readobj -relocations %t.o | FileCheck %s --check-prefix=RELOCS +# RUN: llvm-nm %t.o | FileCheck %s --check-prefix=SYMBOLS-POST + +# RELOCS: Relocations [ +# RELOCS-NEXT: Section (1) .text { +# RELOCS-NEXT: 0x2 IMAGE_REL_AMD64_REL32 ptr +# RELOCS-NEXT: } +# RELOCS-NEXT: ] + +# SYMBOLS-PRE: 00000000 T mainfunc +# SYMBOLS-PRE: 0000000c T otherfunc +# SYMBOLS-PRE: 0000000d T otherfunc2 +# SYMBOLS-PRE: 00000000 d ptr +# SYMBOLS-PRE-EMPTY: + +# SYMBOLS-POST: 00000000 T mainfunc +# SYMBOLS-POST-NOT: otherfunc +# SYMBOLS-POST: 0000000d T otherfunc2 +# SYMBOLS-POST: 00000000 d ptr +# SYMBOLS-POST-EMPTY: + + + .text + .globl mainfunc +mainfunc: + movl ptr(%rip), %eax + call otherfunc + ret + .globl otherfunc +otherfunc: + ret + .globl otherfunc2 +otherfunc2: + ret + + .data +ptr: + .long 42 Index: test/tools/llvm-objcopy/COFF/X86/remove-symbol2.s =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/COFF/X86/remove-symbol2.s @@ -0,0 +1,29 @@ +# RUN: llvm-mc -triple=x86_64-windows-gnu %s -filetype=obj -o %t.o + +# RUN: llvm-nm %t.o | FileCheck %s --check-prefix=SYMBOLS-PRE +# RUN: not llvm-objcopy -N ptr %t.o 2>&1 | FileCheck %s --check-prefix=ERROR + +# SYMBOLS-PRE: 00000000 T mainfunc +# SYMBOLS-PRE: 0000000c T otherfunc +# SYMBOLS-PRE: 0000000d T otherfunc2 +# SYMBOLS-PRE: 00000000 d ptr +# SYMBOLS-PRE-EMPTY: + +# ERROR: Can't remove referenced symbol ptr + + .text + .globl mainfunc +mainfunc: + movl ptr(%rip), %eax + call otherfunc + ret + .globl otherfunc +otherfunc: + ret + .globl otherfunc2 +otherfunc2: + ret + + .data +ptr: + .long 42 Index: tools/llvm-objcopy/CMakeLists.txt =================================================================== --- tools/llvm-objcopy/CMakeLists.txt +++ tools/llvm-objcopy/CMakeLists.txt @@ -18,6 +18,7 @@ CopyConfig.cpp llvm-objcopy.cpp COFF/COFFObjcopy.cpp + COFF/Object.cpp COFF/Reader.cpp COFF/Writer.cpp ELF/ELFObjcopy.cpp Index: tools/llvm-objcopy/COFF/COFFObjcopy.cpp =================================================================== --- tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -26,6 +26,44 @@ using namespace object; using namespace COFF; +static Error handleArgs(const CopyConfig &Config, Object &Obj) { + // 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) { + if (is_contained(Config.SymbolsToKeep, Sym.Name)) + return false; + + if (Config.StripAll || Config.StripAllGNU) + return true; + + if (is_contained(Config.SymbolsToRemove, Sym.Name)) { + // Explicitly removing a referenced symbol is an error, while + // implicitly removing them as part of a blanket removal above is + // accepted. + if (Sym.Referenced) + reportError(Config.OutputFilename, + make_error("Can't remove referenced symbol " + + Sym.Name, + object_error::parse_failed)); + return true; + } + + // GNU binutils objcopy keeps referenced local symbols if Config.DiscaredAll + // is set. + if ((Config.StripUnneeded || Config.DiscardAll) && !Sym.Referenced && + (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC || Obj.IsPE)) + return true; + + return false; + }); + return Error::success(); +} + void executeObjcopyOnBinary(const CopyConfig &Config, object::COFFObjectFile &In, Buffer &Out) { COFFReader Reader(In); @@ -34,6 +72,8 @@ 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)); Index: tools/llvm-objcopy/COFF/Object.h =================================================================== --- tools/llvm-objcopy/COFF/Object.h +++ tools/llvm-objcopy/COFF/Object.h @@ -11,6 +11,7 @@ #define LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" @@ -22,10 +23,19 @@ 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 Contents; - std::vector Relocs; + std::vector Relocs; StringRef Name; }; @@ -33,6 +43,9 @@ object::coff_symbol32 Sym; StringRef Name; ArrayRef AuxData; + size_t Key; + size_t RawIdx; + bool Referenced = false; }; struct Object { @@ -50,6 +63,18 @@ std::vector DataDirectories; std::vector
Sections; std::vector Symbols; + DenseMap SymbolMap; + size_t NextSymbolKey = 0; + + // Update SymbolMap and RawIdx in each Symbol. This must be called + // after updating the Symbols vector from outside of this class. + void updateSymbols(); + + void removeSymbols(function_ref ToRemove); + + // Set the Referenced field on all Symbols, based on relocations in + // all sections. + Error markSymbols(); }; // Copy between coff_symbol16 and coff_symbol32. Index: tools/llvm-objcopy/COFF/Object.cpp =================================================================== --- /dev/null +++ tools/llvm-objcopy/COFF/Object.cpp @@ -0,0 +1,55 @@ +//===- 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 + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; + +void Object::updateSymbols() { + SymbolMap.clear(); + size_t RawSymIdx = 0; + for (Symbol &Sym : Symbols) { + SymbolMap[Sym.Key] = &Sym; + Sym.RawIdx = RawSymIdx; + RawSymIdx += 1 + Sym.Sym.NumberOfAuxSymbols; + } +} + +void Object::removeSymbols(function_ref 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("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 Index: tools/llvm-objcopy/COFF/Reader.h =================================================================== --- tools/llvm-objcopy/COFF/Reader.h +++ tools/llvm-objcopy/COFF/Reader.h @@ -35,6 +35,7 @@ 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) {} Index: tools/llvm-objcopy/COFF/Reader.cpp =================================================================== --- tools/llvm-objcopy/COFF/Reader.cpp +++ tools/llvm-objcopy/COFF/Reader.cpp @@ -102,6 +102,7 @@ if (auto EC = COFFObj.getSymbolName(SymRef, Sym.Name)) return errorCodeToError(EC); Sym.AuxData = COFFObj.getSymbolAuxData(SymRef); + Sym.Key = Obj.NextSymbolKey++; assert((Sym.AuxData.size() % (IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16))) == 0); I += 1 + Sym.Sym.NumberOfAuxSymbols; @@ -109,6 +110,29 @@ return Error::success(); } +Error COFFReader::setRelocTargets(Object &Obj) const { + std::vector RawSymbolTable; + for (Symbol &Sym : Obj.Symbols) { + 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("SymbolTableIndex out of range", + object_error::parse_failed); + Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex]; + if (Sym == nullptr) + return make_error("Invalid SymbolTableIndex", + object_error::parse_failed); + R.Target = Sym->Key; + R.TargetName = Sym->Name; + } + } + return Error::success(); +} + Expected> COFFReader::create() const { auto Obj = llvm::make_unique(); @@ -136,6 +160,9 @@ return std::move(E); if (Error E = readSymbols(*Obj, IsBigObj)) return std::move(E); + if (Error E = setRelocTargets(*Obj)) + return std::move(E); + Obj->updateSymbols(); return std::move(Obj); } Index: tools/llvm-objcopy/COFF/Writer.h =================================================================== --- tools/llvm-objcopy/COFF/Writer.h +++ tools/llvm-objcopy/COFF/Writer.h @@ -40,11 +40,12 @@ size_t SizeOfInitializedData; StringTableBuilder StrTabBuilder; + Error finalizeRelocTargets(); void layoutSections(); size_t finalizeStringTable(); template std::pair finalizeSymbolTable(); - void finalize(bool IsBigObj); + Error finalize(bool IsBigObj); void writeHeaders(bool IsBigObj); void writeSections(); Index: tools/llvm-objcopy/COFF/Writer.cpp =================================================================== --- tools/llvm-objcopy/COFF/Writer.cpp +++ tools/llvm-objcopy/COFF/Writer.cpp @@ -27,6 +27,20 @@ Writer::~Writer() {} +Error COFFWriter::finalizeRelocTargets() { + for (Section &Sec : Obj.Sections) { + for (Relocation &R : Sec.Relocs) { + auto It = Obj.SymbolMap.find(R.Target); + if (It == Obj.SymbolMap.end()) + return make_error("Relocation target " + R.TargetName + + " (" + Twine(R.Target) + ") not found", + object_error::invalid_symbol_index); + R.Reloc.SymbolTableIndex = It->second->RawIdx; + } + } + return Error::success(); +} + void COFFWriter::layoutSections() { for (auto &S : Obj.Sections) { if (S.Header.SizeOfRawData > 0) @@ -81,7 +95,10 @@ return std::make_pair(SymTabSize, sizeof(SymbolTy)); } -void COFFWriter::finalize(bool IsBigObj) { +Error COFFWriter::finalize(bool IsBigObj) { + if (Error E = finalizeRelocTargets()) + return E; + size_t SizeOfHeaders = 0; FileAlignment = 1; size_t PeHeaderSize = 0; @@ -149,6 +166,8 @@ Obj.CoffFileHeader.NumberOfSymbols = NumRawSymbols; FileSize += SymTabSize + StrTabSize; FileSize = alignTo(FileSize, FileAlignment); + + return Error::success(); } void COFFWriter::writeHeaders(bool IsBigObj) { @@ -225,8 +244,10 @@ S.Header.SizeOfRawData - S.Contents.size()); Ptr += S.Header.SizeOfRawData; - std::copy(S.Relocs.begin(), S.Relocs.end(), - reinterpret_cast(Ptr)); + for (const auto &R : S.Relocs) { + memcpy(Ptr, &R.Reloc, sizeof(R.Reloc)); + Ptr += sizeof(R.Reloc); + } } } @@ -248,7 +269,8 @@ } Error COFFWriter::write(bool IsBigObj) { - finalize(IsBigObj); + if (Error E = finalize(IsBigObj)) + return E; Buf.allocate(FileSize);