[LinkerScript] Handle symbols defined in linker scripts

Puts symbols defined in linker script expressions in a runtime file that is
added as input to the resolver, making the input object files see symbols
defined in linker scripts.

http://reviews.llvm.org/D8263

llvm-svn: 232409
This commit is contained in:
Rafael Auler 2015-03-16 20:39:07 +00:00
parent 18e92078f2
commit 01d73c9678
12 changed files with 191 additions and 27 deletions

View File

@ -306,6 +306,7 @@ public:
void setUndefinesResolver(std::unique_ptr<File> resolver);
script::Sema &linkerScriptSema() { return _linkerScriptSema; }
const script::Sema &linkerScriptSema() const { return _linkerScriptSema; }
private:
ELFLinkingContext() = delete;

View File

@ -21,6 +21,7 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/MemoryBuffer.h"
@ -1264,6 +1265,14 @@ public:
/// update our symbol table with new symbols calculated in this expression.
std::error_code evalExpr(const SymbolAssignment *assgn, uint64_t &curPos);
/// Retrieve the set of symbols defined in linker script expressions.
const llvm::StringSet<> &getScriptDefinedSymbols() const;
/// Queries the linker script symbol table for the value of a given symbol.
/// This function must be called after linker script expressions evaluation
/// has been performed (by calling evalExpr() for all expressions).
uint64_t getLinkerScriptExprValue(StringRef name) const;
void dump() const;
private:
@ -1370,6 +1379,7 @@ private:
mutable std::unordered_map<SectionKey, int, SectionKeyHash, SectionKeyEq>
_cacheSectionOrder, _cacheExpressionOrder;
llvm::DenseSet<int> _deliveredExprs;
mutable llvm::StringSet<> _definedSymbols;
Expression::SymbolTableTy _symbolTable;
};

View File

