diff options
author | Simon Atanasyan <simon@atanasyan.com> | 2013-12-15 12:57:28 +0000 |
---|---|---|
committer | Simon Atanasyan <simon@atanasyan.com> | 2013-12-15 12:57:28 +0000 |
commit | 9931f95bac43c621d0343f9ef8777a60f5030b37 (patch) | |
tree | 50d16c2551e41d2305d352b6519f889522357b08 /lld/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp | |
parent | 7631b0eba40b0996cfd0b7fa614fbde0bcdb872a (diff) | |
download | bcm5719-llvm-9931f95bac43c621d0343f9ef8777a60f5030b37.tar.gz bcm5719-llvm-9931f95bac43c621d0343f9ef8777a60f5030b37.zip |
Linking of shared libraries for MIPS little-endian 32-bit target.
The following are the most significant peculiarities of MIPS target:
- MIPS ABI requires some special tags in the dynamic table.
- GOT consists of two parts local and global. The local part contains
entries refer locally visible symbols. The global part contains entries
refer global symbols.
- Entries in the .dynsym section which have corresponded entries in the
GOT should be:
* Emitted at the end of .dynsym section
* Sorted accordingly to theirs GOT counterparts
- There are "paired" relocations. One or more R_MIPS_HI16 and R_MIPS_GOT16
relocations should be followed by R_MIPS_LO16 relocation. To calculate
result of R_MIPS_HI16 and R_MIPS_GOT16 relocations we need to combine
addends from these relocations and paired R_MIPS_LO16 relocation.
The patch reviewed by Michael Spencer, Shankar Easwaran, Rui Ueyama.
http://llvm-reviews.chandlerc.com/D2156
llvm-svn: 197342
Diffstat (limited to 'lld/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp')
-rw-r--r-- | lld/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/lld/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp b/lld/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp new file mode 100644 index 00000000000..24718a3e3d4 --- /dev/null +++ b/lld/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp @@ -0,0 +1,230 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp -------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "MipsLinkingContext.h" +#include "MipsTargetHandler.h" + +#include "llvm/ADT/StringSwitch.h" + +using namespace lld; +using namespace lld::elf; + +namespace { + +// Lazy resolver +const uint8_t mipsGot0AtomContent[] = { 0x00, 0x00, 0x00, 0x00 }; + +// Module pointer +const uint8_t mipsGotModulePointerAtomContent[] = { 0x00, 0x00, 0x00, 0x80 }; + +/// \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 MipsGOT0Atom : public MipsGOTAtom { +public: + MipsGOT0Atom(const File &f) : MipsGOTAtom(f) {} + + virtual ArrayRef<uint8_t> rawContent() const { + return llvm::makeArrayRef(mipsGot0AtomContent); + } +}; + +/// \brief MIPS GOT entry initialized by zero. +class MipsGOTModulePointerAtom : public MipsGOTAtom { +public: + MipsGOTModulePointerAtom(const File &f) : MipsGOTAtom(f) {} + + virtual ArrayRef<uint8_t> rawContent() const { + return llvm::makeArrayRef(mipsGotModulePointerAtomContent); + } +}; + +class MipsGOTPassFile : public SimpleFile { +public: + MipsGOTPassFile(const ELFLinkingContext &ctx) + : SimpleFile(ctx, "MipsGOTPassFile") { + setOrdinal(ctx.getNextOrdinalAndIncrement()); + } + + llvm::BumpPtrAllocator _alloc; +}; + +class MipsGOTPass : public Pass { +public: + MipsGOTPass(MipsLinkingContext &context) + : _file(context), _got0(new (_file._alloc) MipsGOT0Atom(_file)), + _got1(new (_file._alloc) MipsGOTModulePointerAtom(_file)) { + _localGotVector.push_back(_got0); + _localGotVector.push_back(_got1); + } + + 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); + } + } + +private: + /// \brief Owner of all the Atoms created by this pass. + MipsGOTPassFile _file; + + /// \brief GOT header entries. + GOTAtom *_got0; + GOTAtom *_got1; + + /// \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 Handle a specific reference. + void handleReference(const DefinedAtom &atom, const Reference &ref) { + switch (ref.kind()) { + case R_MIPS_GOT16: + case R_MIPS_CALL16: + handleGOT(ref); + break; + } + } + + void handleGOT(const Reference &ref) { + const_cast<Reference &>(ref).setTarget(getEntry(ref.target())); + } + + const GOTAtom *getEntry(const Atom *a) { + auto got = _gotMap.find(a); + if (got != _gotMap.end()) + return got->second; + + const DefinedAtom *da = dyn_cast<DefinedAtom>(a); + bool isLocal = (da && da->scope() == Atom::scopeTranslationUnit); + + auto ga = new (_file._alloc) MipsGOT0Atom(_file); + _gotMap[a] = ga; + if (isLocal) + _localGotVector.push_back(ga); + else { + if (da) + ga->addReference(R_MIPS_32, 0, a, 0); + else + ga->addReference(R_MIPS_NONE, 0, a, 0); + _globalGotVector.push_back(ga); + } + + DEBUG_WITH_TYPE("MipsGOT", { + ga->_name = "__got_"; + ga->_name += a->name(); + llvm::dbgs() << "[ GOT ] Create " << (isLocal ? "L " : "G ") << a->name() + << "\n"; + }); + + return ga; + } +}; + +} // end anon namespace + +MipsLinkingContext::MipsLinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new MipsTargetHandler(*this))) {} + +MipsTargetLayout<Mips32ElELFType> &MipsLinkingContext::getTargetLayout() { + auto &layout = getTargetHandler<Mips32ElELFType>().targetLayout(); + return static_cast<MipsTargetLayout<Mips32ElELFType> &>(layout); +} + +const MipsTargetLayout<Mips32ElELFType> & +MipsLinkingContext::getTargetLayout() const { + auto &layout = getTargetHandler<Mips32ElELFType>().targetLayout(); + return static_cast<MipsTargetLayout<Mips32ElELFType> &>(layout); +} + +bool MipsLinkingContext::isLittleEndian() const { + return Mips32ElELFType::TargetEndianness == llvm::support::little; +} + +#undef LLD_CASE +#define LLD_CASE(name) .Case(#name, llvm::ELF::name) + +ErrorOr<Reference::Kind> +MipsLinkingContext::relocKindFromString(StringRef str) const { + int32_t ret = llvm::StringSwitch<int32_t>(str) + LLD_CASE(R_MIPS_NONE) + LLD_CASE(R_MIPS_32) + LLD_CASE(R_MIPS_HI16) + LLD_CASE(R_MIPS_LO16) + LLD_CASE(R_MIPS_GOT16) + LLD_CASE(R_MIPS_CALL16) + LLD_CASE(R_MIPS_JALR) + .Default(-1); + + if (ret == -1) + return make_error_code(YamlReaderError::illegal_value); + return ret; +} + +#undef LLD_CASE +#define LLD_CASE(name) \ + case llvm::ELF::name: \ + return std::string(#name); + +ErrorOr<std::string> +MipsLinkingContext::stringFromRelocKind(Reference::Kind kind) const { + switch (kind) { + LLD_CASE(R_MIPS_NONE) + LLD_CASE(R_MIPS_32) + LLD_CASE(R_MIPS_HI16) + LLD_CASE(R_MIPS_LO16) + LLD_CASE(R_MIPS_GOT16) + LLD_CASE(R_MIPS_CALL16) + LLD_CASE(R_MIPS_JALR) + } + + return make_error_code(YamlReaderError::illegal_value); +} + +void MipsLinkingContext::addPasses(PassManager &pm) { + switch (getOutputELFType()) { + case llvm::ELF::ET_DYN: + pm.add(std::unique_ptr<Pass>(new MipsGOTPass(*this))); + break; + default: + llvm_unreachable("Unhandled output file type"); + } + + ELFLinkingContext::addPasses(pm); +} |