//===- 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 big-endian PLT0 entry static const uint8_t mipsBePlt0AtomContent[] = { 0x3c, 0x1c, 0x00, 0x00, // lui $28, %hi(&GOTPLT[0]) 0x8f, 0x99, 0x00, 0x00, // lw $25, %lo(&GOTPLT[0])($28) 0x27, 0x9c, 0x00, 0x00, // addiu $28, $28, %lo(&GOTPLT[0]) 0x03, 0x1c, 0xc0, 0x23, // subu $24, $24, $28 0x03, 0xe0, 0x78, 0x21, // move $15, $31 0x00, 0x18, 0xc0, 0x82, // srl $24, $24, 2 0x03, 0x20, 0xf8, 0x09, // jalr $25 0x27, 0x18, 0xff, 0xfe // subu $24, $24, 2 }; // Regular little-endian PLT0 entry static const uint8_t mipsLePlt0AtomContent[] = { 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 big-endian PLT0 entry static const uint8_t microMipsBePlt0AtomContent[] = { 0x79, 0x80, 0x00, 0x00, // addiupc $3, (&GOTPLT[0]) - . 0xff, 0x23, 0x00, 0x00, // lw $25, 0($3) 0x05, 0x35, // subu $2, $2, $3 0x25, 0x25, // srl $2, $2, 2 0x33, 0x02, 0xff, 0xfe, // subu $24, $2, 2 0x0d, 0xff, // move $15, $31 0x45, 0xf9, // jalrs $25 0x0f, 0x83, // move $28, $3 0x0c, 0x00 // nop }; // microMIPS little-endian PLT0 entry static const uint8_t microMipsLePlt0AtomContent[] = { 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 big-endian PLT entry static const uint8_t mipsBePltAAtomContent[] = { 0x3c, 0x0f, 0x00, 0x00, // lui $15, %hi(.got.plt entry) 0x8d, 0xf9, 0x00, 0x00, // l[wd] $25, %lo(.got.plt entry)($15) 0x03, 0x20, 0x00, 0x08, // jr $25 0x25, 0xf8, 0x00, 0x00 // addiu $24, $15, %lo(.got.plt entry) }; // Regular little-endian PLT entry static const uint8_t mipsLePltAAtomContent[] = { 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 big-endian PLT entry static const uint8_t microMipsBePltAAtomContent[] = { 0x79, 0x00, 0x00, 0x00, // addiupc $2, (.got.plt entry) - . 0xff, 0x22, 0x00, 0x00, // lw $25, 0($2) 0x45, 0x99, // jr $25 0x0f, 0x02 // move $24, $2 }; // microMIPS little-endian PLT entry static const uint8_t microMipsLePltAAtomContent[] = { 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 big-endian PLT entry static const uint8_t mipsR6BePltAAtomContent[] = { 0x3c, 0x0f, 0x00, 0x00, // lui $15, %hi(.got.plt entry) 0x8d, 0xf9, 0x00, 0x00, // l[wd] $25, %lo(.got.plt entry)($15) 0x03, 0x20, 0x00, 0x09, // jr $25 0x25, 0xf8, 0x00, 0x00 // addiu $24, $15, %lo(.got.plt entry) }; // R6 little-endian PLT entry static const uint8_t mipsR6LePltAAtomContent[] = { 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 big-endian stub entry static const uint8_t mipsBeLA25AtomContent[] = { 0x3c, 0x19, 0x00, 0x00, // lui $25, %hi(func) 0x08, 0x00, 0x00, 0x00, // j func 0x27, 0x39, 0x00, 0x00, // addiu $25, $25, %lo(func) 0x00, 0x00, 0x00, 0x00 // nop }; // LA25 little-endian stub entry static const uint8_t mipsLeLA25AtomContent[] = { 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 big-endian stub entry static const uint8_t microMipsBeLA25AtomContent[] = { 0x41, 0xbe, 0x00, 0x00, // lui $25, %hi(func) 0xd4, 0x00, 0x00, 0x00, // j func 0x33, 0x39, 0x00, 0x00, // addiu $25, $25, %lo(func) 0x00, 0x00, 0x00, 0x00 // nop }; // microMIPS LA25 little-endian stub entry static const uint8_t microMipsLeLA25AtomContent[] = { 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 4; } }; /// \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).slice(4); } template <> ArrayRef GOT0Atom::rawContent() const { return llvm::makeArrayRef(mipsGot0AtomContent); } 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).slice(4); } template <> ArrayRef GOTModulePointerAtom::rawContent() const { return llvm::makeArrayRef(mipsGotModulePointerAtomContent); } 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).slice(8); } template <> ArrayRef GOTTLSGdAtom::rawContent() const { return llvm::makeArrayRef(mipsGotTlsGdAtomContent); } 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 4; } ArrayRef rawContent() const override { return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); } }; template class PLT0Atom : public PLTAtom { public: PLT0Atom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the PLT0 entry. addReferenceELF_Mips(R_MIPS_HI16, 0, got, 0); addReferenceELF_Mips(R_MIPS_LO16, 4, got, 0); addReferenceELF_Mips(R_MIPS_LO16, 8, got, 0); } ArrayRef rawContent() const override { llvm_unreachable("PLT0 is not applicable for this target"); } }; template <> ArrayRef PLT0Atom::rawContent() const { return llvm::makeArrayRef(mipsBePlt0AtomContent); } template <> ArrayRef PLT0Atom::rawContent() const { return llvm::makeArrayRef(mipsLePlt0AtomContent); } template 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 { llvm_unreachable("PLT0 is not applicable for this target"); } }; template <> ArrayRef PLT0MicroAtom::rawContent() const { return llvm::makeArrayRef(microMipsBePlt0AtomContent); } template <> ArrayRef PLT0MicroAtom::rawContent() const { return llvm::makeArrayRef(microMipsLePlt0AtomContent); } class PLTAAtom : public PLTAtom { public: PLTAAtom(const GOTPLTAtom *got, const File &f) : PLTAtom(f, ".plt") { // Setup reference to fixup the PLT entry. addReferenceELF_Mips(R_MIPS_HI16, 0, got, 0); addReferenceELF_Mips(R_MIPS_LO16, 4, got, 0); addReferenceELF_Mips(R_MIPS_LO16, 12, got, 0); } }; template class PLTARegAtom : public PLTAAtom { public: PLTARegAtom(const GOTPLTAtom *got, const File &f) : PLTAAtom(got, f) {} ArrayRef rawContent() const override { llvm_unreachable("PLT is not applicable for this target"); } }; template <> ArrayRef PLTARegAtom::rawContent() const { return llvm::makeArrayRef(mipsBePltAAtomContent); } template <> ArrayRef PLTARegAtom::rawContent() const { return llvm::makeArrayRef(mipsLePltAAtomContent); } template class PLTR6Atom : public PLTAAtom { public: PLTR6Atom(const GOTPLTAtom *got, const File &f) : PLTAAtom(got, f) {} ArrayRef rawContent() const override { llvm_unreachable("PLT is not applicable for this target"); } }; template <> ArrayRef PLTR6Atom::rawContent() const { return llvm::makeArrayRef(mipsR6BePltAAtomContent); } template <> ArrayRef PLTR6Atom::rawContent() const { return llvm::makeArrayRef(mipsR6LePltAAtomContent); } template 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 2; } CodeModel codeModel() const override { return codeMipsMicro; } ArrayRef rawContent() const override { llvm_unreachable("PLT is not applicable for this target"); } }; template <> ArrayRef PLTMicroAtom::rawContent() const { return llvm::makeArrayRef(microMipsBePltAAtomContent); } template <> ArrayRef PLTMicroAtom::rawContent() const { return llvm::makeArrayRef(microMipsLePltAAtomContent); } class LA25Atom : public PLTAtom { public: LA25Atom(const File &f) : PLTAtom(f, ".text") {} }; template 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 { llvm_unreachable("LA25 stubs are not applicable for this target"); } }; template <> ArrayRef LA25RegAtom::rawContent() const { return llvm::makeArrayRef(mipsBeLA25AtomContent); } template <> ArrayRef LA25RegAtom::rawContent() const { return llvm::makeArrayRef(mipsLeLA25AtomContent); } template 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 { llvm_unreachable("LA25 stubs are not applicable for this target"); } }; template <> ArrayRef LA25MicroAtom::rawContent() const { return llvm::makeArrayRef(microMipsBeLA25AtomContent); } template <> ArrayRef LA25MicroAtom::rawContent() const { return llvm::makeArrayRef(microMipsLeLA25AtomContent); } class MipsGlobalOffsetTableAtom : public GlobalOffsetTableAtom { public: MipsGlobalOffsetTableAtom(const File &f) : GlobalOffsetTableAtom(f) {} StringRef customSectionName() const override { return ".got"; } }; 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); std::error_code perform(SimpleFile &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 = nullptr; /// \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. std::error_code collectReferenceInfo(const MipsELFDefinedAtom &atom, Reference &ref); /// \brief Check that the relocation is valid for the current linking mode. std::error_code validateRelocation(const DefinedAtom &atom, const Reference &ref) const; void handlePlain(const MipsELFDefinedAtom &atom, Reference &ref); void handleBranch(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, Reference::Addend addend); const GOTAtom *getTLSGdGOTEntry(const Atom *a, Reference::Addend addend); 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 *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 MipsELFDefinedAtom &atom, const Reference &ref) 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; /// \brief Linked files contain microMIPS code. bool isMicroMips(); /// \brief Linked files contain MIPS R6 code. bool isMipsR6(); }; template RelocationPass::RelocationPass(MipsLinkingContext &ctx) : _ctx(ctx), _file(ctx) { _localGotVector.push_back(new (_file._alloc) GOT0Atom(_file)); _localGotVector.push_back(new (_file._alloc) GOTModulePointerAtom(_file)); } template std::error_code RelocationPass::perform(SimpleFile &mf) { for (const auto &atom : mf.defined()) for (const auto &ref : *atom) { const auto &da = *cast>(atom); if (auto ec = collectReferenceInfo(da, const_cast(*ref))) return ec; } // 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; if (!_localGotVector.empty() || !_globalGotVector.empty() || !_tlsGotVector.empty()) { SimpleDefinedAtom *ga = new (_file._alloc) MipsGlobalOffsetTableAtom(_file); ga->setOrdinal(ordinal++); mf.addAtom(*ga); } 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); } return std::error_code(); } static bool isMicroMipsReloc(Reference::KindValue kind) { return R_MICROMIPS_26_S1 <= kind && kind <= R_MICROMIPS_PC19_S2; } static bool isHiLo16Reloc(Reference::KindValue kind) { return kind == R_MIPS_HI16 || kind == R_MIPS_LO16 || kind == R_MIPS_PCHI16 || kind == R_MIPS_PCLO16 || kind == R_MICROMIPS_HI16 || kind == R_MICROMIPS_LO16 || kind == R_MICROMIPS_HI0_LO16; } static bool isBranchReloc(Reference::KindValue kind) { return kind == R_MIPS_26 || kind == R_MICROMIPS_26_S1 || kind == R_MIPS_PC16 || kind == R_MIPS_PC21_S2 || kind == R_MIPS_PC26_S2 || kind == R_MICROMIPS_PC7_S1 || kind == R_MICROMIPS_PC10_S1 || kind == R_MICROMIPS_PC16_S1 || kind == R_MICROMIPS_PC23_S2; } static bool isGotReloc(Reference::KindValue kind) { return kind == R_MIPS_GOT16 || kind == R_MICROMIPS_GOT16; } static bool isAllGotReloc(Reference::KindValue kind) { return isGotReloc(kind) || kind == R_MIPS_GOT_HI16 || kind == R_MIPS_GOT_LO16 || kind == R_MICROMIPS_GOT_HI16 || kind == R_MICROMIPS_GOT_LO16; } static bool isCallReloc(Reference::KindValue kind) { return kind == R_MIPS_CALL16 || kind == R_MICROMIPS_CALL16; } static bool isAllCallReloc(Reference::KindValue kind) { return isCallReloc(kind) || kind == R_MIPS_CALL_HI16 || kind == R_MIPS_CALL_LO16 || kind == R_MICROMIPS_CALL_HI16 || kind == R_MICROMIPS_CALL_LO16; } static bool isGotDispReloc(Reference::KindValue kind) { return kind == R_MIPS_GOT_DISP || kind == R_MICROMIPS_GOT_DISP; } static bool isGotPageReloc(Reference::KindValue kind) { return kind == R_MIPS_GOT_PAGE || kind == R_MICROMIPS_GOT_PAGE; } static bool isTlsDtpReloc(Reference::KindValue kind) { return kind == R_MIPS_TLS_DTPREL_HI16 || kind == R_MIPS_TLS_DTPREL_LO16 || kind == R_MICROMIPS_TLS_DTPREL_HI16 || kind == R_MICROMIPS_TLS_DTPREL_LO16; } static bool isTlsTpReloc(Reference::KindValue kind) { return kind == R_MIPS_TLS_TPREL_HI16 || kind == R_MIPS_TLS_TPREL_LO16 || kind == R_MICROMIPS_TLS_TPREL_HI16 || kind == R_MICROMIPS_TLS_TPREL_LO16; } static bool isTlsGdReloc(Reference::KindValue kind) { return kind == R_MIPS_TLS_GD || kind == R_MICROMIPS_TLS_GD; } static bool isTlsLdmReloc(Reference::KindValue kind) { return kind == R_MIPS_TLS_LDM || kind == R_MICROMIPS_TLS_LDM; } static bool isTlsGotTpReloc(Reference::KindValue kind) { return kind == R_MIPS_TLS_GOTTPREL || kind == R_MICROMIPS_TLS_GOTTPREL; } static bool isGpRelReloc(Reference::KindValue kind) { return kind == R_MIPS_GPREL32 || kind == R_MIPS_GPREL16 || kind == R_MICROMIPS_GPREL16 || kind == R_MICROMIPS_GPREL7_S2 || kind == R_MIPS_LITERAL || kind == R_MICROMIPS_LITERAL; } template void RelocationPass::handleReference(const MipsELFDefinedAtom &atom, Reference &ref) { if (!ref.target()) return; if (ref.kindNamespace() != Reference::KindNamespace::ELF) return; assert(ref.kindArch() == Reference::KindArch::Mips); Reference::KindValue kind = ref.kindValue(); if (isHiLo16Reloc(kind) || kind == R_MIPS_32 || kind == R_MIPS_PC32) handlePlain(atom, ref); else if (isBranchReloc(kind)) handleBranch(atom, ref); else if (isAllGotReloc(kind) || isAllCallReloc(kind) || isGotDispReloc(kind) || isGotPageReloc(kind) || kind == R_MIPS_EH) handleGOT(ref); else if (isTlsDtpReloc(kind)) ref.setAddend(ref.addend() - atom.file().getDTPOffset()); else if (isTlsTpReloc(kind)) ref.setAddend(ref.addend() - atom.file().getTPOffset()); else if (isTlsGdReloc(kind)) ref.setTarget(getTLSGdGOTEntry(ref.target(), ref.addend())); else if (isTlsLdmReloc(kind)) ref.setTarget(getTLSLdmGOTEntry(ref.target())); else if (isTlsGotTpReloc(kind)) ref.setTarget(getTLSGOTEntry(ref.target(), ref.addend())); else if (kind == R_MIPS_GPREL32 || (isLocal(ref.target()) && isGpRelReloc(kind))) ref.setAddend(ref.addend() + atom.file().getGP0()); } 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: case R_MICROMIPS_GPREL16: case R_MICROMIPS_GPREL7_S2: case R_MIPS_LITERAL: case R_MICROMIPS_LITERAL: return false; default: return true; } } template std::error_code RelocationPass::collectReferenceInfo(const MipsELFDefinedAtom &atom, Reference &ref) { if (!ref.target()) return std::error_code(); if (ref.kindNamespace() != Reference::KindNamespace::ELF) return std::error_code(); auto refKind = ref.kindValue(); if (refKind == R_MIPS_EH && this->_ctx.mipsPcRelEhRel()) ref.setKindValue(R_MIPS_PC32); if (auto ec = validateRelocation(atom, ref)) return ec; if (!isConstrainSym(atom, refKind)) return std::error_code(); if (mightBeDynamic(atom, refKind)) _rel32Candidates.push_back(&ref); else _hasStaticRelocations.insert(ref.target()); if (!isBranchReloc(refKind) && !isAllCallReloc(refKind) && refKind != R_MIPS_EH) _requiresPtrEquality.insert(ref.target()); return std::error_code(); } static std::error_code make_reject_for_shared_lib_reloc_error(const ELFLinkingContext &ctx, const DefinedAtom &atom, const Reference &ref) { StringRef kindValStr = "unknown"; ctx.registry().referenceKindToString(ref.kindNamespace(), ref.kindArch(), ref.kindValue(), kindValStr); return make_dynamic_error_code(Twine(kindValStr) + " (" + Twine(ref.kindValue()) + ") relocation cannot be used " "when making a shared object, recompile " + atom.file().path() + " with -fPIC"); } static std::error_code make_external_gprel32_reloc_error(const ELFLinkingContext &ctx, const DefinedAtom &atom, const Reference &ref) { return make_dynamic_error_code( "R_MIPS_GPREL32 (12) relocation cannot be used " "against external symbol " + ref.target()->name() + " in file " + atom.file().path()); } static std::error_code make_local_call16_reloc_error(const ELFLinkingContext &ctx, const DefinedAtom &atom, const Reference &ref) { return make_dynamic_error_code("R_MIPS_CALL16 (11) relocation cannot be used " "against local symbol " + ref.target()->name() + " in file " + atom.file().path()); } template std::error_code RelocationPass::validateRelocation(const DefinedAtom &atom, const Reference &ref) const { if (!ref.target()) return std::error_code(); if (ref.kindValue() == R_MIPS_GPREL32 && !isLocal(ref.target())) return make_external_gprel32_reloc_error(this->_ctx, atom, ref); if (isCallReloc(ref.kindValue()) && isLocal(ref.target())) return make_local_call16_reloc_error(this->_ctx, atom, ref); if (this->_ctx.getOutputELFType() != ET_DYN) return std::error_code(); switch (ref.kindValue()) { case R_MIPS16_HI16: case R_MIPS_HI16: case R_MIPS_HIGHER: case R_MIPS_HIGHEST: case R_MICROMIPS_HI16: case R_MICROMIPS_HIGHER: case R_MICROMIPS_HIGHEST: // For shared object we accepts "high" relocations // against the "_gp_disp" symbol only. if (ref.target()->name() != "_gp_disp") return make_reject_for_shared_lib_reloc_error(this->_ctx, atom, ref); break; case R_MIPS16_26: case R_MIPS_26: case R_MICROMIPS_26_S1: // These relocations are position dependent // and not acceptable in a shared object. return make_reject_for_shared_lib_reloc_error(this->_ctx, atom, ref); default: break; } return std::error_code(); } 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 (isAllGotReloc(refKind) || isAllCallReloc(refKind)) 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.isPIC()) return true; return false; } template bool RelocationPass::hasPLTEntry(const Atom *atom) const { return _pltRegMap.count(atom) || _pltMicroMap.count(atom); } template bool RelocationPass::isMicroMips() { TargetHandler &handler = this->_ctx.getTargetHandler(); return static_cast &>(handler) .getAbiInfoHandler() .isMicroMips(); } template bool RelocationPass::isMipsR6() { TargetHandler &handler = this->_ctx.getTargetHandler(); return static_cast &>(handler) .getAbiInfoHandler() .isMipsR6(); } 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; if (isa(atom)) return true; if (_ctx.getOutputELFType() != ET_DYN) return false; if (da && da->scope() != DefinedAtom::scopeTranslationUnit) return true; return isa(atom); } template static bool isMicroMips(const MipsELFDefinedAtom &atom) { return atom.codeModel() == DefinedAtom::codeMipsMicro || atom.codeModel() == DefinedAtom::codeMipsMicroPIC; } template const PLTAtom *RelocationPass::getPLTEntry(const Atom *a) { // If file contains microMIPS code try to reuse compressed PLT entry... if (isMicroMips()) { 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 isMicroMips() ? 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::handleBranch(const MipsELFDefinedAtom &atom, Reference &ref) { bool isMicro = isMicroMipsReloc(ref.kindValue()); if (const auto *sla = dyn_cast(ref.target())) { if (sla->type() == SharedLibraryAtom::Type::Code) ref.setTarget(isMicro ? getPLTMicroEntry(sla) : getPLTRegEntry(sla)); } else if (requireLA25Stub(atom, ref)) { if (isMicro) ref.setTarget(getLA25MicroEntry(ref.target())); else ref.setTarget(getLA25RegEntry(ref.target())); } if (!isLocal(ref.target())) { if (ref.kindValue() == R_MICROMIPS_26_S1) ref.setKindValue(LLD_R_MICROMIPS_GLOBAL_26_S1); else if (ref.kindValue() == R_MIPS_26) ref.setKindValue(LLD_R_MIPS_GLOBAL_26); } } template void RelocationPass::handleGOT(Reference &ref) { if (!isLocalCall(ref.target())) ref.setTarget(getGlobalGOTEntry(ref.target())); else if (isGotPageReloc(ref.kindValue())) ref.setTarget(getLocalGOTPageEntry(ref)); else if (isLocal(ref.target()) && (isCallReloc(ref.kindValue()) || isGotReloc(ref.kindValue()))) 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 MipsELFDefinedAtom &atom, const Reference &ref) const { if (atom.file().isPIC()) return false; if (auto *da = dyn_cast(ref.target())) return static_cast *>(da)->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, Reference::Addend addend) { 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, addend); return ga; } template const GOTAtom * RelocationPass::getTLSGdGOTEntry(const Atom *a, Reference::Addend addend) { 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, addend); ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL64, 8, a, addend); } else { ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, a, addend); ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL32, 4, a, addend); } 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); 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 = nullptr; if (isMipsR6()) pa = new (_file._alloc) PLTR6Atom(getGOTPLTEntry(a), _file); else pa = new (_file._alloc) PLTARegAtom(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::mips: return llvm::make_unique>(ctx); case llvm::Triple::mipsel: return llvm::make_unique>(ctx); case llvm::Triple::mips64: 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"); } }