[ELF] Add support for IFUNC.

This sadly doesn't have a test for the final
output because llvm-objdump can't dump relocations
that don't belong to a section :(

llvm-svn: 173808
This commit is contained in:
Michael J. Spencer 2013-01-29 16:38:03 +00:00
parent 978b5a0e02
commit 289dcedea5
12 changed files with 291 additions and 32 deletions

View File

@ -18,6 +18,8 @@
#include <memory>
namespace lld {
class DefinedAtom;
class Reference;
namespace elf { template <typename ELFT> class ELFTargetHandler; }
@ -37,6 +39,11 @@ public:
virtual StringRef getEntry() const;
virtual uint64_t getBaseAddress() const { return _options._baseAddress; }
virtual bool isRuntimeRelocation(const DefinedAtom &,
const Reference &) const {
return false;
}
static std::unique_ptr<ELFTargetInfo> create(const LinkerOptions &lo);
template <typename ELFT>

View File

@ -23,7 +23,10 @@
namespace lld {
class SimpleFile : public MutableFile {
public:
SimpleFile(const TargetInfo &ti, StringRef path) : MutableFile(ti, path) {}
SimpleFile(const TargetInfo &ti, StringRef path) : MutableFile(ti, path) {
static uint32_t lastOrdinal = 0;
_ordinal = lastOrdinal++;
}
virtual void addAtom(const Atom &atom) {
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&atom)) {

View File

@ -61,6 +61,7 @@ public:
ORDER_TEXT = 70,
ORDER_PLT = 80,
ORDER_FINI = 90,
ORDER_REL = 95,
ORDER_RODATA = 100,
ORDER_EH_FRAME = 110,
ORDER_EH_FRAMEHDR = 120,
@ -251,6 +252,16 @@ public:
return _programHeader;
}
ELFRelocationTable<ELFT> *getRelocationTable() {
// Only create the relocation table if it is needed.
if (!_relocationTable) {
_relocationTable = new (_allocator)
ELFRelocationTable<ELFT>(_targetInfo, ".rela.plt", ORDER_REL);
addSection(_relocationTable);
}
return _relocationTable;
}
private:
SectionMapT _sectionMap;
MergedSectionMapT _mergedSectionMap;
@ -260,6 +271,7 @@ private:
std::vector<MergedSections<ELFT> *> _mergedSections;
ELFHeader<ELFT> *_elfHeader;
ELFProgramHeader<ELFT> *_programHeader;
ELFRelocationTable<ELFT> *_relocationTable;
std::vector<AbsoluteAtomPair> _absoluteAtoms;
llvm::BumpPtrAllocator _allocator;
const ELFTargetInfo &_targetInfo;
@ -272,6 +284,7 @@ DefaultELFLayout<ELFT>::getSectionOrder(const StringRef name,
int32_t contentPermissions)
{
switch (contentType) {
case DefinedAtom::typeResolver:
case DefinedAtom::typeCode:
return llvm::StringSwitch<Reference::Kind>(name)
.StartsWith(".eh_frame_hdr", ORDER_EH_FRAMEHDR)
@ -292,6 +305,11 @@ DefaultELFLayout<ELFT>::getSectionOrder(const StringRef name,
case DefinedAtom::typeZeroFill:
return ORDER_BSS;
case DefinedAtom::typeGOT:
return ORDER_GOT;
case DefinedAtom::typeStub:
return ORDER_PLT;
default:
// If we get passed in a section push it to OTHER
if (contentPermissions == DefinedAtom::perm___)
@ -327,6 +345,7 @@ DefaultELFLayout<ELFT>::getSegmentType(Section<ELFT> *section) const {
case ORDER_HASH:
case ORDER_DYNAMIC_SYMBOLS:
case ORDER_DYNAMIC_STRINGS:
case ORDER_REL:
case ORDER_INIT:
case ORDER_PLT:
case ORDER_FINI:
@ -343,9 +362,9 @@ DefaultELFLayout<ELFT>::getSegmentType(Section<ELFT> *section) const {
case ORDER_CTORS:
case ORDER_DTORS:
case ORDER_GOT:
return llvm::ELF::PT_GNU_RELRO;
case ORDER_GOT:
case ORDER_GOT_PLT:
case ORDER_DATA:
case ORDER_BSS:
@ -366,6 +385,7 @@ DefaultELFLayout<ELFT>::hasOutputSegment(Section<ELFT> *section) {
case ORDER_HASH:
case ORDER_DYNAMIC_SYMBOLS:
case ORDER_DYNAMIC_STRINGS:
case ORDER_REL:
case ORDER_INIT:
case ORDER_PLT:
case ORDER_TEXT:
@ -415,6 +435,10 @@ DefaultELFLayout<ELFT>::addAtom(const Atom *atom) {
section = _sectionMap[sectionKey];
}
section->appendAtom(atom);
// Add runtime relocations to the .rela section.
for (const auto &reloc : *definedAtom)
if (_targetInfo.isRuntimeRelocation(*definedAtom, *reloc))
getRelocationTable()->addRelocation(*definedAtom, *reloc);
} else if (const AbsoluteAtom *absoluteAtom = dyn_cast<AbsoluteAtom>(atom)) {
// Absolute atoms are not part of any section, they are global for the whole
// link

View File

@ -227,9 +227,16 @@ Section<ELFT>::appendAtom(const Atom *atom) {
case DefinedAtom::typeCode:
case DefinedAtom::typeData:
case DefinedAtom::typeConstant:
case DefinedAtom::typeGOT:
case DefinedAtom::typeStub:
case DefinedAtom::typeResolver:
_atoms.push_back(AtomLayout(atom, fOffset, 0));
this->_fsize = fOffset + definedAtom->size();
this->_msize = mOffset + definedAtom->size();
DEBUG_WITH_TYPE("Section",
llvm::dbgs() << "[" << this->name() << " " << this << "] "
<< "Adding atom: " << atom->name() << "@"
<< fOffset << "\n");
break;
case DefinedAtom::typeZeroFill:
_atoms.push_back(AtomLayout(atom, mOffset, 0));
@ -286,10 +293,16 @@ Section<ELFT>::flags() {
template<class ELFT>
int
Section<ELFT>::type() {
if (_sectionKind == K_SymbolTable)
return llvm::ELF::SHT_SYMTAB;
switch (_contentType) {
case DefinedAtom::typeCode:
case DefinedAtom::typeData:
case DefinedAtom::typeConstant:
case DefinedAtom::typeGOT:
case DefinedAtom::typeStub:
case DefinedAtom::typeResolver:
return llvm::ELF::SHT_PROGBITS;
case DefinedAtom::typeZeroFill:
@ -332,6 +345,9 @@ template <class ELFT>
void Section<ELFT>::write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
for (auto &ai : _atoms) {
DEBUG_WITH_TYPE("Section",
llvm::dbgs() << "Writing atom: " << ai._atom->name()
<< " | " << ai._fileOffset << "\n");
const DefinedAtom *definedAtom = cast<DefinedAtom>(ai._atom);
if (definedAtom->contentType() == DefinedAtom::typeZeroFill)
continue;
@ -603,11 +619,17 @@ ELFSymbolTable<ELFT>::addSymbol(const Atom *atom,
lld::DefinedAtom::ContentType ct;
switch (ct = da->contentType()){
case DefinedAtom::typeCode:
case DefinedAtom::typeStub:
symbol->st_value = addr;
type = llvm::ELF::STT_FUNC;
break;
case DefinedAtom::typeResolver:
symbol->st_value = addr;
type = llvm::ELF::STT_GNU_IFUNC;
break;
case DefinedAtom::typeData:
case DefinedAtom::typeConstant:
case DefinedAtom::typeGOT:
symbol->st_value = addr;
type = llvm::ELF::STT_OBJECT;
break;
@ -678,6 +700,46 @@ void ELFSymbolTable<ELFT>::write(ELFWriter *writer,
}
}
template <class ELFT> class ELFRelocationTable : public Section<ELFT> {
public:
typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela;
ELFRelocationTable(const ELFTargetInfo &ti, StringRef str, int32_t order)
: Section<ELFT>(ti, str, llvm::ELF::SHT_RELA, DefinedAtom::permR__, order,
Section<ELFT>::K_Default) {
this->setOrder(order);
this->_entSize = sizeof(Elf_Rela);
this->_align2 = llvm::alignOf<Elf_Rela>();
}
void addRelocation(const DefinedAtom &da, const Reference &r) {
_relocs.emplace_back(da, r);
this->_fsize = _relocs.size() * sizeof(Elf_Rela);
this->_msize = this->_fsize;
}
void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
uint8_t *dest = chunkBuffer + this->fileOffset();
for (const auto &rel : _relocs) {
Elf_Rela *r = reinterpret_cast<Elf_Rela *>(dest);
r->setSymbolAndType(0, rel.second.kind());
r->r_offset =
writer->addressOfAtom(&rel.first) + rel.second.offsetInAtom();
r->r_addend =
writer->addressOfAtom(rel.second.target()) + rel.second.addend();
dest += sizeof(Elf_Rela);
DEBUG_WITH_TYPE("ELFRelocationTable", llvm::dbgs()
<< "IRELATIVE relocation at " << rel.first.name() << "@"
<< r->r_offset << " to " << rel.second.target()->name()
<< "@" << r->r_addend << "\n");
}
}
private:
std::vector<std::pair<const DefinedAtom &, const Reference &>> _relocs;
};
} // elf
} // lld

View File

@ -52,6 +52,9 @@ public:
FileELF(const ELFTargetInfo &ti, std::unique_ptr<llvm::MemoryBuffer> MB,
llvm::error_code &EC)
: File(MB->getBufferIdentifier()), _elfTargetInfo(ti) {
static uint32_t lastOrdinal = 0;
_ordinal = lastOrdinal++;
llvm::OwningPtr<llvm::object::Binary> binaryFile;
EC = createBinary(MB.release(), binaryFile);
if (EC)

View File

@ -156,6 +156,8 @@ void ELFExecutableWriter<ELFT>::addDefaultAtoms() {
_runtimeFile.addAbsoluteAtom("end");
_runtimeFile.addAbsoluteAtom("__init_array_start");
_runtimeFile.addAbsoluteAtom("__init_array_end");
_runtimeFile.addAbsoluteAtom("__rela_iplt_start");
_runtimeFile.addAbsoluteAtom("__rela_iplt_end");
}
/// \brief Hook in lld to add CRuntime file
@ -175,16 +177,24 @@ void ELFExecutableWriter<ELFT>::finalizeDefaultAtomValues() {
auto endAtomIter = _layout->findAbsoluteAtom("end");
auto initArrayStartIter = _layout->findAbsoluteAtom("__init_array_start");
auto initArrayEndIter = _layout->findAbsoluteAtom("__init_array_end");
auto realIpltStartIter = _layout->findAbsoluteAtom("__rela_iplt_start");
auto realIpltEndIter = _layout->findAbsoluteAtom("__rela_iplt_end");
auto section = _layout->findOutputSection(".init_array");
auto startEnd = [&](typename DefaultELFLayout<ELFT>::AbsoluteAtomIterT start,
typename DefaultELFLayout<ELFT>::AbsoluteAtomIterT end,
StringRef sec) -> void {
auto section = _layout->findOutputSection(sec);
if (section) {
initArrayStartIter->setValue(section->virtualAddr());
initArrayEndIter->setValue(section->virtualAddr() +
section->memSize());
start->setValue(section->virtualAddr());
end->setValue(section->virtualAddr() + section->memSize());
} else {
initArrayStartIter->setValue(0);
initArrayEndIter->setValue(0);
start->setValue(0);
end->setValue(0);
}
};
startEnd(initArrayStartIter, initArrayEndIter, ".init_array");
startEnd(realIpltStartIter, realIpltEndIter, ".rela.plt");
assert(!(bssStartAtomIter == _layout->absoluteAtoms().end() ||
bssEndAtomIter == _layout->absoluteAtoms().end() ||
@ -193,17 +203,15 @@ void ELFExecutableWriter<ELFT>::finalizeDefaultAtomValues() {
"Unable to find the absolute atoms that have been added by lld");
auto phe = _programHeader->findProgramHeader(
llvm::ELF::PT_LOAD,
llvm::ELF::PF_W,
llvm::ELF::PF_X);
llvm::ELF::PT_LOAD, llvm::ELF::PF_W, llvm::ELF::PF_X);
assert(!(phe == _programHeader->end()) &&
"Can't find a data segment in the program header!");
bssStartAtomIter->setValue((*phe)->p_vaddr+(*phe)->p_filesz);
bssEndAtomIter->setValue((*phe)->p_vaddr+(*phe)->p_memsz);
underScoreEndAtomIter->setValue((*phe)->p_vaddr+(*phe)->p_memsz);
endAtomIter->setValue((*phe)->p_vaddr+(*phe)->p_memsz);
bssStartAtomIter->setValue((*phe)->p_vaddr + (*phe)->p_filesz);
bssEndAtomIter->setValue((*phe)->p_vaddr + (*phe)->p_memsz);
underScoreEndAtomIter->setValue((*phe)->p_vaddr + (*phe)->p_memsz);
endAtomIter->setValue((*phe)->p_vaddr + (*phe)->p_memsz);
}
template<class ELFT>

View File

@ -30,12 +30,19 @@ public:
virtual uint64_t getPageSize() const { return 0x1000; }
virtual void addPasses(PassManager &) const;
virtual uint64_t getBaseAddress() const {
if (_options._baseAddress == 0)
return 0x400000;
return _options._baseAddress;
}
virtual bool isRuntimeRelocation(const DefinedAtom &,
const Reference &r) const {
return r.kind() == llvm::ELF::R_X86_64_IRELATIVE;
}
virtual ErrorOr<int32_t> relocKindFromString(StringRef str) const;
virtual ErrorOr<std::string> stringFromRelocKind(int32_t kind) const;

View File

@ -9,10 +9,129 @@
#include "X86_64ELFTargetInfo.h"
#include "lld/Core/File.h"
#include "lld/Core/Pass.h"
#include "lld/Core/PassManager.h"
#include "lld/ReaderWriter/Simple.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringSwitch.h"
using namespace lld;
namespace {
class GOTAtom : public SimpleDefinedAtom {
static const uint8_t _defaultContent[8];
public:
GOTAtom(const File &f, const DefinedAtom *target) : SimpleDefinedAtom(f) {
if (target->contentType() == typeResolver) {
DEBUG_WITH_TYPE("GOTAtom", llvm::dbgs() << "IRELATIVE relocation to "
<< target->name());
addReference(llvm::ELF::R_X86_64_IRELATIVE, 0, target, 0);
}
}
virtual StringRef name() const { return "ELF-GOTAtom"; }
virtual SectionChoice sectionChoice() const { return sectionCustomRequired; }
virtual StringRef customSectionName() const { return ".got.plt"; }
virtual ContentType contentType() const { return typeGOT; }
virtual uint64_t size() const { return rawContent().size(); }
virtual ContentPermissions permissions() const { return permRW_; }
virtual ArrayRef<uint8_t> rawContent() const {
return ArrayRef<uint8_t>(_defaultContent, 8);
}
};
const uint8_t GOTAtom::_defaultContent[8] = { 0 };
class PLTAtom : public SimpleDefinedAtom {
static const uint8_t _defaultContent[16];
public:
PLTAtom(const File &f, GOTAtom *ga) : SimpleDefinedAtom(f) {
addReference(llvm::ELF::R_X86_64_PC32, 2, ga, -4);
}
virtual StringRef name() const { return "ELF-PLTAtom"; }
virtual SectionChoice sectionChoice() const { return sectionCustomRequired; }
virtual StringRef customSectionName() const { return ".plt"; }
virtual ContentType contentType() const { return typeStub; }
virtual uint64_t size() const { return rawContent().size(); }
virtual ContentPermissions permissions() const { return permR_X; }
virtual ArrayRef<uint8_t> rawContent() const {
return ArrayRef<uint8_t>(_defaultContent, 16);
}
};
const uint8_t PLTAtom::_defaultContent[16] = {
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmpq *gotatom(%rip)
0x68, 0x00, 0x00, 0x00, 0x00, // pushq pltentry
0xe9, 0x00, 0x00, 0x00, 0x00 // jmpq plt[-1]
};
class ELFPassFile : public SimpleFile {
public:
ELFPassFile(const ELFTargetInfo &eti) : SimpleFile(eti, "ELFPassFile") {}
llvm::BumpPtrAllocator _alloc;
};
class PLTPass : public Pass {
public:
PLTPass(const ELFTargetInfo &ti) : _file(ti) {}
virtual void perform(MutableFile &mf) {
for (const auto &atom : mf.defined())
for (const auto &ref : *atom) {
if (ref->kind() != llvm::ELF::R_X86_64_PC32)
continue;
if (const DefinedAtom *da =
dyn_cast<const DefinedAtom>(ref->target())) {
if (da->contentType() != DefinedAtom::typeResolver)
continue;
// We have a PC32 call to a IFUNC. Create a plt and got entry.
// Look it up first.
const PLTAtom *pa;
auto plt = _pltMap.find(da);
if (plt == _pltMap.end()) {
// Add an entry.
auto ga = new (_file._alloc) GOTAtom(_file, da);
mf.addAtom(*ga);
pa = new (_file._alloc) PLTAtom(_file, ga);
mf.addAtom(*pa);
_pltMap[da] = pa;
} else
pa = plt->second;
// This is dirty.
const_cast<Reference *>(ref)->setTarget(pa);
}
}
}
private:
llvm::DenseMap<const DefinedAtom *, const PLTAtom *> _pltMap;
ELFPassFile _file;
};
} // end anon namespace
void elf::X86_64ELFTargetInfo::addPasses(PassManager &pm) const {
pm.add(std::unique_ptr<Pass>(new PLTPass(*this)));
}
#define LLD_CASE(name) .Case(#name, llvm::ELF::name)
ErrorOr<int32_t> elf::X86_64ELFTargetInfo::relocKindFromString(StringRef str) const {

View File

@ -112,6 +112,8 @@ bool X86_64KindHandler::isLazyTarget(Kind kind) {
void X86_64KindHandler::applyFixup(int32_t reloc, uint64_t addend,
uint8_t *location, uint64_t fixupAddress,
uint64_t targetAddress) {
if (reloc == llvm::ELF::R_X86_64_IRELATIVE)
return;
if (_fixupHandler[reloc])
_fixupHandler[reloc](location, fixupAddress, targetAddress, addend);
else {

View File

@ -0,0 +1,3 @@
extern "C" int hey();
int main() { return hey(); }

Binary file not shown.

View File

@ -1,6 +1,27 @@
RUN: lld -core -target x86_64-linux -emit-yaml -output=- %p/Inputs/ifunc.x86-64 \
RUN: | FileCheck %s
RUN: lld -core -target x86_64-linux -emit-yaml -output=- %p/Inputs/ifunc.x86-64 \
RUN: %p/Inputs/ifunc.cpp.x86-64 | FileCheck %s --check-prefix=PLT
CHECK: name: hey
CHECK: scope: global
CHECK: type: resolver
// Get the target that main references.
PLT: name: main
PLT: scope: global
PLT: references
PLT: kind: R_X86_64_PC32
PLT: target: [[PLTNAME:[a-zA-Z0-9-]+]]
// Make sure there's a got entry with a IRELATIVE relocation.
PLT: type: got
PLT: kind: R_X86_64_IRELATIVE
// Make sure the target of main's relocation is a stub with a PC32 relocation.
// This relocation is to the got atom, but you can't really write that check in
// FileCheck.
PLT: name: [[PLTNAME]]
PLT: type: stub
PLT: references
PLT: kind: R_X86_64_PC32