#include <memory>
namespace lld {
+class DefinedAtom;
+class Reference;
namespace elf { template <typename ELFT> class ELFTargetHandler; }
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>
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)) {
ORDER_TEXT = 70,
ORDER_PLT = 80,
ORDER_FINI = 90,
+ ORDER_REL = 95,
ORDER_RODATA = 100,
ORDER_EH_FRAME = 110,
ORDER_EH_FRAMEHDR = 120,
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;
std::vector<MergedSections<ELFT> *> _mergedSections;
ELFHeader<ELFT> *_elfHeader;
ELFProgramHeader<ELFT> *_programHeader;
+ ELFRelocationTable<ELFT> *_relocationTable;
std::vector<AbsoluteAtomPair> _absoluteAtoms;
llvm::BumpPtrAllocator _allocator;
const ELFTargetInfo &_targetInfo;
int32_t contentPermissions)
{
switch (contentType) {
+ case DefinedAtom::typeResolver:
case DefinedAtom::typeCode:
return llvm::StringSwitch<Reference::Kind>(name)
.StartsWith(".eh_frame_hdr", ORDER_EH_FRAMEHDR)
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
case ORDER_HASH:
case ORDER_DYNAMIC_SYMBOLS:
case ORDER_DYNAMIC_STRINGS:
+ case ORDER_REL:
case ORDER_INIT:
case ORDER_PLT:
case ORDER_FINI:
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:
case ORDER_HASH:
case ORDER_DYNAMIC_SYMBOLS:
case ORDER_DYNAMIC_STRINGS:
+ case ORDER_REL:
case ORDER_INIT:
case ORDER_PLT:
case ORDER_TEXT:
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
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));
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:
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;
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;
}
}
+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
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)
_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
/// created
template<class ELFT>
void ELFExecutableWriter<ELFT>::finalizeDefaultAtomValues() {
- auto bssStartAtomIter = _layout->findAbsoluteAtom("__bss_start");
- auto bssEndAtomIter = _layout->findAbsoluteAtom("__bss_end");
- auto underScoreEndAtomIter = _layout->findAbsoluteAtom("_end");
- auto endAtomIter = _layout->findAbsoluteAtom("end");
- auto initArrayStartIter = _layout->findAbsoluteAtom("__init_array_start");
- auto initArrayEndIter = _layout->findAbsoluteAtom("__init_array_end");
-
- auto section = _layout->findOutputSection(".init_array");
- if (section) {
- initArrayStartIter->setValue(section->virtualAddr());
- initArrayEndIter->setValue(section->virtualAddr() +
- section->memSize());
- } else {
- initArrayStartIter->setValue(0);
- initArrayEndIter->setValue(0);
- }
-
- assert(!(bssStartAtomIter == _layout->absoluteAtoms().end() ||
- bssEndAtomIter == _layout->absoluteAtoms().end() ||
- underScoreEndAtomIter == _layout->absoluteAtoms().end() ||
- endAtomIter == _layout->absoluteAtoms().end()) &&
- "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);
-
- 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);
+ auto bssStartAtomIter = _layout->findAbsoluteAtom("__bss_start");
+ auto bssEndAtomIter = _layout->findAbsoluteAtom("__bss_end");
+ auto underScoreEndAtomIter = _layout->findAbsoluteAtom("_end");
+ 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 startEnd = [&](typename DefaultELFLayout<ELFT>::AbsoluteAtomIterT start,
+ typename DefaultELFLayout<ELFT>::AbsoluteAtomIterT end,
+ StringRef sec) -> void {
+ auto section = _layout->findOutputSection(sec);
+ if (section) {
+ start->setValue(section->virtualAddr());
+ end->setValue(section->virtualAddr() + section->memSize());
+ } else {
+ 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() ||
+ underScoreEndAtomIter == _layout->absoluteAtoms().end() ||
+ endAtomIter == _layout->absoluteAtoms().end()) &&
+ "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);
+
+ 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);
}
template<class ELFT>
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;
#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 {
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 {
--- /dev/null
+extern "C" int hey();
+
+int main() { return hey(); }
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