//===- MipsRelocator.cpp -----------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "MipsRelocator.h" #include "MipsRelocationFunctions.h" #include "mcld/IRBuilder.h" #include "mcld/LinkerConfig.h" #include "mcld/Object/ObjectBuilder.h" #include "mcld/Support/MsgHandling.h" #include "mcld/Target/OutputRelocSection.h" #include "mcld/LD/ELFFileFormat.h" #include <llvm/ADT/Twine.h> #include <llvm/Support/ELF.h> namespace mcld { //===----------------------------------------------------------------------===// // MipsRelocationInfo //===----------------------------------------------------------------------===// class MipsRelocationInfo { public: static bool HasSubType(const Relocation& pParent, Relocation::Type pType) { if (llvm::ELF::R_MIPS_NONE == pType) return true; for (Relocation::Type type = pParent.type(); llvm::ELF::R_MIPS_NONE != (type & 0xff); type >>= 8) { if ((type & 0xff) == pType) return true; } return false; } MipsRelocationInfo(Relocation& pParent, bool pIsRel) : m_Parent(&pParent), m_Type(pParent.type()), m_Addend(pIsRel ? pParent.target() : pParent.addend()), m_Symbol(pParent.symValue()), m_Result(pParent.target()) {} bool isNone() const { return llvm::ELF::R_MIPS_NONE == type(); } bool isFirst() const { return type() == (parent().type() & 0xff); } bool isLast() const { return llvm::ELF::R_MIPS_NONE == (m_Type >> 8); } MipsRelocationInfo next() const { return MipsRelocationInfo(*m_Parent, m_Type >> 8, result(), result()); } const Relocation& parent() const { return *m_Parent; } Relocation& parent() { return *m_Parent; } Relocation::Type type() const { return m_Type & 0xff; } Relocation::DWord A() const { return m_Addend; } Relocation::DWord S() const { return m_Symbol; } Relocation::DWord P() const { return parent().place(); } Relocation::DWord result() const { return m_Result; } Relocation::DWord& result() { return m_Result; } private: Relocation* m_Parent; Relocation::Type m_Type; Relocation::DWord m_Addend; Relocation::DWord m_Symbol; Relocation::DWord m_Result; MipsRelocationInfo(Relocation& pParent, Relocation::Type pType, Relocation::DWord pResult, Relocation::DWord pAddend) : m_Parent(&pParent), m_Type(pType), m_Addend(pAddend), m_Symbol(0), m_Result(pResult) {} }; static void helper_PLT_init(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { ResolveInfo* rsym = pReloc.parent().symInfo(); assert(pParent.getSymPLTMap().lookUp(*rsym) == NULL && "PLT entry exists"); MipsGNULDBackend& backend = pParent.getTarget(); PLTEntryBase* pltEntry = backend.getPLT().create(); pParent.getSymPLTMap().record(*rsym, *pltEntry); assert(pParent.getSymGOTPLTMap().lookUp(*rsym) == NULL && "PLT entry not exist, but DynRel entry exist!"); Fragment* gotpltEntry = backend.getGOTPLT().create(); pParent.getSymGOTPLTMap().record(*rsym, *gotpltEntry); Relocation* relEntry = backend.getRelPLT().create(); relEntry->setType(llvm::ELF::R_MIPS_JUMP_SLOT); relEntry->targetRef().assign(*gotpltEntry); relEntry->setSymInfo(rsym); } static Relocator::Address helper_get_PLT_address(ResolveInfo& pSym, MipsRelocator& pParent) { PLTEntryBase* plt_entry = pParent.getSymPLTMap().lookUp(pSym); assert(plt_entry != NULL); return pParent.getTarget().getPLT().addr() + plt_entry->getOffset(); } //===----------------------------------------------------------------------===// // Relocation Functions and Tables //===----------------------------------------------------------------------===// DECL_MIPS_APPLY_RELOC_FUNCS /// the prototype of applying function typedef Relocator::Result (*ApplyFunctionType)(MipsRelocationInfo&, MipsRelocator& pParent); // the table entry of applying functions struct ApplyFunctionTriple { ApplyFunctionType func; unsigned int type; const char* name; unsigned int size; }; // declare the table of applying functions static const ApplyFunctionTriple ApplyFunctions[] = { DECL_MIPS_APPLY_RELOC_FUNC_PTRS}; //===----------------------------------------------------------------------===// // MipsRelocator //===----------------------------------------------------------------------===// MipsRelocator::MipsRelocator(MipsGNULDBackend& pParent, const LinkerConfig& pConfig) : Relocator(pConfig), m_Target(pParent), m_pApplyingInput(NULL), m_CurrentLo16Reloc(NULL) { } Relocator::Result MipsRelocator::applyRelocation(Relocation& pReloc) { // If m_CurrentLo16Reloc is not NULL we are processing // postponed relocation. Otherwise check relocation type // and postpone it for later handling. if (m_CurrentLo16Reloc == NULL && isPostponed(pReloc)) { postponeRelocation(pReloc); return OK; } for (MipsRelocationInfo info(pReloc, isRel()); !info.isNone(); info = info.next()) { if (info.type() >= sizeof(ApplyFunctions) / sizeof(ApplyFunctions[0])) return Unknown; const ApplyFunctionTriple& triple = ApplyFunctions[info.type()]; Result res = triple.func(info, *this); if (OK != res) return res; if (info.isLast()) { uint64_t mask = 0xFFFFFFFFFFFFFFFFULL >> (64 - triple.size); pReloc.target() &= ~mask; pReloc.target() |= info.result() & mask; } } return OK; } const char* MipsRelocator::getName(Relocation::Type pType) const { return ApplyFunctions[pType & 0xff].name; } void MipsRelocator::scanRelocation(Relocation& pReloc, IRBuilder& pBuilder, Module& pModule, LDSection& pSection, Input& pInput) { // rsym - The relocation target symbol ResolveInfo* rsym = pReloc.symInfo(); assert(rsym != NULL && "ResolveInfo of relocation not set while scanRelocation"); // Skip relocation against _gp_disp if (getTarget().getGpDispSymbol() != NULL && rsym == getTarget().getGpDispSymbol()->resolveInfo()) return; assert(pSection.getLink() != NULL); if ((pSection.getLink()->flag() & llvm::ELF::SHF_ALLOC) == 0) return; for (MipsRelocationInfo info(pReloc, isRel()); !info.isNone(); info = info.next()) { // We test isLocal or if pInputSym is not a dynamic symbol // We assume -Bsymbolic to bind all symbols internaly via !rsym->isDyn() // Don't put undef symbols into local entries. if (isLocalReloc(*rsym)) scanLocalReloc(info, pBuilder, pSection); else scanGlobalReloc(info, pBuilder, pSection); if (getTarget().needsLA25Stub(info.type(), info.parent().symInfo())) getTarget().addNonPICBranchSym(pReloc.symInfo()); } // Check if we should issue undefined reference // for the relocation target symbol. if (rsym->isUndef() && !rsym->isDyn() && !rsym->isWeak() && !rsym->isNull()) issueUndefRef(pReloc, pSection, pInput); } bool MipsRelocator::initializeScan(Input& pInput) { if (LinkerConfig::Object != config().codeGenType()) getTarget().getGOT().initializeScan(pInput); return true; } bool MipsRelocator::finalizeScan(Input& pInput) { if (LinkerConfig::Object != config().codeGenType()) getTarget().getGOT().finalizeScan(pInput); return true; } bool MipsRelocator::initializeApply(Input& pInput) { m_pApplyingInput = &pInput; return true; } bool MipsRelocator::finalizeApply(Input& pInput) { m_pApplyingInput = NULL; return true; } void MipsRelocator::scanLocalReloc(MipsRelocationInfo& pReloc, IRBuilder& pBuilder, const LDSection& pSection) { ResolveInfo* rsym = pReloc.parent().symInfo(); switch (pReloc.type()) { case llvm::ELF::R_MIPS_NONE: case llvm::ELF::R_MIPS_16: break; case llvm::ELF::R_MIPS_32: case llvm::ELF::R_MIPS_64: if (pReloc.isFirst() && LinkerConfig::DynObj == config().codeGenType()) { // TODO: (simon) The gold linker does not create an entry in .rel.dyn // section if the symbol section flags contains SHF_EXECINSTR. // 1. Find the reason of this condition. // 2. Check this condition here. getTarget().getRelDyn().reserveEntry(); rsym->setReserved(rsym->reserved() | ReserveRel); getTarget().checkAndSetHasTextRel(*pSection.getLink()); } break; case llvm::ELF::R_MIPS_REL32: case llvm::ELF::R_MIPS_26: case llvm::ELF::R_MIPS_HI16: case llvm::ELF::R_MIPS_LO16: case llvm::ELF::R_MIPS_SHIFT5: case llvm::ELF::R_MIPS_SHIFT6: case llvm::ELF::R_MIPS_SUB: case llvm::ELF::R_MIPS_INSERT_A: case llvm::ELF::R_MIPS_INSERT_B: case llvm::ELF::R_MIPS_DELETE: case llvm::ELF::R_MIPS_HIGHER: case llvm::ELF::R_MIPS_HIGHEST: case llvm::ELF::R_MIPS_SCN_DISP: case llvm::ELF::R_MIPS_REL16: case llvm::ELF::R_MIPS_ADD_IMMEDIATE: case llvm::ELF::R_MIPS_PJUMP: case llvm::ELF::R_MIPS_RELGOT: case llvm::ELF::R_MIPS_JALR: case llvm::ELF::R_MIPS_GLOB_DAT: case llvm::ELF::R_MIPS_COPY: case llvm::ELF::R_MIPS_JUMP_SLOT: break; case llvm::ELF::R_MIPS_GOT16: case llvm::ELF::R_MIPS_CALL16: case llvm::ELF::R_MIPS_GOT_HI16: case llvm::ELF::R_MIPS_CALL_HI16: case llvm::ELF::R_MIPS_GOT_LO16: case llvm::ELF::R_MIPS_CALL_LO16: case llvm::ELF::R_MIPS_GOT_DISP: case llvm::ELF::R_MIPS_GOT_PAGE: case llvm::ELF::R_MIPS_GOT_OFST: if (getTarget() .getGOT() .reserveLocalEntry(*rsym, pReloc.type(), pReloc.A())) { if (getTarget().getGOT().hasMultipleGOT()) getTarget().checkAndSetHasTextRel(*pSection.getLink()); } break; case llvm::ELF::R_MIPS_GPREL32: case llvm::ELF::R_MIPS_GPREL16: case llvm::ELF::R_MIPS_LITERAL: break; case llvm::ELF::R_MIPS_TLS_GD: getTarget().getGOT().reserveTLSGdEntry(*rsym); getTarget().checkAndSetHasTextRel(*pSection.getLink()); break; case llvm::ELF::R_MIPS_TLS_LDM: getTarget().getGOT().reserveTLSLdmEntry(); getTarget().checkAndSetHasTextRel(*pSection.getLink()); break; case llvm::ELF::R_MIPS_TLS_GOTTPREL: getTarget().getGOT().reserveTLSGotEntry(*rsym); getTarget().checkAndSetHasTextRel(*pSection.getLink()); break; case llvm::ELF::R_MIPS_TLS_DTPMOD32: case llvm::ELF::R_MIPS_TLS_DTPREL32: case llvm::ELF::R_MIPS_TLS_DTPMOD64: case llvm::ELF::R_MIPS_TLS_DTPREL64: case llvm::ELF::R_MIPS_TLS_DTPREL_HI16: case llvm::ELF::R_MIPS_TLS_DTPREL_LO16: case llvm::ELF::R_MIPS_TLS_TPREL32: case llvm::ELF::R_MIPS_TLS_TPREL64: case llvm::ELF::R_MIPS_TLS_TPREL_HI16: case llvm::ELF::R_MIPS_TLS_TPREL_LO16: break; case llvm::ELF::R_MIPS_PC16: case llvm::ELF::R_MIPS_PC32: case llvm::ELF::R_MIPS_PC18_S3: case llvm::ELF::R_MIPS_PC19_S2: case llvm::ELF::R_MIPS_PC21_S2: case llvm::ELF::R_MIPS_PC26_S2: case llvm::ELF::R_MIPS_PCHI16: case llvm::ELF::R_MIPS_PCLO16: break; default: fatal(diag::unknown_relocation) << static_cast<int>(pReloc.type()) << rsym->name(); } } void MipsRelocator::scanGlobalReloc(MipsRelocationInfo& pReloc, IRBuilder& pBuilder, const LDSection& pSection) { ResolveInfo* rsym = pReloc.parent().symInfo(); bool hasPLT = rsym->reserved() & ReservePLT; switch (pReloc.type()) { case llvm::ELF::R_MIPS_NONE: case llvm::ELF::R_MIPS_INSERT_A: case llvm::ELF::R_MIPS_INSERT_B: case llvm::ELF::R_MIPS_DELETE: case llvm::ELF::R_MIPS_TLS_DTPMOD64: case llvm::ELF::R_MIPS_TLS_DTPREL64: case llvm::ELF::R_MIPS_REL16: case llvm::ELF::R_MIPS_ADD_IMMEDIATE: case llvm::ELF::R_MIPS_PJUMP: case llvm::ELF::R_MIPS_RELGOT: case llvm::ELF::R_MIPS_TLS_TPREL64: break; case llvm::ELF::R_MIPS_32: case llvm::ELF::R_MIPS_64: if (pReloc.isFirst() && getTarget().symbolNeedsDynRel(*rsym, hasPLT, true)) { getTarget().getRelDyn().reserveEntry(); rsym->setReserved(rsym->reserved() | ReserveRel); getTarget().checkAndSetHasTextRel(*pSection.getLink()); if (!getTarget().symbolFinalValueIsKnown(*rsym)) getTarget().getGOT().reserveGlobalEntry(*rsym); } break; case llvm::ELF::R_MIPS_HI16: case llvm::ELF::R_MIPS_LO16: if (getTarget().symbolNeedsDynRel(*rsym, hasPLT, true) || getTarget().symbolNeedsCopyReloc(pReloc.parent(), *rsym)) { getTarget().getRelDyn().reserveEntry(); LDSymbol& cpySym = defineSymbolforCopyReloc(pBuilder, *rsym); addCopyReloc(*cpySym.resolveInfo()); } break; case llvm::ELF::R_MIPS_GOT16: case llvm::ELF::R_MIPS_CALL16: case llvm::ELF::R_MIPS_GOT_DISP: case llvm::ELF::R_MIPS_GOT_HI16: case llvm::ELF::R_MIPS_CALL_HI16: case llvm::ELF::R_MIPS_GOT_LO16: case llvm::ELF::R_MIPS_CALL_LO16: case llvm::ELF::R_MIPS_GOT_PAGE: case llvm::ELF::R_MIPS_GOT_OFST: if (getTarget().getGOT().reserveGlobalEntry(*rsym)) { if (getTarget().getGOT().hasMultipleGOT()) getTarget().checkAndSetHasTextRel(*pSection.getLink()); } break; case llvm::ELF::R_MIPS_LITERAL: case llvm::ELF::R_MIPS_GPREL32: fatal(diag::invalid_global_relocation) << static_cast<int>(pReloc.type()) << rsym->name(); break; case llvm::ELF::R_MIPS_GPREL16: break; case llvm::ELF::R_MIPS_26: // Create a PLT entry if the symbol requires it and does not have it. if (getTarget().symbolNeedsPLT(*rsym) && !hasPLT) { helper_PLT_init(pReloc, *this); rsym->setReserved(rsym->reserved() | ReservePLT); } break; case llvm::ELF::R_MIPS_16: case llvm::ELF::R_MIPS_SHIFT5: case llvm::ELF::R_MIPS_SHIFT6: case llvm::ELF::R_MIPS_SUB: case llvm::ELF::R_MIPS_HIGHER: case llvm::ELF::R_MIPS_HIGHEST: case llvm::ELF::R_MIPS_SCN_DISP: break; case llvm::ELF::R_MIPS_TLS_GD: getTarget().getGOT().reserveTLSGdEntry(*rsym); getTarget().checkAndSetHasTextRel(*pSection.getLink()); break; case llvm::ELF::R_MIPS_TLS_LDM: getTarget().getGOT().reserveTLSLdmEntry(); getTarget().checkAndSetHasTextRel(*pSection.getLink()); break; case llvm::ELF::R_MIPS_TLS_GOTTPREL: getTarget().getGOT().reserveTLSGotEntry(*rsym); getTarget().checkAndSetHasTextRel(*pSection.getLink()); break; case llvm::ELF::R_MIPS_TLS_DTPREL32: case llvm::ELF::R_MIPS_TLS_DTPREL_HI16: case llvm::ELF::R_MIPS_TLS_DTPREL_LO16: case llvm::ELF::R_MIPS_TLS_TPREL32: case llvm::ELF::R_MIPS_TLS_TPREL_HI16: case llvm::ELF::R_MIPS_TLS_TPREL_LO16: break; case llvm::ELF::R_MIPS_REL32: case llvm::ELF::R_MIPS_JALR: case llvm::ELF::R_MIPS_PC16: case llvm::ELF::R_MIPS_PC32: case llvm::ELF::R_MIPS_PC18_S3: case llvm::ELF::R_MIPS_PC19_S2: case llvm::ELF::R_MIPS_PC21_S2: case llvm::ELF::R_MIPS_PC26_S2: case llvm::ELF::R_MIPS_PCHI16: case llvm::ELF::R_MIPS_PCLO16: break; case llvm::ELF::R_MIPS_COPY: case llvm::ELF::R_MIPS_GLOB_DAT: case llvm::ELF::R_MIPS_JUMP_SLOT: fatal(diag::dynamic_relocation) << static_cast<int>(pReloc.type()); break; default: fatal(diag::unknown_relocation) << static_cast<int>(pReloc.type()) << rsym->name(); } } bool MipsRelocator::isPostponed(const Relocation& pReloc) const { if (isN64ABI()) return false; if (MipsRelocationInfo::HasSubType(pReloc, llvm::ELF::R_MIPS_HI16) || MipsRelocationInfo::HasSubType(pReloc, llvm::ELF::R_MIPS_PCHI16)) return true; if (MipsRelocationInfo::HasSubType(pReloc, llvm::ELF::R_MIPS_GOT16) && pReloc.symInfo()->isLocal()) return true; return false; } void MipsRelocator::addCopyReloc(ResolveInfo& pSym) { Relocation& relEntry = *getTarget().getRelDyn().consumeEntry(); relEntry.setType(llvm::ELF::R_MIPS_COPY); assert(pSym.outSymbol()->hasFragRef()); relEntry.targetRef().assign(*pSym.outSymbol()->fragRef()); relEntry.setSymInfo(&pSym); } LDSymbol& MipsRelocator::defineSymbolforCopyReloc(IRBuilder& pBuilder, const ResolveInfo& pSym) { // Get or create corresponding BSS LDSection ELFFileFormat* fileFormat = getTarget().getOutputFormat(); LDSection* bssSectHdr = ResolveInfo::ThreadLocal == pSym.type() ? &fileFormat->getTBSS() : &fileFormat->getBSS(); // Get or create corresponding BSS SectionData SectionData* bssData = bssSectHdr->hasSectionData() ? bssSectHdr->getSectionData() : IRBuilder::CreateSectionData(*bssSectHdr); // Determine the alignment by the symbol value // FIXME: here we use the largest alignment uint32_t addrAlign = config().targets().bitclass() / 8; // Allocate space in BSS for the copy symbol Fragment* frag = new FillFragment(0x0, 1, pSym.size()); uint64_t size = ObjectBuilder::AppendFragment(*frag, *bssData, addrAlign); bssSectHdr->setSize(bssSectHdr->size() + size); // Change symbol binding to Global if it's a weak symbol ResolveInfo::Binding binding = (ResolveInfo::Binding)pSym.binding(); if (binding == ResolveInfo::Weak) binding = ResolveInfo::Global; // Define the copy symbol in the bss section and resolve it LDSymbol* cpySym = pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>( pSym.name(), (ResolveInfo::Type)pSym.type(), ResolveInfo::Define, binding, pSym.size(), // size 0x0, // value FragmentRef::Create(*frag, 0x0), (ResolveInfo::Visibility)pSym.other()); // Output all other alias symbols if any Module::AliasList* alias_list = pBuilder.getModule().getAliasList(pSym); if (alias_list == NULL) return *cpySym; for (Module::alias_iterator it = alias_list->begin(), ie = alias_list->end(); it != ie; ++it) { const ResolveInfo* alias = *it; if (alias == &pSym || !alias->isDyn()) continue; pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>( alias->name(), (ResolveInfo::Type)alias->type(), ResolveInfo::Define, binding, alias->size(), // size 0x0, // value FragmentRef::Create(*frag, 0x0), (ResolveInfo::Visibility)alias->other()); } return *cpySym; } void MipsRelocator::postponeRelocation(Relocation& pReloc) { ResolveInfo* rsym = pReloc.symInfo(); m_PostponedRelocs[rsym].insert(&pReloc); } void MipsRelocator::applyPostponedRelocations(MipsRelocationInfo& pLo16Reloc) { m_CurrentLo16Reloc = &pLo16Reloc; ResolveInfo* rsym = pLo16Reloc.parent().symInfo(); RelocationSet& relocs = m_PostponedRelocs[rsym]; for (RelocationSet::iterator it = relocs.begin(); it != relocs.end(); ++it) (*it)->apply(*this); m_PostponedRelocs.erase(rsym); m_CurrentLo16Reloc = NULL; } bool MipsRelocator::isGpDisp(const Relocation& pReloc) const { return strcmp("_gp_disp", pReloc.symInfo()->name()) == 0; } bool MipsRelocator::isRel() const { return config().targets().is32Bits(); } bool MipsRelocator::isLocalReloc(ResolveInfo& pSym) const { if (pSym.isUndef()) return false; return pSym.isLocal() || !getTarget().isDynamicSymbol(pSym) || !pSym.isDyn(); } Relocator::Address MipsRelocator::getGPAddress() { return getTarget().getGOT().getGPAddr(getApplyingInput()); } Relocator::Address MipsRelocator::getTPOffset() { return getTarget().getTPOffset(getApplyingInput()); } Relocator::Address MipsRelocator::getDTPOffset() { return getTarget().getDTPOffset(getApplyingInput()); } Relocator::Address MipsRelocator::getGP0() { return getTarget().getGP0(getApplyingInput()); } Fragment& MipsRelocator::getLocalGOTEntry(MipsRelocationInfo& pReloc, Relocation::DWord entryValue) { // rsym - The relocation target symbol ResolveInfo* rsym = pReloc.parent().symInfo(); MipsGOT& got = getTarget().getGOT(); assert(isLocalReloc(*rsym) && "Attempt to get a global GOT entry for the local relocation"); Fragment* got_entry = got.lookupLocalEntry(rsym, entryValue); // Found a mapping, then return the mapped entry immediately. if (got_entry != NULL) return *got_entry; // Not found. got_entry = got.consumeLocal(); if (got.isPrimaryGOTConsumed()) setupRel32DynEntry(*FragmentRef::Create(*got_entry, 0), NULL); else got.setEntryValue(got_entry, entryValue); got.recordLocalEntry(rsym, entryValue, got_entry); return *got_entry; } Fragment& MipsRelocator::getGlobalGOTEntry(MipsRelocationInfo& pReloc) { // rsym - The relocation target symbol ResolveInfo* rsym = pReloc.parent().symInfo(); MipsGOT& got = getTarget().getGOT(); assert(!isLocalReloc(*rsym) && "Attempt to get a local GOT entry for the global relocation"); Fragment* got_entry = got.lookupGlobalEntry(rsym); // Found a mapping, then return the mapped entry immediately. if (got_entry != NULL) return *got_entry; // Not found. got_entry = got.consumeGlobal(); if (got.isPrimaryGOTConsumed()) setupRel32DynEntry(*FragmentRef::Create(*got_entry, 0), rsym); else got.setEntryValue(got_entry, pReloc.parent().symValue()); got.recordGlobalEntry(rsym, got_entry); return *got_entry; } Fragment& MipsRelocator::getTLSGOTEntry(MipsRelocationInfo& pReloc) { // rsym - The relocation target symbol ResolveInfo* rsym = pReloc.parent().symInfo(); MipsGOT& got = getTarget().getGOT(); Fragment* modEntry = got.lookupTLSEntry(rsym, pReloc.type()); // Found a mapping, then return the mapped entry immediately. if (modEntry != NULL) return *modEntry; // Not found. modEntry = got.consumeTLS(pReloc.type()); setupTLSDynEntry(*modEntry, rsym, pReloc.type()); got.recordTLSEntry(rsym, modEntry, pReloc.type()); return *modEntry; } Relocator::Address MipsRelocator::getGOTOffset(MipsRelocationInfo& pReloc) { ResolveInfo* rsym = pReloc.parent().symInfo(); MipsGOT& got = getTarget().getGOT(); if (isLocalReloc(*rsym)) { uint64_t value = pReloc.S(); if (ResolveInfo::Section == rsym->type()) value += pReloc.A(); return got.getGPRelOffset(getApplyingInput(), getLocalGOTEntry(pReloc, value)); } else { return got.getGPRelOffset(getApplyingInput(), getGlobalGOTEntry(pReloc)); } } Relocator::Address MipsRelocator::getTLSGOTOffset(MipsRelocationInfo& pReloc) { MipsGOT& got = getTarget().getGOT(); return got.getGPRelOffset(getApplyingInput(), getTLSGOTEntry(pReloc)); } void MipsRelocator::createDynRel(MipsRelocationInfo& pReloc) { Relocator::DWord A = pReloc.A(); Relocator::DWord S = pReloc.S(); ResolveInfo* rsym = pReloc.parent().symInfo(); if (getTarget().isDynamicSymbol(*rsym)) { setupRel32DynEntry(pReloc.parent().targetRef(), rsym); // Don't add symbol value that will be resolved by the dynamic linker. pReloc.result() = A; } else { setupRel32DynEntry(pReloc.parent().targetRef(), NULL); pReloc.result() = A + S; } if (!isLocalReloc(*rsym) && !getTarget().symbolFinalValueIsKnown(*rsym)) getGlobalGOTEntry(pReloc); } uint64_t MipsRelocator::calcAHL(const MipsRelocationInfo& pHiReloc) { if (isN64ABI()) return pHiReloc.A(); assert(m_CurrentLo16Reloc != NULL && "There is no saved R_MIPS_LO16 relocation"); uint64_t AHI = pHiReloc.A() & 0xFFFF; uint64_t ALO = m_CurrentLo16Reloc->A() & 0xFFFF; uint64_t AHL = (AHI << 16) + int16_t(ALO); return AHL; } bool MipsRelocator::isN64ABI() const { return config().targets().is64Bits(); } uint32_t MipsRelocator::getDebugStringOffset(Relocation& pReloc) const { if (pReloc.type() != llvm::ELF::R_MIPS_32) error(diag::unsupport_reloc_for_debug_string) << getName(pReloc.type()) << "mclinker@googlegroups.com"; if (pReloc.symInfo()->type() == ResolveInfo::Section) return pReloc.target() + pReloc.addend(); else return pReloc.symInfo()->outSymbol()->fragRef()->offset() + pReloc.target() + pReloc.addend(); } void MipsRelocator::applyDebugStringOffset(Relocation& pReloc, uint32_t pOffset) { pReloc.target() = pOffset; } void MipsRelocator::setupRelDynEntry(FragmentRef& pFragRef, ResolveInfo* pSym, Relocation::Type pType) { Relocation& relEntry = *getTarget().getRelDyn().consumeEntry(); relEntry.setType(pType); relEntry.targetRef() = pFragRef; relEntry.setSymInfo(pSym); } //===----------------------------------------------------------------------===// // Mips32Relocator //===----------------------------------------------------------------------===// Mips32Relocator::Mips32Relocator(Mips32GNULDBackend& pParent, const LinkerConfig& pConfig) : MipsRelocator(pParent, pConfig) { } void Mips32Relocator::setupRel32DynEntry(FragmentRef& pFragRef, ResolveInfo* pSym) { setupRelDynEntry(pFragRef, pSym, llvm::ELF::R_MIPS_REL32); } void Mips32Relocator::setupTLSDynEntry(Fragment& pFrag, ResolveInfo* pSym, Relocation::Type pType) { pSym = pSym->isLocal() ? nullptr : pSym; if (pType == llvm::ELF::R_MIPS_TLS_GD) { FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0); setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPMOD32); FragmentRef& relFrag = *FragmentRef::Create(*pFrag.getNextNode(), 0); setupRelDynEntry(relFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPREL32); } else if (pType == llvm::ELF::R_MIPS_TLS_LDM) { FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0); setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPMOD32); } else if (pType == llvm::ELF::R_MIPS_TLS_GOTTPREL) { FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0); setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_TPREL32); } else { llvm_unreachable("Unexpected relocation"); } } Relocator::Size Mips32Relocator::getSize(Relocation::Type pType) const { return ApplyFunctions[pType & 0xff].size; } //===----------------------------------------------------------------------===// // Mips64Relocator //===----------------------------------------------------------------------===// Mips64Relocator::Mips64Relocator(Mips64GNULDBackend& pParent, const LinkerConfig& pConfig) : MipsRelocator(pParent, pConfig) { } void Mips64Relocator::setupRel32DynEntry(FragmentRef& pFragRef, ResolveInfo* pSym) { Relocation::Type type = llvm::ELF::R_MIPS_REL32 | llvm::ELF::R_MIPS_64 << 8; setupRelDynEntry(pFragRef, pSym, type); } void Mips64Relocator::setupTLSDynEntry(Fragment& pFrag, ResolveInfo* pSym, Relocation::Type pType) { pSym = pSym->isLocal() ? nullptr : pSym; if (pType == llvm::ELF::R_MIPS_TLS_GD) { FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0); setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPMOD64); FragmentRef& relFrag = *FragmentRef::Create(*pFrag.getNextNode(), 0); setupRelDynEntry(relFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPREL64); } else if (pType == llvm::ELF::R_MIPS_TLS_LDM) { FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0); setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPMOD64); } else if (pType == llvm::ELF::R_MIPS_TLS_GOTTPREL) { FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0); setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_TPREL64); } else { llvm_unreachable("Unexpected relocation"); } } Relocator::Size Mips64Relocator::getSize(Relocation::Type pType) const { if (((pType >> 16) & 0xff) != llvm::ELF::R_MIPS_NONE) return ApplyFunctions[(pType >> 16) & 0xff].size; if (((pType >> 8) & 0xff) != llvm::ELF::R_MIPS_NONE) return ApplyFunctions[(pType >> 8) & 0xff].size; return ApplyFunctions[pType & 0xff].size; } //=========================================// // Relocation functions implementation // //=========================================// // R_MIPS_NONE and those unsupported/deprecated relocation type static MipsRelocator::Result none(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { return Relocator::OK; } // R_MIPS_32: S + A static MipsRelocator::Result abs32(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { ResolveInfo* rsym = pReloc.parent().symInfo(); Relocator::DWord A = pReloc.A(); Relocator::DWord S = pReloc.S(); LDSection& target_sect = pReloc.parent().targetRef().frag()->getParent()->getSection(); // If the flag of target section is not ALLOC, we will not scan this // relocation // but perform static relocation. (e.g., applying .debug section) if ((llvm::ELF::SHF_ALLOC & target_sect.flag()) == 0x0) { pReloc.result() = S + A; return Relocator::OK; } if (rsym->reserved() & MipsRelocator::ReserveRel) { pParent.createDynRel(pReloc); return Relocator::OK; } pReloc.result() = S + A; return Relocator::OK; } // R_MIPS_26: // local : ((A | ((P + 4) & 0x3F000000)) + S) >> 2 // external: (sign–extend(A) + S) >> 2 static MipsRelocator::Result rel26(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { ResolveInfo* rsym = pReloc.parent().symInfo(); int32_t A = pParent.isN64ABI() ? pReloc.A() : (pReloc.A() & 0x03FFFFFF) << 2; int32_t P = pReloc.P(); int32_t S = rsym->reserved() & MipsRelocator::ReservePLT ? helper_get_PLT_address(*rsym, pParent) : pReloc.S(); if (rsym->isLocal()) pReloc.result() = A | ((P + 4) & 0x3F000000); else pReloc.result() = signExtend<28>(A); pReloc.result() = (pReloc.result() + S) >> 2; return Relocator::OK; } // R_MIPS_HI16: // local/external: ((AHL + S) - (short)(AHL + S)) >> 16 // _gp_disp : ((AHL + GP - P) - (short)(AHL + GP - P)) >> 16 static MipsRelocator::Result hi16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { uint64_t AHL = pParent.calcAHL(pReloc); if (pParent.isGpDisp(pReloc.parent())) { int32_t P = pReloc.P(); int32_t GP = pParent.getGPAddress(); pReloc.result() = ((AHL + GP - P) - (int16_t)(AHL + GP - P)) >> 16; } else { int32_t S = pReloc.S(); if (pParent.isN64ABI()) pReloc.result() = (pReloc.A() + S + 0x8000ull) >> 16; else pReloc.result() = ((AHL + S) - (int16_t)(AHL + S)) >> 16; } return Relocator::OK; } // R_MIPS_LO16: // local/external: AHL + S // _gp_disp : AHL + GP - P + 4 static MipsRelocator::Result lo16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { // AHL is a combination of HI16 and LO16 addends. But R_MIPS_LO16 // uses low 16 bits of the AHL. That is why we do not need R_MIPS_HI16 // addend here. int32_t AHL = (pReloc.A() & 0xFFFF); if (pParent.isGpDisp(pReloc.parent())) { int32_t P = pReloc.P(); int32_t GP = pParent.getGPAddress(); pReloc.result() = AHL + GP - P + 4; } else { int32_t S = pReloc.S(); pReloc.result() = AHL + S; } pParent.applyPostponedRelocations(pReloc); return Relocator::OK; } // R_MIPS_GPREL16: // external: sign–extend(A) + S - GP // local : sign–extend(A) + S + GP0 – GP static MipsRelocator::Result gprel16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { // Remember to add the section offset to A. uint64_t A = pReloc.A(); uint64_t S = pReloc.S(); uint64_t GP0 = pParent.getGP0(); uint64_t GP = pParent.getGPAddress(); ResolveInfo* rsym = pReloc.parent().symInfo(); if (rsym->isLocal()) pReloc.result() = A + S + GP0 - GP; else pReloc.result() = A + S - GP; return Relocator::OK; } // R_MIPS_GOT16: // local : G (calculate AHL and put high 16 bit to GOT) // external: G static MipsRelocator::Result got16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { if (pReloc.parent().symInfo()->isLocal()) { int32_t AHL = pParent.calcAHL(pReloc); int32_t S = pReloc.S(); int32_t res = (AHL + S + 0x8000) & 0xFFFF0000; MipsGOT& got = pParent.getTarget().getGOT(); Fragment& got_entry = pParent.getLocalGOTEntry(pReloc, res); pReloc.result() = got.getGPRelOffset(pParent.getApplyingInput(), got_entry); } else { pReloc.result() = pParent.getGOTOffset(pReloc); } return Relocator::OK; } // R_MIPS_GOTHI16: // external: (G - (short)G) >> 16 + A static MipsRelocator::Result gothi16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { Relocator::Address G = pParent.getGOTOffset(pReloc); int32_t A = pReloc.A(); pReloc.result() = (G - (int16_t)G) >> (16 + A); return Relocator::OK; } // R_MIPS_GOTLO16: // external: G & 0xffff static MipsRelocator::Result gotlo16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { pReloc.result() = pParent.getGOTOffset(pReloc) & 0xffff; return Relocator::OK; } // R_MIPS_SUB: // external/local: S - A static MipsRelocator::Result sub(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { uint64_t S = pReloc.S(); uint64_t A = pReloc.A(); pReloc.result() = S - A; return Relocator::OK; } // R_MIPS_CALL16: G static MipsRelocator::Result call16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { pReloc.result() = pParent.getGOTOffset(pReloc); return Relocator::OK; } // R_MIPS_GPREL32: A + S + GP0 - GP static MipsRelocator::Result gprel32(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { // Remember to add the section offset to A. uint64_t A = pReloc.A(); uint64_t S = pReloc.S(); uint64_t GP0 = pParent.getGP0(); uint64_t GP = pParent.getGPAddress(); pReloc.result() = A + S + GP0 - GP; return Relocator::OK; } // R_MIPS_64: S + A static MipsRelocator::Result abs64(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { // FIXME (simon): Consider to merge with abs32() or use the same function // but with another mask size. ResolveInfo* rsym = pReloc.parent().symInfo(); Relocator::DWord A = pReloc.A(); Relocator::DWord S = pReloc.S(); LDSection& target_sect = pReloc.parent().targetRef().frag()->getParent()->getSection(); // If the flag of target section is not ALLOC, we will not scan this // relocation // but perform static relocation. (e.g., applying .debug section) if (0x0 == (llvm::ELF::SHF_ALLOC & target_sect.flag())) { pReloc.result() = S + A; return Relocator::OK; } if (rsym->reserved() & MipsRelocator::ReserveRel) { pParent.createDynRel(pReloc); return Relocator::OK; } pReloc.result() = S + A; return Relocator::OK; } // R_MIPS_GOT_DISP / R_MIPS_GOT_PAGE: G static MipsRelocator::Result gotdisp(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { pReloc.result() = pParent.getGOTOffset(pReloc); return Relocator::OK; } // R_MIPS_GOT_OFST: static MipsRelocator::Result gotoff(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { // FIXME (simon): Needs to be implemented. return Relocator::OK; } // R_MIPS_JALR: static MipsRelocator::Result jalr(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { return Relocator::OK; } // R_MIPS_PC16 static MipsRelocator::Result pc16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { int64_t A = signExtend<18>(pReloc.A() << 2); int64_t S = pReloc.S(); int64_t P = pReloc.P(); pReloc.result() = (A + S - P) >> 2; return Relocator::OK; } // R_MIPS_PC32 static MipsRelocator::Result pc32(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { int64_t A = pReloc.A(); int64_t S = pReloc.S(); int64_t P = pReloc.P(); pReloc.result() = A + S - P; return Relocator::OK; } // R_MIPS_PC18_S3 static MipsRelocator::Result pc18_s3(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { int64_t A = signExtend<21>(pReloc.A() << 3); int64_t S = pReloc.S(); int64_t P = pReloc.P(); pReloc.result() = (S + A - ((P | 7) ^ 7)) >> 3; return Relocator::OK; } // R_MIPS_PC19_S2 static MipsRelocator::Result pc19_s2(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { int64_t A = signExtend<21>(pReloc.A() << 2); int64_t S = pReloc.S(); int64_t P = pReloc.P(); pReloc.result() = (A + S - P) >> 2; return Relocator::OK; } // R_MIPS_PC21_S2 static MipsRelocator::Result pc21_s2(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { int32_t A = signExtend<23>(pReloc.A() << 2); int32_t S = pReloc.S(); int32_t P = pReloc.P(); pReloc.result() = (A + S - P) >> 2; return Relocator::OK; } // R_MIPS_PC26_S2 static MipsRelocator::Result pc26_s2(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { int64_t A = signExtend<28>(pReloc.A() << 2); int64_t S = pReloc.S(); int64_t P = pReloc.P(); pReloc.result() = (A + S - P) >> 2; return Relocator::OK; } // R_MIPS_PCHI16 static MipsRelocator::Result pchi16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { uint64_t AHL = pParent.calcAHL(pReloc); int64_t S = pReloc.S(); int64_t P = pReloc.P(); pReloc.result() = (S + AHL - P + 0x8000) >> 16; return Relocator::OK; } // R_MIPS_PCLO16 static MipsRelocator::Result pclo16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { int32_t AHL = pReloc.A() & 0xFFFF; int64_t S = pReloc.S(); int64_t P = pReloc.P(); pReloc.result() = S + AHL - P; pParent.applyPostponedRelocations(pReloc); return Relocator::OK; } // R_MIPS_TLS_TPREL_HI16, R_MIPS_TLS_DTPREL_HI16 // local/external: (A + S - TP Offset) >> 16 // _gp_disp : (A + GP - P - TP Offset) >> 16 static MipsRelocator::Result tlshi16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { uint64_t A = pReloc.A() & 0xFFFF; if (pReloc.type() == llvm::ELF::R_MIPS_TLS_TPREL_HI16) A -= pParent.getTPOffset(); else if (pReloc.type() == llvm::ELF::R_MIPS_TLS_DTPREL_HI16) A -= pParent.getDTPOffset(); else llvm_unreachable("Unexpected relocation"); if (pParent.isGpDisp(pReloc.parent())) pReloc.result() = (A + pReloc.S() - pReloc.P() + 0x8000) >> 16; else pReloc.result() = (A + pReloc.S() + 0x8000) >> 16; return Relocator::OK; } // R_MIPS_TLS_TPREL_LO16, R_MIPS_TLS_DTPREL_LO16 // local/external: A + S - TP Offset // _gp_disp : A + GP - P + 4 - TP Offset static MipsRelocator::Result tlslo16(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { uint64_t A = pReloc.A() & 0xFFFF; if (pReloc.type() == llvm::ELF::R_MIPS_TLS_TPREL_LO16) A -= pParent.getTPOffset(); else if (pReloc.type() == llvm::ELF::R_MIPS_TLS_DTPREL_LO16) A -= pParent.getDTPOffset(); else llvm_unreachable("Unexpected relocation"); if (pParent.isGpDisp(pReloc.parent())) pReloc.result() = A + pReloc.S() - pReloc.P() + 4; else pReloc.result() = A + pReloc.S(); return Relocator::OK; } // R_MIPS_TLS_GD, R_MIPS_TLS_LDM static MipsRelocator::Result tlsgot(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { pReloc.result() = pParent.getTLSGOTOffset(pReloc); return Relocator::OK; } static MipsRelocator::Result unsupported(MipsRelocationInfo& pReloc, MipsRelocator& pParent) { return Relocator::Unsupported; } } // namespace mcld