@ -37,7 +37,7 @@ protected:
unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override;
void processUndefinedSymbol(StringRef symName,
CRuntimeFile<ELFT> &file) const override;
RuntimeFile<ELFT> &file) const override;
private:
ARMLinkingContext &_context;
ARMTargetLayout<ELFT> &_armLayout;
@ -83,7 +83,7 @@ unique_bump_ptr<SymbolTable<ELFT>>
template <class ELFT>
void ARMExecutableWriter<ELFT>::processUndefinedSymbol(
StringRef symName, CRuntimeFile<ELFT> &file) const {
StringRef symName, RuntimeFile<ELFT> &file) const {
if (symName == _gotSymbol) {
file.addAbsoluteAtom(_gotSymbol);
} else if (symName.startswith("__exidx")) {

View File

@ -27,7 +27,7 @@ class DynamicLibraryWriter : public OutputELFWriter<ELFT> {
public:
DynamicLibraryWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout)
: OutputELFWriter<ELFT>(context, layout),
_runtimeFile(new CRuntimeFile<ELFT>(context)) {}
_runtimeFile(new RuntimeFile<ELFT>(context, "C runtime")) {}
protected:
virtual void buildDynamicSymbolTable(const File &file);
@ -36,7 +36,7 @@ protected:
virtual void finalizeDefaultAtomValues();
protected:
std::unique_ptr<CRuntimeFile<ELFT> > _runtimeFile;
std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile;
};
//===----------------------------------------------------------------------===//

View File

@ -430,14 +430,14 @@ protected:
};
/// \brief All atoms are owned by a File. To add linker specific atoms
/// the atoms need to be inserted to a file called (CRuntimeFile) which
/// the atoms need to be inserted to a file called (RuntimeFile) which
/// are basically additional symbols required by libc and other runtime
/// libraries part of executing a program. This class provides support
/// for adding absolute symbols and undefined symbols
template <class ELFT> class CRuntimeFile : public ELFFile<ELFT> {
template <class ELFT> class RuntimeFile : public ELFFile<ELFT> {
public:
typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;
CRuntimeFile(ELFLinkingContext &context, StringRef name = "C runtime")
RuntimeFile(ELFLinkingContext &context, StringRef name)
: ELFFile<ELFT>(name, context) {}
/// \brief add a global absolute atom
@ -470,7 +470,7 @@ public:
return *newAtom;
}
// cannot add atoms to C Runtime file
// cannot add atoms to Runtime file
virtual void addAtom(const Atom &) {
llvm_unreachable("cannot add atoms to Runtime files");
}

View File

@ -27,7 +27,7 @@ class ExecutableWriter : public OutputELFWriter<ELFT> {
public:
ExecutableWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout)
: OutputELFWriter<ELFT>(context, layout),
_runtimeFile(new CRuntimeFile<ELFT>(context)) {}
_runtimeFile(new RuntimeFile<ELFT>(context, "C runtime")) {}
protected:
virtual void buildDynamicSymbolTable(const File &file);
@ -41,7 +41,7 @@ protected:
}
unique_bump_ptr<InterpSection<ELFT>> _interpSection;
std::unique_ptr<CRuntimeFile<ELFT> > _runtimeFile;
std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile;
};
//===----------------------------------------------------------------------===//
@ -80,6 +80,7 @@ void ExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) {
/// absolute symbols
template<class ELFT>
void ExecutableWriter<ELFT>::addDefaultAtoms() {
OutputELFWriter<ELFT>::addDefaultAtoms();
_runtimeFile->addUndefinedAtom(this->_context.entrySymbolName());
_runtimeFile->addAbsoluteAtom("__bss_start");
_runtimeFile->addAbsoluteAtom("__bss_end");
@ -124,6 +125,7 @@ template <class ELFT> void ExecutableWriter<ELFT>::createDefaultSections() {
/// Finalize the value of all the absolute symbols that we
/// created
template <class ELFT> void ExecutableWriter<ELFT>::finalizeDefaultAtomValues() {
OutputELFWriter<ELFT>::finalizeDefaultAtomValues();
auto bssStartAtomIter = this->_layout.findAbsoluteAtom("__bss_start");
auto bssEndAtomIter = this->_layout.findAbsoluteAtom("__bss_end");
auto underScoreEndAtomIter = this->_layout.findAbsoluteAtom("_end");

View File

@ -18,10 +18,10 @@ typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType;
class HexagonLinkingContext;
template <class HexagonELFType> class HexagonRuntimeFile
: public CRuntimeFile<HexagonELFType> {
: public RuntimeFile<HexagonELFType> {
public:
HexagonRuntimeFile(HexagonLinkingContext &context)
: CRuntimeFile<HexagonELFType>(context, "Hexagon runtime file") {}
: RuntimeFile<HexagonELFType>(context, "Hexagon runtime file") {}
};
} // elf
} // lld

View File

@ -87,10 +87,10 @@ private:
/// \brief Mips Runtime file.
template <class ELFType>
class MipsRuntimeFile final : public CRuntimeFile<ELFType> {
class MipsRuntimeFile final : public RuntimeFile<ELFType> {
public:
MipsRuntimeFile(MipsLinkingContext &ctx)
: CRuntimeFile<ELFType>(ctx, "Mips runtime file") {}
: RuntimeFile<ELFType>(ctx, "Mips runtime file") {}
};
/// \brief Auxiliary class holds relocation's names table.

View File

@ -33,14 +33,14 @@ template <class ELFT> class TargetLayout;
namespace {
template<class ELFT>
class SymbolFile : public CRuntimeFile<ELFT> {
class SymbolFile : public RuntimeFile<ELFT> {
public:
SymbolFile(ELFLinkingContext &context)
: CRuntimeFile<ELFT>(context, "Dynamic absolute symbols"),
: RuntimeFile<ELFT>(context, "Dynamic absolute symbols"),
_atomsAdded(false) {}
Atom *addAbsoluteAtom(StringRef symbolName) override {
auto *a = CRuntimeFile<ELFT>::addAbsoluteAtom(symbolName);
auto *a = RuntimeFile<ELFT>::addAbsoluteAtom(symbolName);
if (a) _atomsAdded = true;
return a;
}
@ -57,7 +57,7 @@ private:
template<class ELFT>
class DynamicSymbolFile : public SimpleArchiveLibraryFile {
typedef std::function<void(StringRef, CRuntimeFile<ELFT> &)> Resolver;
typedef std::function<void(StringRef, RuntimeFile<ELFT> &)> Resolver;
public:
DynamicSymbolFile(ELFLinkingContext &context, Resolver resolver)
: SimpleArchiveLibraryFile("Dynamically added runtime symbols"),
@ -99,7 +99,7 @@ public:
typedef Elf_Sym_Impl<ELFT> Elf_Sym;
typedef Elf_Dyn_Impl<ELFT> Elf_Dyn;
OutputELFWriter(const ELFLinkingContext &context, TargetLayout<ELFT> &layout);
OutputELFWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout);
protected:
// build the sections that need to be created
@ -141,13 +141,13 @@ protected:
virtual void assignSectionsWithNoSegments();
// Add default atoms that need to be present in the output file
virtual void addDefaultAtoms() = 0;
virtual void addDefaultAtoms();
// Add any runtime files and their atoms to the output
bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override;
// Finalize the default atom values
virtual void finalizeDefaultAtomValues() = 0;
virtual void finalizeDefaultAtomValues();
// This is called by the write section to apply relocations
uint64_t addressOfAtom(const Atom *atom) override {
@ -180,11 +180,11 @@ protected:
/// \brief Process undefined symbols that left after resolution step.
virtual void processUndefinedSymbol(StringRef symName,
CRuntimeFile<ELFT> &file) const {}
RuntimeFile<ELFT> &file) const {}
llvm::BumpPtrAllocator _alloc;
const ELFLinkingContext &_context;
ELFLinkingContext &_context;
TargetHandler<ELFT> &_targetHandler;
typedef llvm::DenseMap<const Atom *, uint64_t> AtomToAddress;
@ -205,6 +205,7 @@ protected:
unique_bump_ptr<HashSection<ELFT>> _hashTable;
llvm::StringSet<> _soNeeded;
/// @}
std::unique_ptr<RuntimeFile<ELFT>> _scriptFile;
private:
static StringRef maybeGetSOName(Node *node);
@ -214,10 +215,11 @@ private:
// OutputELFWriter
//===----------------------------------------------------------------------===//
template <class ELFT>
OutputELFWriter<ELFT>::OutputELFWriter(const ELFLinkingContext &context,
OutputELFWriter<ELFT>::OutputELFWriter(ELFLinkingContext &context,
TargetLayout<ELFT> &layout)
: _context(context), _targetHandler(context.getTargetHandler<ELFT>()),
_layout(layout) {}
_layout(layout),
_scriptFile(new RuntimeFile<ELFT>(context, "Linker script runtime")) {}
template <class ELFT>
void OutputELFWriter<ELFT>::buildChunks(const File &file) {
@ -363,20 +365,41 @@ void OutputELFWriter<ELFT>::assignSectionsWithNoSegments() {
_shdrtab->updateSection(section);
}
template <class ELFT> void OutputELFWriter<ELFT>::addDefaultAtoms() {
const llvm::StringSet<> &symbols =
_context.linkerScriptSema().getScriptDefinedSymbols();
for (auto &sym : symbols)
_scriptFile->addAbsoluteAtom(sym.getKey());
}
template <class ELFT>
bool OutputELFWriter<ELFT>::createImplicitFiles(
std::vector<std::unique_ptr<File>> &) {
std::vector<std::unique_ptr<File>> &result) {
// Add the virtual archive to resolve undefined symbols.
// The file will be added later in the linking context.
auto callback = [this](StringRef sym, CRuntimeFile<ELFT> &file) {
auto callback = [this](StringRef sym, RuntimeFile<ELFT> &file) {
processUndefinedSymbol(sym, file);
};
auto &ctx = const_cast<ELFLinkingContext &>(_context);
ctx.setUndefinesResolver(
llvm::make_unique<DynamicSymbolFile<ELFT>>(ctx, std::move(callback)));
// Add script defined symbols
result.push_back(std::move(_scriptFile));
return true;
}
template <class ELFT>
void OutputELFWriter<ELFT>::finalizeDefaultAtomValues() {
const llvm::StringSet<> &symbols =
_context.linkerScriptSema().getScriptDefinedSymbols();
for (auto &sym : symbols) {
uint64_t res =
_context.linkerScriptSema().getLinkerScriptExprValue(sym.getKey());
auto a = _layout.findAbsoluteAtom(sym.getKey());
(*a)->_virtualAddr = res;
}
}
template <class ELFT> void OutputELFWriter<ELFT>::createDefaultSections() {
_elfHeader.reset(new (_alloc) ELFHeader<ELFT>(_context));
_programHeader.reset(new (_alloc) ProgramHeader<ELFT>(_context));

View File

@ -2232,6 +2232,28 @@ std::error_code Sema::evalExpr(const SymbolAssignment *assgn,
return std::error_code();
}
const llvm::StringSet<> &Sema::getScriptDefinedSymbols() const {
// Do we have cached results?
if (!_definedSymbols.empty())
return _definedSymbols;
// Populate our defined set and return it
for (auto cmd : _layoutCommands)
if (auto sa = dyn_cast<SymbolAssignment>(cmd)) {
StringRef symbol = sa->symbol();
if (!symbol.empty() && symbol != ".")
_definedSymbols.insert(symbol);
}
return _definedSymbols;
}
uint64_t Sema::getLinkerScriptExprValue(StringRef name) const {
auto it = _symbolTable.find(name);
assert (it != _symbolTable.end() && "Invalid symbol name!");
return it->second;
}
void Sema::dump() const {
raw_ostream &os = llvm::outs();
os << "Linker script semantics dump\n";

View File

@ -0,0 +1,52 @@
---
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
OSABI: ELFOSABI_GNU
Type: ET_REL
Machine: EM_X86_64
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
AddressAlign: 0x0000000000000004
Content: B80100000048C7C70100000048C7C60000000048C7C20E0000000F05C3E8DEFFFFFFB83C0000000F05C3
- Name: .rela.text
Type: SHT_RELA
Link: .symtab
AddressAlign: 0x0000000000000008
Info: .text
Relocations:
- Offset: 0x000000000000000F
Symbol: .data
Type: R_X86_64_32S
- Name: .data
Type: SHT_PROGBITS
Flags: [ SHF_WRITE, SHF_ALLOC ]
AddressAlign: 0x0000000000000004
Content: 48656C6C6F2C20576F726C64210A00
- Name: .bss
Type: SHT_NOBITS
Flags: [ SHF_WRITE, SHF_ALLOC ]
AddressAlign: 0x0000000000000004
Content: ''
Symbols:
Local:
- Name: main
Section: .text
- Name: msg
Section: .data
- Name: .text
Type: STT_SECTION
Section: .text
- Name: .data
Type: STT_SECTION
Section: .data
- Name: .bss
Type: STT_SECTION
Section: .bss
Global:
- Name: _start
Section: .text
Value: 0x000000000000001D
...

View File

@ -0,0 +1,54 @@
/*
We test whether we can define symbols in a linker script and have them exported
to the output file symbol table.
This test uses a single X86-64 input object, simple.o, created with the
following X86-64 assembly code:
*** simple.S:
(command line clang -c simple.S -o simple.o)
.text
main:
mov $1, %eax
movq $1, %rdi
movq $msg, %rsi
movq $14, %rdx
syscall
ret
.globl _start
_start:
call main
mov $60, %eax
syscall
ret
.data
msg: .asciz "Hello, World!\n"
We use the following linker script for this test:
*/
ENTRY(_start)
SECTIONS
{
. = 0x500000;
.text : { *(.text) }
MYSTRING = .;
.data : { *(.data) }
}
/*
RUN: mkdir -p %T
RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%T/simple.o
RUN: lld -flavor gnu -target x86_64 -T %s %T/simple.o -static -o %t1
RUN: llvm-readobj -symbols %t1 | FileCheck -check-prefix CHECKSYMS %s
CHECKSYMS: Name: MYSTRING
CHECKSYMS-NEXT: Value: 0x501000
*/