| //===- 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()) << "[email protected]"; |
| 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 |