//===- 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 "MipsELFFile.h" #include "MipsLinkingContext.h" #include "MipsRelocationPass.h" #include "MipsTargetHandler.h" #include "llvm/ADT/DenseSet.h" using namespace lld; using namespace lld::elf; using namespace llvm::ELF; // Lazy resolver static const uint8_t mipsGot0AtomContent[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Module pointer static const uint8_t mipsGotModulePointerAtomContent[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 }; // TLS GD Entry static const uint8_t mipsGotTlsGdAtomContent[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Regular PLT0 entry static 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 }; // microMIPS PLT0 entry static const uint8_t micromipsPlt0AtomContent[] = { 0x80, 0x79, 0x00, 0x00, // addiupc $3, (&GOTPLT[0]) - . 0x23, 0xff, 0x00, 0x00, // lw $25, 0($3) 0x35, 0x05, // subu $2, $2, $3 0x25, 0x25, // srl $2, $2, 2 0x02, 0x33, 0xfe, 0xff, // subu $24, $2, 2 0xff, 0x0d, // move $15, $31 0xf9, 0x45, // jalrs $25 0x83, 0x0f, // move $28, $3 0x00, 0x0c // nop }; // Regular PLT entry static 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) }; // microMIPS PLT entry static const uint8_t micromipsPltAtomContent[] = { 0x00, 0x79, 0x00, 0x00, // addiupc $2, (.got.plt entry) - . 0x22, 0xff, 0x00, 0x00, // lw $25, 0($2) 0x99, 0x45, // jr $25 0x02, 0x0f // move $24, $2 }; // R6 PLT entry static const uint8_t mipsR6PltAAtomContent[] = { 0x00, 0x00, 0x0f, 0x3c, // lui $15, %hi(.got.plt entry) 0x00, 0x00, 0xf9, 0x8d, // l[wd] $25, %lo(.got.plt entry)($15) 0x09, 0x00, 0x20, 0x03, // jr $25 0x00, 0x00, 0xf8, 0x25 // addiu $24, $15, %lo(.got.plt entry) }; // LA25 stub entry static const uint8_t mipsLA25AtomContent[] = { 0x00, 0x00, 0x19, 0x3c, // lui $25, %hi(func) 0x00, 0x00, 0x00, 0x08, // j func 0x00, 0x00, 0x39, 0x27, // addiu $25, $25, %lo(func) 0x00, 0x00, 0x00, 0x00 // nop }; // microMIPS LA25 stub entry static const uint8_t micromipsLA25AtomContent[] = { 0xb9, 0x41, 0x00, 0x00, // lui $25, %hi(func) 0x00, 0xd4, 0x00, 0x00, // j func 0x39, 0x33, 0x00, 0x00, // addiu $25, $25, %lo(func) 0x00, 0x00, 0x00, 0x00 // nop }; namespace { /// \brief Abstract base class represent MIPS GOT entries. class MipsGOTAtom : public GOTAtom { public: MipsGOTAtom(const File &f) : GOTAtom(f, ".got") {} Alignment alignment() const override { return Alignment(2); } }; /// \brief MIPS GOT entry initialized by zero. template class GOT0Atom : public MipsGOTAtom { public: GOT0Atom(const File &f) : MipsGOTAtom(f) {} ArrayRef rawContent() const override; }; template <> ArrayRef GOT0Atom::rawContent() const { return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); } template <> ArrayRef GOT0Atom::rawContent() const { return llvm::makeArrayRef(mipsGot0AtomContent); } /// \brief MIPS GOT entry initialized by zero. template class GOTModulePointerAtom : public MipsGOTAtom { public: GOTModulePointerAtom(const File &f) : MipsGOTAtom(f) {} ArrayRef rawContent() const override; }; template <> ArrayRef GOTModulePointerAtom::rawContent() const { return llvm::makeArrayRef(mipsGotModulePointerAtomContent).slice(4); } template <> ArrayRef GOTModulePointerAtom::rawContent() const { return llvm::makeArrayRef(mipsGotModulePointerAtomContent); } /// \brief MIPS GOT TLS GD entry. template class GOTTLSGdAtom : public MipsGOTAtom { public: GOTTLSGdAtom(const File &f) : MipsGOTAtom(f) {} ArrayRef rawContent() const override; }; template <> ArrayRef GOTTLSGdAtom::rawContent() const { return llvm::makeArrayRef(mipsGotTlsGdAtomContent).slice(8); } template <> ArrayRef GOTTLSGdAtom::rawContent() const { return llvm::makeArrayRef(mipsGotTlsGdAtomContent); } class GOTPLTAtom : public GOTAtom { public: GOTPLTAtom(const File &f) : GOTAtom(f, ".got.plt") {} GOTPLTAtom(const Atom *a, const File &f) : GOTAtom(f, ".got.plt") { // Create dynamic relocation to adjust the .got.plt entry at runtime. addReferenceELF_Mips(R_MIPS_JUMP_SLOT, 0, a, 0); } /// Setup reference to assign initial value to the .got.plt entry. void setPLT0(const PLTAtom *plt0) { addReferenceELF_Mips(R_MIPS_32, 0, plt0, 0); } Alignment alignment() const override { return Alignment(2); } ArrayRef rawContent() const override { return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); } }; class PLT0Atom : public PLTAtom { public: PLT0Atom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the PLT0 entry. addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, got, 0); addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, got, 0); addReferenceELF_Mips(LLD_R_MIPS_LO16, 8, got, 0); } ArrayRef rawContent() const override { return llvm::makeArrayRef(mipsPlt0AtomContent); } }; class PLT0MicroAtom : public PLTAtom { public: PLT0MicroAtom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the PLT0 entry. addReferenceELF_Mips(R_MICROMIPS_PC23_S2, 0, got, 0); } CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef rawContent() const override { return llvm::makeArrayRef(micromipsPlt0AtomContent); } }; class PLTAAtom : public PLTAtom { public: PLTAAtom(const GOTPLTAtom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the PLT entry. addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, got, 0); addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, got, 0); addReferenceELF_Mips(LLD_R_MIPS_LO16, 12, got, 0); } ArrayRef rawContent() const override { return llvm::makeArrayRef(mipsPltAAtomContent); } }; class PLTR6Atom : public PLTAAtom { public: PLTR6Atom(const GOTPLTAtom *got, const File &f) : PLTAAtom(got, f) {} ArrayRef rawContent() const override { return llvm::makeArrayRef(mipsR6PltAAtomContent); } }; class PLTMicroAtom : public PLTAtom { public: PLTMicroAtom(const GOTPLTAtom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the microMIPS PLT entry. addReferenceELF_Mips(R_MICROMIPS_PC23_S2, 0, got, 0); } Alignment alignment() const override { return Alignment(1); } CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef rawContent() const override { return llvm::makeArrayRef(micromipsPltAtomContent); } }; class LA25Atom : public PLTAtom { public: LA25Atom(const File &f) : PLTAtom(f, ".text") {} }; class LA25RegAtom : public LA25Atom { public: LA25RegAtom(const Atom *a, const File &f) : LA25Atom(f) { // Setup reference to fixup the LA25 stub entry. addReferenceELF_Mips(R_MIPS_HI16, 0, a, 0); addReferenceELF_Mips(R_MIPS_26, 4, a, 0); addReferenceELF_Mips(R_MIPS_LO16, 8, a, 0); } ArrayRef rawContent() const override { return llvm::makeArrayRef(mipsLA25AtomContent); } }; class LA25MicroAtom : public LA25Atom { public: LA25MicroAtom(const Atom *a, const File &f) : LA25Atom(f) { // Setup reference to fixup the microMIPS LA25 stub entry. addReferenceELF_Mips(R_MICROMIPS_HI16, 0, a, 0); addReferenceELF_Mips(R_MICROMIPS_26_S1, 4, a, 0); addReferenceELF_Mips(R_MICROMIPS_LO16, 8, a, 0); } CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef rawContent() const override { return llvm::makeArrayRef(micromipsLA25AtomContent); } }; class RelocationPassFile : public SimpleFile { public: RelocationPassFile(const ELFLinkingContext &ctx) : SimpleFile("RelocationPassFile") { setOrdinal(ctx.getNextOrdinalAndIncrement()); } llvm::BumpPtrAllocator _alloc; }; template class RelocationPass : public Pass { public: RelocationPass(MipsLinkingContext &ctx); void perform(std::unique_ptr &mf) override; private: /// \brief Reference to the linking context. const MipsLinkingContext &_ctx; /// \brief Owner of all the Atoms created by this pass. RelocationPassFile _file; /// \brief Map Atoms and addend to local GOT entries. typedef std::pair LocalGotMapKeyT; llvm::DenseMap _gotLocalMap; llvm::DenseMap _gotLocalPageMap; /// \brief Map Atoms to global GOT entries. llvm::DenseMap _gotGlobalMap; /// \brief Map Atoms to TLS GOT entries. llvm::DenseMap _gotTLSMap; /// \brief Map Atoms to TLS GD GOT entries. llvm::DenseMap _gotTLSGdMap; /// \brief GOT entry for the R_xxxMIPS_TLS_LDM relocations. GOTTLSGdAtom *_gotLDMEntry; /// \brief the list of local GOT atoms. std::vector _localGotVector; /// \brief the list of global GOT atoms. std::vector _globalGotVector; /// \brief the list of TLS GOT atoms. std::vector _tlsGotVector; /// \brief Map Atoms to their GOTPLT entries. llvm::DenseMap _gotpltMap; /// \brief Map Atoms to their PLT entries. llvm::DenseMap _pltRegMap; llvm::DenseMap _pltMicroMap; /// \brief Map Atoms to their Object entries. llvm::DenseMap _objectMap; /// \brief Map Atoms to their LA25 entries. llvm::DenseMap _la25RegMap; llvm::DenseMap _la25MicroMap; /// \brief Atoms referenced by static relocations. llvm::DenseSet _hasStaticRelocations; /// \brief Atoms require pointers equality. llvm::DenseSet _requiresPtrEquality; /// \brief References which are candidates for converting /// to the R_MIPS_REL32 relocation. std::vector _rel32Candidates; /// \brief the list of PLT atoms. std::vector _pltRegVector; std::vector _pltMicroVector; /// \brief the list of GOTPLT atoms. std::vector _gotpltVector; /// \brief the list of Object entries. std::vector _objectVector; /// \brief the list of LA25 entries. std::vector _la25Vector; /// \brief Handle a specific reference. void handleReference(const MipsELFDefinedAtom &atom, Reference &ref); /// \brief Collect information about the reference to use it /// later in the handleReference() routine. void collectReferenceInfo(const MipsELFDefinedAtom &atom, Reference &ref); void handlePlain(const MipsELFDefinedAtom &atom, Reference &ref); void handle26(const MipsELFDefinedAtom &atom, Reference &ref); void handleGOT(Reference &ref); const GOTAtom *getLocalGOTEntry(const Reference &ref); const GOTAtom *getLocalGOTPageEntry(const Reference &ref); const GOTAtom *getGlobalGOTEntry(const Atom *a); const GOTAtom *getTLSGOTEntry(const Atom *a); const GOTAtom *getTLSGdGOTEntry(const Atom *a); const GOTAtom *getTLSLdmGOTEntry(const Atom *a); const GOTPLTAtom *getGOTPLTEntry(const Atom *a); const PLTAtom *getPLTEntry(const Atom *a); const PLTAtom *getPLTRegEntry(const Atom *a); const PLTAtom *getPLTMicroEntry(const Atom *a); const LA25Atom *getLA25Entry(const Atom *target, bool isMicroMips); const LA25Atom *getLA25RegEntry(const Atom *a); const LA25Atom *getLA25MicroEntry(const Atom *a); const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a); PLTAtom *createPLTHeader(bool isMicroMips); bool isLocal(const Atom *a) const; bool isLocalCall(const Atom *a) const; bool isDynamic(const Atom *atom) const; bool requireLA25Stub(const Atom *a) const; bool requirePLTEntry(const Atom *a) const; bool requireCopy(const Atom *a) const; bool mightBeDynamic(const MipsELFDefinedAtom &atom, Reference::KindValue refKind) const; bool hasPLTEntry(const Atom *atom) const; bool isR6Target() const; }; template RelocationPass::RelocationPass(MipsLinkingContext &ctx) : _ctx(ctx), _file(ctx), _gotLDMEntry(nullptr) { _localGotVector.push_back(new (_file._alloc) GOT0Atom(_file)); _localGotVector.push_back(new (_file._alloc) GOTModulePointerAtom(_file)); } template void RelocationPass::perform(std::unique_ptr &mf) { for (const auto &atom : mf->defined()) for (const auto &ref : *atom) collectReferenceInfo(*cast>(atom), const_cast(*ref)); // Process all references. for (const auto &atom : mf->defined()) for (const auto &ref : *atom) handleReference(*cast>(atom), const_cast(*ref)); // Create R_MIPS_REL32 relocations. for (auto *ref : _rel32Candidates) { if (!isDynamic(ref->target()) || hasPLTEntry(ref->target())) continue; ref->setKindValue(R_MIPS_REL32); if (ELFT::Is64Bits) static_cast *>(ref)->setTag(R_MIPS_64); if (!isLocalCall(ref->target())) getGlobalGOTEntry(ref->target()); } uint64_t ordinal = 0; for (auto &got : _localGotVector) { got->setOrdinal(ordinal++); mf->addAtom(*got); } for (auto &got : _globalGotVector) { got->setOrdinal(ordinal++); mf->addAtom(*got); } for (auto &got : _tlsGotVector) { got->setOrdinal(ordinal++); mf->addAtom(*got); } // Create and emit PLT0 entry. PLTAtom *plt0Atom = nullptr; if (!_pltRegVector.empty()) plt0Atom = createPLTHeader(false); else if (!_pltMicroVector.empty()) plt0Atom = createPLTHeader(true); if (plt0Atom) { plt0Atom->setOrdinal(ordinal++); mf->addAtom(*plt0Atom); } // Emit regular PLT entries firts. for (auto &plt : _pltRegVector) { plt->setOrdinal(ordinal++); mf->addAtom(*plt); } // microMIPS PLT entries come after regular ones. for (auto &plt : _pltMicroVector) { plt->setOrdinal(ordinal++); mf->addAtom(*plt); } // Assign PLT0 to GOTPLT entries. assert(_gotpltMap.empty() || plt0Atom); for (auto &a: _gotpltMap) a.second->setPLT0(plt0Atom); for (auto &gotplt : _gotpltVector) { gotplt->setOrdinal(ordinal++); mf->addAtom(*gotplt); } for (auto obj : _objectVector) { obj->setOrdinal(ordinal++); mf->addAtom(*obj); } for (auto la25 : _la25Vector) { la25->setOrdinal(ordinal++); mf->addAtom(*la25); } } template void RelocationPass::handleReference(const MipsELFDefinedAtom &atom, Reference &ref) { if (!ref.target()) return; if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) return; assert(ref.kindArch() == Reference::KindArch::Mips); switch (ref.kindValue()) { case R_MIPS_32: case R_MIPS_PC32: case R_MIPS_HI16: case R_MIPS_LO16: case R_MIPS_PCHI16: case R_MIPS_PCLO16: case R_MICROMIPS_HI16: case R_MICROMIPS_LO16: // FIXME (simon): Handle dynamic/static linking differently. handlePlain(atom, ref); break; case R_MIPS_26: case R_MICROMIPS_26_S1: handle26(atom, ref); break; case R_MIPS_GOT16: case R_MIPS_CALL16: case R_MICROMIPS_GOT16: case R_MICROMIPS_CALL16: case R_MIPS_GOT_DISP: case R_MIPS_GOT_PAGE: handleGOT(ref); break; case R_MIPS_GOT_OFST: // Nothing to do. We create GOT page entry in the R_MIPS_GOT_PAGE handler. break; case R_MIPS_GPREL16: if (isLocal(ref.target())) ref.setAddend(ref.addend() + atom.file().getGP0()); break; case R_MIPS_GPREL32: ref.setAddend(ref.addend() + atom.file().getGP0()); break; case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_DTPREL_LO16: ref.setAddend(ref.addend() - atom.file().getDTPOffset()); break; case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_LO16: ref.setAddend(ref.addend() - atom.file().getTPOffset()); break; case R_MIPS_TLS_GD: case R_MICROMIPS_TLS_GD: ref.setTarget(getTLSGdGOTEntry(ref.target())); break; case R_MIPS_TLS_LDM: case R_MICROMIPS_TLS_LDM: ref.setTarget(getTLSLdmGOTEntry(ref.target())); break; case R_MIPS_TLS_GOTTPREL: case R_MICROMIPS_TLS_GOTTPREL: ref.setTarget(getTLSGOTEntry(ref.target())); break; } } template static bool isConstrainSym(const MipsELFDefinedAtom &atom, Reference::KindValue refKind) { if ((atom.section()->sh_flags & SHF_ALLOC) == 0) return false; switch (refKind) { case R_MIPS_NONE: case R_MIPS_JALR: case R_MICROMIPS_JALR: case R_MIPS_GPREL16: case R_MIPS_GPREL32: return false; default: return true; } } template void RelocationPass::collectReferenceInfo( const MipsELFDefinedAtom &atom, Reference &ref) { if (!ref.target()) return; if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) return; auto refKind = ref.kindValue(); if (!isConstrainSym(atom, refKind)) return; if (mightBeDynamic(atom, refKind)) _rel32Candidates.push_back(&ref); else _hasStaticRelocations.insert(ref.target()); if (refKind != R_MIPS_CALL16 && refKind != R_MICROMIPS_CALL16 && refKind != R_MIPS_26 && refKind != R_MICROMIPS_26_S1) _requiresPtrEquality.insert(ref.target()); } template bool RelocationPass::isLocal(const Atom *a) const { if (auto *da = dyn_cast(a)) return da->scope() == Atom::scopeTranslationUnit; return false; } template static bool isMipsReadonly(const MipsELFDefinedAtom &atom) { auto secFlags = atom.section()->sh_flags; auto secType = atom.section()->sh_type; if ((secFlags & SHF_ALLOC) == 0) return false; if (secType == SHT_NOBITS) return false; if ((secFlags & SHF_WRITE) != 0) return false; return true; } template bool RelocationPass::mightBeDynamic(const MipsELFDefinedAtom &atom, Reference::KindValue refKind) const { if (refKind == R_MIPS_CALL16 || refKind == R_MIPS_GOT16 || refKind == R_MICROMIPS_CALL16 || refKind == R_MICROMIPS_GOT16) return true; if (refKind != R_MIPS_32 && refKind != R_MIPS_64) return false; if ((atom.section()->sh_flags & SHF_ALLOC) == 0) return false; if (_ctx.getOutputELFType() == ET_DYN) return true; if (!isMipsReadonly(atom)) return true; if (atom.file().isPIC()) return true; return false; } template bool RelocationPass::hasPLTEntry(const Atom *atom) const { return _pltRegMap.count(atom) || _pltMicroMap.count(atom); } template bool RelocationPass::isR6Target() const { switch (_ctx.getMergedELFFlags() & EF_MIPS_ARCH) { case EF_MIPS_ARCH_32R6: case EF_MIPS_ARCH_64R6: return true; default: return false; } } template bool RelocationPass::requirePLTEntry(const Atom *a) const { if (!_hasStaticRelocations.count(a)) return false; const auto *sa = dyn_cast>(a); if (sa && sa->type() != SharedLibraryAtom::Type::Code) return false; const auto *da = dyn_cast>(a); if (da && da->contentType() != DefinedAtom::typeCode) return false; if (isLocalCall(a)) return false; return true; } template bool RelocationPass::requireCopy(const Atom *a) const { if (!_hasStaticRelocations.count(a)) return false; const auto *sa = dyn_cast>(a); return sa && sa->type() == SharedLibraryAtom::Type::Data; } template bool RelocationPass::isDynamic(const Atom *atom) const { const auto *da = dyn_cast(atom); if (da && da->dynamicExport() == DefinedAtom::dynamicExportAlways) return true; const auto *sa = dyn_cast(atom); if (sa) return true; if (_ctx.getOutputELFType() == ET_DYN) { if (da && da->scope() != DefinedAtom::scopeTranslationUnit) return true; const auto *ua = dyn_cast(atom); if (ua) return true; } return false; } template static bool isMicroMips(const MipsELFDefinedAtom &atom) { return atom.codeModel() == DefinedAtom::codeMipsMicro || atom.codeModel() == DefinedAtom::codeMipsMicroPIC; } template const LA25Atom *RelocationPass::getLA25Entry(const Atom *target, bool isMicroMips) { return isMicroMips ? getLA25MicroEntry(target) : getLA25RegEntry(target); } template const PLTAtom *RelocationPass::getPLTEntry(const Atom *a) { bool hasMicroCode = _ctx.getMergedELFFlags() & EF_MIPS_MICROMIPS; // If file contains microMIPS code try to reuse compressed PLT entry... if (hasMicroCode) { auto microPLT = _pltMicroMap.find(a); if (microPLT != _pltMicroMap.end()) return microPLT->second; } // ... then try to reuse a regular PLT entry ... auto regPLT = _pltRegMap.find(a); if (regPLT != _pltRegMap.end()) return regPLT->second; // ... and finally prefer to create new compressed PLT entry. return hasMicroCode ? getPLTMicroEntry(a) : getPLTRegEntry(a); } template void RelocationPass::handlePlain(const MipsELFDefinedAtom &atom, Reference &ref) { if (!isDynamic(ref.target())) return; if (requirePLTEntry(ref.target())) ref.setTarget(getPLTEntry(ref.target())); else if (requireCopy(ref.target())) ref.setTarget(getObjectEntry(cast(ref.target()))); } template void RelocationPass::handle26(const MipsELFDefinedAtom &atom, Reference &ref) { bool isMicro = ref.kindValue() == R_MICROMIPS_26_S1; assert((isMicro || ref.kindValue() == R_MIPS_26) && "Unexpected relocation"); const auto *sla = dyn_cast(ref.target()); if (sla && sla->type() == SharedLibraryAtom::Type::Code) ref.setTarget(isMicro ? getPLTMicroEntry(sla) : getPLTRegEntry(sla)); if (requireLA25Stub(ref.target())) ref.setTarget(getLA25Entry(ref.target(), isMicro)); if (!isLocal(ref.target())) { if (isMicro) ref.setKindValue(LLD_R_MICROMIPS_GLOBAL_26_S1); else ref.setKindValue(LLD_R_MIPS_GLOBAL_26); } } template void RelocationPass::handleGOT(Reference &ref) { if (!isLocalCall(ref.target())) { ref.setTarget(getGlobalGOTEntry(ref.target())); return; } if (ref.kindValue() == R_MIPS_GOT_PAGE) ref.setTarget(getLocalGOTPageEntry(ref)); else if (ref.kindValue() == R_MIPS_GOT_DISP) ref.setTarget(getLocalGOTEntry(ref)); else if (isLocal(ref.target())) ref.setTarget(getLocalGOTPageEntry(ref)); else ref.setTarget(getLocalGOTEntry(ref)); } template bool RelocationPass::isLocalCall(const Atom *a) const { Atom::Scope scope; if (auto *da = dyn_cast(a)) scope = da->scope(); else if (auto *aa = dyn_cast(a)) scope = aa->scope(); else return false; // Local and hidden symbols must be local. if (scope == Atom::scopeTranslationUnit || scope == Atom::scopeLinkageUnit) return true; // Calls to external symbols defined in an executable file resolved locally. if (_ctx.getOutputELFType() == ET_EXEC) return true; return false; } template bool RelocationPass::requireLA25Stub(const Atom *a) const { if (isLocal(a)) return false; if (auto *da = dyn_cast(a)) return static_cast *>(da)->file().isPIC(); return false; } template const GOTAtom *RelocationPass::getLocalGOTEntry(const Reference &ref) { const Atom *a = ref.target(); LocalGotMapKeyT key(a, ref.addend()); auto got = _gotLocalMap.find(key); if (got != _gotLocalMap.end()) return got->second; auto ga = new (_file._alloc) GOT0Atom(_file); _gotLocalMap[key] = ga; _localGotVector.push_back(ga); Reference::KindValue relKind = ELFT::Is64Bits ? R_MIPS_64 : R_MIPS_32; ga->addReferenceELF_Mips(relKind, 0, a, 0); return ga; } template const GOTAtom * RelocationPass::getLocalGOTPageEntry(const Reference &ref) { const Atom *a = ref.target(); LocalGotMapKeyT key(a, ref.addend()); auto got = _gotLocalPageMap.find(key); if (got != _gotLocalPageMap.end()) return got->second; auto ga = new (_file._alloc) GOT0Atom(_file); _gotLocalPageMap[key] = ga; _localGotVector.push_back(ga); Reference::KindValue relKind = ELFT::Is64Bits ? LLD_R_MIPS_64_HI16 : LLD_R_MIPS_32_HI16; ga->addReferenceELF_Mips(relKind, 0, a, ref.addend()); return ga; } template const GOTAtom *RelocationPass::getGlobalGOTEntry(const Atom *a) { auto got = _gotGlobalMap.find(a); if (got != _gotGlobalMap.end()) return got->second; auto ga = new (_file._alloc) GOT0Atom(_file); _gotGlobalMap[a] = ga; _globalGotVector.push_back(ga); ga->addReferenceELF_Mips(LLD_R_MIPS_GLOBAL_GOT, 0, a, 0); if (const DefinedAtom *da = dyn_cast(a)) ga->addReferenceELF_Mips(R_MIPS_32, 0, da, 0); return ga; } template const GOTAtom *RelocationPass::getTLSGOTEntry(const Atom *a) { auto got = _gotTLSMap.find(a); if (got != _gotTLSMap.end()) return got->second; auto ga = new (_file._alloc) GOT0Atom(_file); _gotTLSMap[a] = ga; _tlsGotVector.push_back(ga); Reference::KindValue relKind = ELFT::Is64Bits ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32; ga->addReferenceELF_Mips(relKind, 0, a, 0); return ga; } template const GOTAtom *RelocationPass::getTLSGdGOTEntry(const Atom *a) { auto got = _gotTLSGdMap.find(a); if (got != _gotTLSGdMap.end()) return got->second; auto ga = new (_file._alloc) GOTTLSGdAtom(_file); _gotTLSGdMap[a] = ga; _tlsGotVector.push_back(ga); if (ELFT::Is64Bits) { ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD64, 0, a, 0); ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL64, 8, a, 0); } else { ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, a, 0); ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL32, 4, a, 0); } return ga; } template const GOTAtom *RelocationPass::getTLSLdmGOTEntry(const Atom *a) { if (_gotLDMEntry) return _gotLDMEntry; _gotLDMEntry = new (_file._alloc) GOTTLSGdAtom(_file); _tlsGotVector.push_back(_gotLDMEntry); if (ELFT::Is64Bits) _gotLDMEntry->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD64, 0, _gotLDMEntry, 0); else _gotLDMEntry->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, _gotLDMEntry, 0); return _gotLDMEntry; } template PLTAtom *RelocationPass::createPLTHeader(bool isMicroMips) { auto ga1 = new (_file._alloc) GOTPLTAtom(_file); _gotpltVector.insert(_gotpltVector.begin(), ga1); auto ga0 = new (_file._alloc) GOTPLTAtom(_file); _gotpltVector.insert(_gotpltVector.begin(), ga0); if (isMicroMips) return new (_file._alloc) PLT0MicroAtom(ga0, _file); else return new (_file._alloc) PLT0Atom(ga0, _file); } template const GOTPLTAtom *RelocationPass::getGOTPLTEntry(const Atom *a) { auto it = _gotpltMap.find(a); if (it != _gotpltMap.end()) return it->second; auto ga = new (_file._alloc) GOTPLTAtom(a, _file); _gotpltMap[a] = ga; _gotpltVector.push_back(ga); return ga; } template const PLTAtom *RelocationPass::getPLTRegEntry(const Atom *a) { auto plt = _pltRegMap.find(a); if (plt != _pltRegMap.end()) return plt->second; PLTAAtom *pa = isR6Target() ? new (_file._alloc) PLTR6Atom(getGOTPLTEntry(a), _file) : new (_file._alloc) PLTAAtom(getGOTPLTEntry(a), _file); _pltRegMap[a] = pa; _pltRegVector.push_back(pa); // Check that 'a' dynamic symbol table record should point to the PLT. if (_hasStaticRelocations.count(a) && _requiresPtrEquality.count(a)) pa->addReferenceELF_Mips(LLD_R_MIPS_STO_PLT, 0, a, 0); return pa; } template const PLTAtom *RelocationPass::getPLTMicroEntry(const Atom *a) { auto plt = _pltMicroMap.find(a); if (plt != _pltMicroMap.end()) return plt->second; auto pa = new (_file._alloc) PLTMicroAtom(getGOTPLTEntry(a), _file); _pltMicroMap[a] = pa; _pltMicroVector.push_back(pa); // Check that 'a' dynamic symbol table record should point to the PLT. if (_hasStaticRelocations.count(a) && _requiresPtrEquality.count(a)) pa->addReferenceELF_Mips(LLD_R_MIPS_STO_PLT, 0, a, 0); return pa; } template const LA25Atom *RelocationPass::getLA25RegEntry(const Atom *a) { auto la25 = _la25RegMap.find(a); if (la25 != _la25RegMap.end()) return la25->second; auto sa = new (_file._alloc) LA25RegAtom(a, _file); _la25RegMap[a] = sa; _la25Vector.push_back(sa); return sa; } template const LA25Atom *RelocationPass::getLA25MicroEntry(const Atom *a) { auto la25 = _la25MicroMap.find(a); if (la25 != _la25MicroMap.end()) return la25->second; auto sa = new (_file._alloc) LA25MicroAtom(a, _file); _la25MicroMap[a] = sa; _la25Vector.push_back(sa); return sa; } template const ObjectAtom * RelocationPass::getObjectEntry(const SharedLibraryAtom *a) { auto obj = _objectMap.find(a); if (obj != _objectMap.end()) return obj->second; auto oa = new (_file._alloc) ObjectAtom(_file); oa->addReferenceELF_Mips(R_MIPS_COPY, 0, oa, 0); oa->_name = a->name(); oa->_size = a->size(); _objectMap[a] = oa; _objectVector.push_back(oa); return oa; } } // end anon namespace static std::unique_ptr createPass(MipsLinkingContext &ctx) { switch (ctx.getTriple().getArch()) { case llvm::Triple::mipsel: return llvm::make_unique>(ctx); case llvm::Triple::mips64el: return llvm::make_unique>(ctx); default: llvm_unreachable("Unhandled arch"); } } std::unique_ptr lld::elf::createMipsRelocationPass(MipsLinkingContext &ctx) { switch (ctx.getOutputELFType()) { case ET_EXEC: case ET_DYN: return createPass(ctx); case ET_REL: return nullptr; default: llvm_unreachable("Unhandled output file type"); } }