Skip to content

Commit

Permalink
[llvm-objcopy] [COFF] Add support for removing symbols
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D55881

llvm-svn: 350893
  • Loading branch information
mstorsjo committed Jan 10, 2019
1 parent 2511670 commit 10b7296
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 14 deletions.
53 changes: 53 additions & 0 deletions llvm/test/tools/llvm-objcopy/COFF/Inputs/strip-symbols.yaml
@@ -0,0 +1,53 @@
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
sections:
- Name: .text
Characteristics: [ ]
Alignment: 4
SectionData: 488B0500000000488B0500000000488B0500000000
Relocations:
- VirtualAddress: 3
SymbolTableIndex: 0
Type: IMAGE_REL_AMD64_REL32
- VirtualAddress: 10
SymbolTableIndex: 1
Type: IMAGE_REL_AMD64_REL32
- VirtualAddress: 17
SymbolName: foo
Type: IMAGE_REL_AMD64_REL32
- Name: .rdata
Characteristics: [ ]
Alignment: 1
SectionData: '00'
- Name: .rdata
Characteristics: [ ]
Alignment: 1
SectionData: '01'
symbols:
- Name: .rdata
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- Name: .rdata
Value: 0
SectionNumber: 3
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- Name: mainfunc
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: foo
Value: 0
SectionNumber: 3
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...
5 changes: 5 additions & 0 deletions llvm/test/tools/llvm-objcopy/COFF/strip-reloc-symbol.test
@@ -0,0 +1,5 @@
# RUN: yaml2obj %p/Inputs/strip-symbols.yaml > %t.o
# RUN: not llvm-objcopy -N foo %t.o 2>&1 | FileCheck %s --check-prefix=ERROR
# RUN: not llvm-objcopy --strip-symbol foo %t.o 2>&1 | FileCheck %s --check-prefix=ERROR

# ERROR: error: '{{.*}}/strip-reloc-symbol.test.tmp.o': not stripping symbol 'foo' because it is named in a relocation.
32 changes: 32 additions & 0 deletions llvm/test/tools/llvm-objcopy/COFF/strip-symbol.test
@@ -0,0 +1,32 @@
# RUN: yaml2obj %p/Inputs/strip-symbols.yaml > %t.in.o

# RUN: llvm-readobj -relocations %t.in.o | FileCheck %s --check-prefixes=RELOCS,RELOCS-PRE
# RUN: llvm-objdump -t %t.in.o | FileCheck %s --check-prefixes=SYMBOLS,SYMBOLS-PRE

# RUN: llvm-objcopy -N mainfunc %t.in.o %t.out.o
# RUN: llvm-readobj -relocations %t.out.o | FileCheck %s --check-prefixes=RELOCS,RELOCS-POST
# RUN: llvm-objdump -t %t.out.o | FileCheck %s --check-prefix=SYMBOLS

# RUN: llvm-objcopy --strip-symbol mainfunc %t.in.o %t.out.o
# RUN: llvm-readobj -relocations %t.out.o | FileCheck %s --check-prefixes=RELOCS,RELOCS-POST
# RUN: llvm-objdump -t %t.out.o | FileCheck %s --check-prefix=SYMBOLS

# Explicitly listing the relocations for the input as well, to show
# that the symbol index of the symbol foo is updated in the relocations,
# while keeping relocations to two distinct .rdata symbols separate.

# RELOCS: Relocations [
# RELOCS-NEXT: Section (1) .text {
# RELOCS-NEXT: 0x3 IMAGE_REL_AMD64_REL32 .rdata (0)
# RELOCS-NEXT: 0xA IMAGE_REL_AMD64_REL32 .rdata (1)
# RELOCS-PRE-NEXT: 0x11 IMAGE_REL_AMD64_REL32 foo (3)
# RELOCS-POST-NEXT: 0x11 IMAGE_REL_AMD64_REL32 foo (2)
# RELOCS-NEXT: }
# RELOCS-NEXT: ]

# SYMBOLS: SYMBOL TABLE:
# SYMBOLS-NEXT: .rdata
# SYMBOLS-NEXT: .rdata
# SYMBOLS-PRE-NEXT: mainfunc
# SYMBOLS-NEXT: foo
# SYMBOLS-EMPTY:
1 change: 1 addition & 0 deletions llvm/tools/llvm-objcopy/CMakeLists.txt
Expand Up @@ -18,6 +18,7 @@ add_llvm_tool(llvm-objcopy
CopyConfig.cpp
llvm-objcopy.cpp
COFF/COFFObjcopy.cpp
COFF/Object.cpp
COFF/Reader.cpp
COFF/Writer.cpp
ELF/ELFObjcopy.cpp
Expand Down
27 changes: 27 additions & 0 deletions llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
Expand Up @@ -17,6 +17,7 @@

#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Errc.h"
#include <cassert>

namespace llvm {
Expand All @@ -26,6 +27,30 @@ namespace coff {
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.SymbolsToRemove.empty())
if (Error E = Obj.markSymbols())
return E;

// Actually do removals of symbols.
Obj.removeSymbols([&](const Symbol &Sym) {
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;
}

return false;
});
return Error::success();
}

void executeObjcopyOnBinary(const CopyConfig &Config,
object::COFFObjectFile &In, Buffer &Out) {
COFFReader Reader(In);
Expand All @@ -34,6 +59,8 @@ void executeObjcopyOnBinary(const CopyConfig &Config,
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));
Expand Down
70 changes: 70 additions & 0 deletions 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
37 changes: 36 additions & 1 deletion llvm/tools/llvm-objcopy/COFF/Object.h
Expand Up @@ -11,7 +11,9 @@
#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>
Expand All @@ -22,17 +24,26 @@ namespace llvm {
namespace objcopy {
namespace coff {

struct Relocation {
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<object::coff_relocation> Relocs;
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 {
Expand All @@ -49,7 +60,31 @@ struct Object {

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.
Expand Down
34 changes: 31 additions & 3 deletions llvm/tools/llvm-objcopy/COFF/Reader.cpp
Expand Up @@ -73,7 +73,7 @@ Error COFFReader::readSections(Object &Obj) const {
return errorCodeToError(EC);
ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec);
for (const coff_relocation &R : Relocs)
S.Relocs.push_back(R);
S.Relocs.push_back(Relocation{R});
if (auto EC = COFFObj.getSectionName(Sec, S.Name))
return errorCodeToError(EC);
if (Sec->hasExtendedRelocations())
Expand All @@ -84,14 +84,16 @@ Error COFFReader::readSections(Object &Obj) const {
}

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;

Obj.Symbols.push_back(Symbol());
Symbol &Sym = Obj.Symbols.back();
Symbols.push_back(Symbol());
Symbol &Sym = Symbols.back();
// Copy symbols from the original form into an intermediate coff_symbol32.
if (IsBigObj)
copySymbol(Sym.Sym,
Expand All @@ -106,6 +108,30 @@ Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
(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();
}

Expand Down Expand Up @@ -136,6 +162,8 @@ Expected<std::unique_ptr<Object>> COFFReader::create() const {
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);
}
Expand Down
1 change: 1 addition & 0 deletions llvm/tools/llvm-objcopy/COFF/Reader.h
Expand Up @@ -35,6 +35,7 @@ class COFFReader : public Reader {
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) {}
Expand Down

0 comments on commit 10b7296

Please sign in to comment.