llvm-project/lld/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp

351 lines
10 KiB
C++

//===- lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp -------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "MipsLinkingContext.h"
#include "MipsRelocationPass.h"
#include "Atoms.h"
namespace {
using namespace lld;
using namespace lld::elf;
using namespace llvm::ELF;
// Lazy resolver
const uint8_t mipsGot0AtomContent[] = { 0x00, 0x00, 0x00, 0x00 };
// Module pointer
const uint8_t mipsGotModulePointerAtomContent[] = { 0x00, 0x00, 0x00, 0x80 };
// PLT0 entry
const uint8_t mipsPlt0AtomContent[] = {
0x00, 0x00, 0x1c, 0x3c, // lui $28, %hi(&GOTPLT[0])
0x00, 0x00, 0x99, 0x8f, // lw $25, %lo(&GOTPLT[0])($28)
0x00, 0x00, 0x9c, 0x27, // addiu $28, $28, %lo(&GOTPLT[0])
0x23, 0xc0, 0x1c, 0x03, // subu $24, $24, $28
0x21, 0x78, 0xe0, 0x03, // move $15, $31
0x82, 0xc0, 0x18, 0x00, // srl $24, $24, 2
0x09, 0xf8, 0x20, 0x03, // jalr $25
0xfe, 0xff, 0x18, 0x27 // subu $24, $24, 2
};
// Regular PLT entry
const uint8_t mipsPltAAtomContent[] = {
0x00, 0x00, 0x0f, 0x3c, // lui $15, %hi(.got.plt entry)
0x00, 0x00, 0xf9, 0x8d, // l[wd] $25, %lo(.got.plt entry)($15)
0x08, 0x00, 0x20, 0x03, // jr $25
0x00, 0x00, 0xf8, 0x25 // addiu $24, $15, %lo(.got.plt entry)
};
/// \brief Abstract base class represent MIPS GOT entries.
class MipsGOTAtom : public GOTAtom {
public:
MipsGOTAtom(const File &f) : GOTAtom(f, ".got") {}
virtual Alignment alignment() const { return Alignment(2); }
};
/// \brief MIPS GOT entry initialized by zero.
class GOT0Atom : public MipsGOTAtom {
public:
GOT0Atom(const File &f) : MipsGOTAtom(f) {}
virtual ArrayRef<uint8_t> rawContent() const {
return llvm::makeArrayRef(mipsGot0AtomContent);
}
};
/// \brief MIPS GOT entry initialized by zero.
class GOTModulePointerAtom : public MipsGOTAtom {
public:
GOTModulePointerAtom(const File &f) : MipsGOTAtom(f) {}
virtual ArrayRef<uint8_t> rawContent() const {
return llvm::makeArrayRef(mipsGotModulePointerAtomContent);
}
};
class PLT0Atom : public PLTAtom {
public:
PLT0Atom(const File &f) : PLTAtom(f, ".plt") {}
virtual ArrayRef<uint8_t> rawContent() const {
return llvm::makeArrayRef(mipsPlt0AtomContent);
}
};
class PLTAAtom : public PLTAtom {
public:
PLTAAtom(const File &f) : PLTAtom(f, ".plt") {}
virtual ArrayRef<uint8_t> rawContent() const {
return llvm::makeArrayRef(mipsPltAAtomContent);
}
};
/// \brief MIPS GOT PLT entry
class GOTPLTAtom : public GOTAtom {
public:
GOTPLTAtom(const File &f) : GOTAtom(f, ".got.plt") {}
virtual Alignment alignment() const { return Alignment(2); }
virtual ArrayRef<uint8_t> rawContent() const {
return llvm::makeArrayRef(mipsGot0AtomContent);
}
};
class RelocationPassFile : public SimpleFile {
public:
RelocationPassFile(const ELFLinkingContext &ctx)
: SimpleFile("RelocationPassFile") {
setOrdinal(ctx.getNextOrdinalAndIncrement());
}
llvm::BumpPtrAllocator _alloc;
};
class RelocationPass : public Pass {
public:
RelocationPass(MipsLinkingContext &context)
: _context(context), _file(context) {
_localGotVector.push_back(new (_file._alloc) GOT0Atom(_file));
_localGotVector.push_back(new (_file._alloc) GOTModulePointerAtom(_file));
}
virtual void perform(std::unique_ptr<MutableFile> &mf) {
// Process all references.
for (const auto &atom : mf->defined())
for (const auto &ref : *atom)
handleReference(*atom, *ref);
uint64_t ordinal = 0;
for (auto &got : _localGotVector) {
DEBUG_WITH_TYPE("MipsGOT", llvm::dbgs() << "[ GOT ] Adding L "
<< got->name() << "\n");
got->setOrdinal(ordinal++);
mf->addAtom(*got);
}
for (auto &got : _globalGotVector) {
DEBUG_WITH_TYPE("MipsGOT", llvm::dbgs() << "[ GOT ] Adding G "
<< got->name() << "\n");
got->setOrdinal(ordinal++);
mf->addAtom(*got);
}
for (auto &plt : _pltVector) {
DEBUG_WITH_TYPE("MipsGOT", llvm::dbgs() << "[ PLT ] Adding "
<< plt->name() << "\n");
plt->setOrdinal(ordinal++);
mf->addAtom(*plt);
}
for (auto &gotplt : _gotpltVector) {
DEBUG_WITH_TYPE("MipsGOT", llvm::dbgs() << "[ GOTPLT ] Adding "
<< gotplt->name() << "\n");
gotplt->setOrdinal(ordinal++);
mf->addAtom(*gotplt);
}
}
private:
/// \brief Reference to the linking context.
const MipsLinkingContext &_context;
/// \brief Owner of all the Atoms created by this pass.
RelocationPassFile _file;
/// \brief Map Atoms to their GOT entries.
llvm::DenseMap<const Atom *, GOTAtom *> _gotMap;
/// \brief the list of local GOT atoms.
std::vector<GOTAtom *> _localGotVector;
/// \brief the list of global GOT atoms.
std::vector<GOTAtom *> _globalGotVector;
/// \brief Map Atoms to their PLT entries.
llvm::DenseMap<const Atom *, PLTAtom *> _pltMap;
/// \brief the list of PLT atoms.
std::vector<PLTAtom *> _pltVector;
/// \brief the list of GOTPLT atoms.
std::vector<GOTAtom *> _gotpltVector;
/// \brief Handle a specific reference.
void handleReference(const DefinedAtom &atom, const Reference &ref) {
if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF)
return;
assert(ref.kindArch() == Reference::KindArch::Mips);
switch (ref.kindValue()) {
case R_MIPS_26:
handlePLT(ref);
break;
case R_MIPS_GOT16:
case R_MIPS_CALL16:
handleGOT(ref);
break;
}
}
bool isLocal(const Atom *a) {
if (auto *da = dyn_cast<DefinedAtom>(a))
return da->scope() == Atom::scopeTranslationUnit;
return false;
}
void handlePLT(const Reference &ref) {
if (ref.kindValue() == R_MIPS_26 && !isLocal(ref.target()))
const_cast<Reference &>(ref).setKindValue(LLD_R_MIPS_GLOBAL_26);
if (isa<SharedLibraryAtom>(ref.target()))
const_cast<Reference &>(ref).setTarget(getPLTEntry(ref.target()));
}
void handleGOT(const Reference &ref) {
if (ref.kindValue() == R_MIPS_GOT16 && !isLocal(ref.target()))
const_cast<Reference &>(ref).setKindValue(LLD_R_MIPS_GLOBAL_GOT16);
const_cast<Reference &>(ref).setTarget(getGOTEntry(ref.target()));
}
bool requireLocalGOT(const Atom *a) {
Atom::Scope scope;
if (auto *da = dyn_cast<DefinedAtom>(a))
scope = da->scope();
else if (auto *aa = dyn_cast<AbsoluteAtom>(a))
scope = aa->scope();
else
return false;
// Local and hidden symbols must be local.
if (scope == Atom::scopeTranslationUnit ||
scope == Atom::scopeLinkageUnit)
return true;
// External symbol defined in an executable file requires a local GOT entry.
if (_context.getOutputELFType() == llvm::ELF::ET_EXEC)
return true;
return false;
}
const GOTAtom *getGOTEntry(const Atom *a) {
auto got = _gotMap.find(a);
if (got != _gotMap.end())
return got->second;
auto ga = new (_file._alloc) GOT0Atom(_file);
_gotMap[a] = ga;
bool localGOT = requireLocalGOT(a);
if (localGOT)
_localGotVector.push_back(ga);
else {
_globalGotVector.push_back(ga);
ga->addReferenceELF_Mips(LLD_R_MIPS_GLOBAL_GOT, 0, a, 0);
}
if (const DefinedAtom *da = dyn_cast<DefinedAtom>(a))
ga->addReferenceELF_Mips(R_MIPS_32, 0, da, 0);
DEBUG_WITH_TYPE("MipsGOT", {
ga->_name = "__got_";
ga->_name += a->name();
llvm::dbgs() << "[ GOT ] Create " << (localGOT ? "L " : "G ") << a->name()
<< "\n";
});
return ga;
}
void createPLTHeader() {
assert(_pltVector.empty() && _gotpltVector.empty());
auto pa = new (_file._alloc) PLT0Atom(_file);
_pltVector.push_back(pa);
auto ga0 = new (_file._alloc) GOTPLTAtom(_file);
_gotpltVector.push_back(ga0);
auto ga1 = new (_file._alloc) GOTPLTAtom(_file);
_gotpltVector.push_back(ga1);
// Setup reference to fixup the PLT0 entry.
pa->addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, ga0, 0);
pa->addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, ga0, 0);
pa->addReferenceELF_Mips(LLD_R_MIPS_LO16, 8, ga0, 0);
DEBUG_WITH_TYPE("MipsGOT", {
pa->_name = "__plt0";
llvm::dbgs() << "[ PLT ] Create PLT0\n";
ga0->_name = "__gotplt0";
llvm::dbgs() << "[ GOTPLT ] Create GOTPLT0\n";
ga1->_name = "__gotplt1";
llvm::dbgs() << "[ GOTPLT ] Create GOTPLT1\n";
});
}
const PLTAtom *getPLTEntry(const Atom *a) {
auto plt = _pltMap.find(a);
if (plt != _pltMap.end())
return plt->second;
if (_pltVector.empty())
createPLTHeader();
auto pa = new (_file._alloc) PLTAAtom(_file);
_pltMap[a] = pa;
_pltVector.push_back(pa);
auto ga = new (_file._alloc) GOTPLTAtom(_file);
_gotpltVector.push_back(ga);
// Setup reference to fixup the PLT entry.
pa->addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, ga, 0);
pa->addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, ga, 0);
pa->addReferenceELF_Mips(LLD_R_MIPS_LO16, 12, ga, 0);
// Setup reference to assign initial value to the .got.plt entry.
ga->addReferenceELF_Mips(R_MIPS_32, 0, _pltVector.front(), 0);
// Create dynamic relocation to adjust the .got.plt entry at runtime.
ga->addReferenceELF_Mips(R_MIPS_JUMP_SLOT, 0, a, 0);
DEBUG_WITH_TYPE("MipsGOT", {
pa->_name = "__plt_";
pa->_name += a->name();
llvm::dbgs() << "[ PLT ] Create " << a->name() << "\n";
ga->_name = "__got_plt_";
ga->_name += a->name();
llvm::dbgs() << "[ GOTPLT ] Create " << a->name() << "\n";
});
return pa;
}
};
} // end anon namespace
std::unique_ptr<Pass>
lld::elf::createMipsRelocationPass(MipsLinkingContext &ctx) {
switch (ctx.getOutputELFType()) {
case llvm::ELF::ET_EXEC:
case llvm::ELF::ET_DYN:
return std::unique_ptr<Pass>(new RelocationPass(ctx));
case llvm::ELF::ET_REL:
return std::unique_ptr<Pass>();
default:
llvm_unreachable("Unhandled output file type");
}
}