//===- MipsRelocator.cpp -----------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include <llvm/ADT/Twine.h> #include <llvm/Support/ELF.h> #include <mcld/Support/MsgHandling.h> #include <mcld/Target/OutputRelocSection.h> #include <mcld/LinkerConfig.h> #include <mcld/IRBuilder.h> #include "MipsRelocator.h" #include "MipsRelocationFunctions.h" using namespace mcld; //===----------------------------------------------------------------------===// // Relocation Functions and Tables //===----------------------------------------------------------------------===// DECL_MIPS_APPLY_RELOC_FUNCS /// the prototype of applying function typedef Relocator::Result (*ApplyFunctionType)(Relocation&, MipsRelocator&); // 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_AHL(0) { } Relocator::Result MipsRelocator::applyRelocation(Relocation& pRelocation) { Relocation::Type type = pRelocation.type(); if (type >= sizeof(ApplyFunctions) / sizeof(ApplyFunctions[0])) { return Unknown; } // apply the relocation return ApplyFunctions[type].func(pRelocation, *this); } const char* MipsRelocator::getName(Relocation::Type pType) const { return ApplyFunctions[pType].name; } Relocator::Size MipsRelocator::getSize(Relocation::Type pType) const { return ApplyFunctions[pType].size; } void MipsRelocator::scanRelocation(Relocation& pReloc, IRBuilder& pBuilder, Module& pModule, LDSection& pSection) { // rsym - The relocation target symbol ResolveInfo* rsym = pReloc.symInfo(); assert(NULL != rsym && "ResolveInfo of relocation not set while scanRelocation"); // Skip relocation against _gp_disp if (NULL != getTarget().getGpDispSymbol()) { if (pReloc.symInfo() == getTarget().getGpDispSymbol()->resolveInfo()) return; } pReloc.updateAddend(); assert(NULL != pSection.getLink()); if (0 == (pSection.getLink()->flag() & llvm::ELF::SHF_ALLOC)) return; // 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 ((rsym->isLocal() || !getTarget().isDynamicSymbol(*rsym) || !rsym->isDyn()) && !rsym->isUndef()) scanLocalReloc(pReloc, pBuilder, pSection); else scanGlobalReloc(pReloc, pBuilder, pSection); // check if we shoule issue undefined reference for the relocation target // symbol if (rsym->isUndef() && !rsym->isDyn() && !rsym->isWeak() && !rsym->isNull()) fatal(diag::undefined_reference) << rsym->name(); } bool MipsRelocator::initializeScan(Input& pInput) { getTarget().getGOT().initializeScan(pInput); return true; } bool MipsRelocator::finalizeScan(Input& pInput) { 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(Relocation& pReloc, IRBuilder& pBuilder, const LDSection& pSection) { ResolveInfo* rsym = pReloc.symInfo(); switch (pReloc.type()){ case llvm::ELF::R_MIPS_NONE: case llvm::ELF::R_MIPS_16: break; case llvm::ELF::R_MIPS_32: if (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()); // Remeber this rsym is a local GOT entry (as if it needs an entry). // Actually we don't allocate an GOT entry. getTarget().getGOT().setLocal(rsym); } 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_PC16: case llvm::ELF::R_MIPS_SHIFT5: case llvm::ELF::R_MIPS_SHIFT6: case llvm::ELF::R_MIPS_64: case llvm::ELF::R_MIPS_GOT_PAGE: case llvm::ELF::R_MIPS_GOT_OFST: 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: if (getTarget().getGOT().reserveLocalEntry(*rsym)) { if (getTarget().getGOT().hasMultipleGOT()) getTarget().checkAndSetHasTextRel(*pSection.getLink()); // Remeber this rsym is a local GOT entry getTarget().getGOT().setLocal(rsym); } break; case llvm::ELF::R_MIPS_GPREL32: case llvm::ELF::R_MIPS_GPREL16: case llvm::ELF::R_MIPS_LITERAL: case llvm::ELF::R_MIPS_GOT_DISP: 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_GD: case llvm::ELF::R_MIPS_TLS_LDM: case llvm::ELF::R_MIPS_TLS_DTPREL_HI16: case llvm::ELF::R_MIPS_TLS_DTPREL_LO16: case llvm::ELF::R_MIPS_TLS_GOTTPREL: 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; default: fatal(diag::unknown_relocation) << (int)pReloc.type() << pReloc.symInfo()->name(); } } void MipsRelocator::scanGlobalReloc(Relocation& pReloc, IRBuilder& pBuilder, const LDSection& pSection) { ResolveInfo* rsym = pReloc.symInfo(); 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: case llvm::ELF::R_MIPS_HI16: case llvm::ELF::R_MIPS_LO16: if (getTarget().symbolNeedsDynRel(*rsym, false, true)) { getTarget().getRelDyn().reserveEntry(); rsym->setReserved(rsym->reserved() | ReserveRel); getTarget().checkAndSetHasTextRel(*pSection.getLink()); // Remeber this rsym is a global GOT entry (as if it needs an entry). // Actually we don't allocate an GOT entry. getTarget().getGOT().setGlobal(rsym); } 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()); // Remeber this rsym is a global GOT entry getTarget().getGOT().setGlobal(rsym); } break; case llvm::ELF::R_MIPS_LITERAL: case llvm::ELF::R_MIPS_GPREL32: fatal(diag::invalid_global_relocation) << (int)pReloc.type() << pReloc.symInfo()->name(); break; case llvm::ELF::R_MIPS_GPREL16: break; case llvm::ELF::R_MIPS_26: case llvm::ELF::R_MIPS_PC16: 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_DTPREL32: case llvm::ELF::R_MIPS_TLS_GD: case llvm::ELF::R_MIPS_TLS_LDM: case llvm::ELF::R_MIPS_TLS_DTPREL_HI16: case llvm::ELF::R_MIPS_TLS_DTPREL_LO16: case llvm::ELF::R_MIPS_TLS_GOTTPREL: 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: break; case llvm::ELF::R_MIPS_JALR: 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) << (int)pReloc.type(); break; default: fatal(diag::unknown_relocation) << (int)pReloc.type() << pReloc.symInfo()->name(); } } //===----------------------------------------------------------------------===// // Relocation helper function //===----------------------------------------------------------------------===// static const char * const GP_DISP_NAME = "_gp_disp"; // Find next R_MIPS_LO16 relocation paired to pReloc. static Relocation* helper_FindLo16Reloc(Relocation& pReloc) { Relocation* reloc = static_cast<Relocation*>(pReloc.getNextNode()); while (NULL != reloc) { if (llvm::ELF::R_MIPS_LO16 == reloc->type() && reloc->symInfo() == pReloc.symInfo()) return reloc; reloc = static_cast<Relocation*>(reloc->getNextNode()); } return NULL; } // Check the symbol is _gp_disp. static bool helper_isGpDisp(const Relocation& pReloc) { const ResolveInfo* rsym = pReloc.symInfo(); return 0 == strcmp(GP_DISP_NAME, rsym->name()); } static Relocator::Address helper_GetGP(MipsRelocator& pParent) { return pParent.getTarget().getGOT().getGPAddr(pParent.getApplyingInput()); } static void helper_SetupRelDynForGOTEntry(MipsGOTEntry& got_entry, Relocation& pReloc, ResolveInfo* rsym, MipsRelocator& pParent) { MipsGNULDBackend& ld_backend = pParent.getTarget(); Relocation& rel_entry = *ld_backend.getRelDyn().consumeEntry(); rel_entry.setType(llvm::ELF::R_MIPS_REL32); rel_entry.targetRef() = *FragmentRef::Create(got_entry, 0); rel_entry.setSymInfo(rsym); } static MipsGOTEntry& helper_GetGOTEntry(Relocation& pReloc, MipsRelocator& pParent) { // rsym - The relocation target symbol ResolveInfo* rsym = pReloc.symInfo(); MipsGNULDBackend& ld_backend = pParent.getTarget(); MipsGOT& got = ld_backend.getGOT(); MipsGOTEntry* got_entry; if (got.isLocal(rsym) && ResolveInfo::Section == rsym->type()) { // Local section symbols consume local got entries. got_entry = got.consumeLocal(); if (got.isPrimaryGOTConsumed()) helper_SetupRelDynForGOTEntry(*got_entry, pReloc, NULL, pParent); return *got_entry; } got_entry = got.lookupEntry(rsym); if (NULL != got_entry) { // found a mapping, then return the mapped entry immediately return *got_entry; } // not found if (got.isLocal(rsym)) got_entry = got.consumeLocal(); else got_entry = got.consumeGlobal(); got.recordEntry(rsym, got_entry); // If we first get this GOT entry, we should initialize it. if (!got.isLocal(rsym) || ResolveInfo::Section != rsym->type()) { if (!got.isPrimaryGOTConsumed()) got_entry->setValue(pReloc.symValue()); } if (got.isPrimaryGOTConsumed()) helper_SetupRelDynForGOTEntry(*got_entry, pReloc, got.isLocal(rsym) ? NULL : rsym, pParent); return *got_entry; } static Relocator::Address helper_GetGOTOffset(Relocation& pReloc, MipsRelocator& pParent) { MipsGNULDBackend& ld_backend = pParent.getTarget(); MipsGOT& got = ld_backend.getGOT(); MipsGOTEntry& got_entry = helper_GetGOTEntry(pReloc, pParent); return got.getGPRelOffset(pParent.getApplyingInput(), got_entry); } static int32_t helper_CalcAHL(const Relocation& pHiReloc, const Relocation& pLoReloc) { assert((pHiReloc.type() == llvm::ELF::R_MIPS_HI16 || pHiReloc.type() == llvm::ELF::R_MIPS_GOT16) && pLoReloc.type() == llvm::ELF::R_MIPS_LO16 && "Incorrect type of relocation for AHL calculation"); // Note the addend is section symbol offset here assert (pHiReloc.addend() == pLoReloc.addend()); int32_t AHI = pHiReloc.target(); int32_t ALO = pLoReloc.target(); int32_t AHL = ((AHI & 0xFFFF) << 16) + (int16_t)(ALO & 0xFFFF) + pLoReloc.addend(); return AHL; } static void helper_DynRel(Relocation& pReloc, MipsRelocator& pParent) { ResolveInfo* rsym = pReloc.symInfo(); MipsGNULDBackend& ld_backend = pParent.getTarget(); MipsGOT& got = ld_backend.getGOT(); Relocation& rel_entry = *ld_backend.getRelDyn().consumeEntry(); rel_entry.setType(llvm::ELF::R_MIPS_REL32); rel_entry.targetRef() = pReloc.targetRef(); Relocator::DWord A = pReloc.target() + pReloc.addend(); Relocator::DWord S = pReloc.symValue(); if (got.isLocal(rsym)) { rel_entry.setSymInfo(NULL); pReloc.target() = A + S; } else { rel_entry.setSymInfo(rsym); // Don't add symbol value that will be resolved by the dynamic linker pReloc.target() = A; } } //=========================================// // Relocation functions implementation // //=========================================// // R_MIPS_NONE and those unsupported/deprecated relocation type static MipsRelocator::Result none(Relocation& pReloc, MipsRelocator& pParent) { return MipsRelocator::OK; } // R_MIPS_32: S + A static MipsRelocator::Result abs32(Relocation& pReloc, MipsRelocator& pParent) { ResolveInfo* rsym = pReloc.symInfo(); Relocator::DWord A = pReloc.target() + pReloc.addend(); Relocator::DWord S = pReloc.symValue(); LDSection& target_sect = pReloc.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.target() = S + A; return MipsRelocator::OK; } if (rsym->reserved() & MipsRelocator::ReserveRel) { helper_DynRel(pReloc, pParent); return MipsRelocator::OK; } pReloc.target() = (S + A); return MipsRelocator::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(Relocation& pReloc, MipsRelocator& pParent) { Relocation* lo_reloc = helper_FindLo16Reloc(pReloc); assert(NULL != lo_reloc && "There is no paired R_MIPS_LO16 for R_MIPS_HI16"); int32_t AHL = helper_CalcAHL(pReloc, *lo_reloc); int32_t res = 0; pParent.setAHL(AHL); if (helper_isGpDisp(pReloc)) { int32_t P = pReloc.place(); int32_t GP = helper_GetGP(pParent); res = ((AHL + GP - P) - (int16_t)(AHL + GP - P)) >> 16; } else { int32_t S = pReloc.symValue(); res = ((AHL + S) - (int16_t)(AHL + S)) >> 16; } pReloc.target() &= 0xFFFF0000; pReloc.target() |= (res & 0xFFFF); return MipsRelocator::OK; } // R_MIPS_LO16: // local/external: AHL + S // _gp_disp : AHL + GP - P + 4 static MipsRelocator::Result lo16(Relocation& pReloc, MipsRelocator& pParent) { int32_t res = 0; if (helper_isGpDisp(pReloc)) { int32_t P = pReloc.place(); int32_t GP = helper_GetGP(pParent); int32_t AHL = pParent.getAHL(); res = AHL + GP - P + 4; } else { int32_t S = pReloc.symValue(); // The previous AHL may be for other hi/lo pairs. // We need to calcuate the lo part now. It is easy. // Remember to add the section offset to ALO. int32_t ALO = (pReloc.target() & 0xFFFF) + pReloc.addend(); res = ALO + S; } pReloc.target() &= 0xFFFF0000; pReloc.target() |= (res & 0xFFFF); return MipsRelocator::OK; } // R_MIPS_GOT16: // local : G (calculate AHL and put high 16 bit to GOT) // external: G static MipsRelocator::Result got16(Relocation& pReloc, MipsRelocator& pParent) { MipsGNULDBackend& ld_backend = pParent.getTarget(); MipsGOT& got = ld_backend.getGOT(); ResolveInfo* rsym = pReloc.symInfo(); Relocator::Address G = 0; if (rsym->isLocal()) { Relocation* lo_reloc = helper_FindLo16Reloc(pReloc); assert(NULL != lo_reloc && "There is no paired R_MIPS_LO16 for R_MIPS_GOT16"); int32_t AHL = helper_CalcAHL(pReloc, *lo_reloc); int32_t S = pReloc.symValue(); pParent.setAHL(AHL); int32_t res = (AHL + S + 0x8000) & 0xFFFF0000; MipsGOTEntry& got_entry = helper_GetGOTEntry(pReloc, pParent); got_entry.setValue(res); G = got.getGPRelOffset(pParent.getApplyingInput(), got_entry); } else { G = helper_GetGOTOffset(pReloc, pParent); } pReloc.target() &= 0xFFFF0000; pReloc.target() |= (G & 0xFFFF); return MipsRelocator::OK; } // R_MIPS_GOTHI16: // external: (G - (short)G) >> 16 + A static MipsRelocator::Result gothi16(Relocation& pReloc, MipsRelocator& pParent) { int32_t res = 0; Relocator::Address G = helper_GetGOTOffset(pReloc, pParent); int32_t A = pReloc.target() + pReloc.addend(); res = (G - (int16_t)G) >> (16 + A); pReloc.target() &= 0xFFFF0000; pReloc.target() |= (res & 0xFFFF); return MipsRelocator::OK; } // R_MIPS_GOTLO16: // external: G & 0xffff static MipsRelocator::Result gotlo16(Relocation& pReloc, MipsRelocator& pParent) { Relocator::Address G = helper_GetGOTOffset(pReloc, pParent); pReloc.target() &= 0xFFFF0000; pReloc.target() |= (G & 0xFFFF); return MipsRelocator::OK; } // R_MIPS_CALL16: G static MipsRelocator::Result call16(Relocation& pReloc, MipsRelocator& pParent) { Relocator::Address G = helper_GetGOTOffset(pReloc, pParent); pReloc.target() &= 0xFFFF0000; pReloc.target() |= (G & 0xFFFF); return MipsRelocator::OK; } // R_MIPS_GPREL32: A + S + GP0 - GP static MipsRelocator::Result gprel32(Relocation& pReloc, MipsRelocator& pParent) { // Remember to add the section offset to A. int32_t A = pReloc.target() + pReloc.addend(); int32_t S = pReloc.symValue(); int32_t GP = helper_GetGP(pParent); // llvm does not emits SHT_MIPS_REGINFO section. // Assume that GP0 is zero. pReloc.target() = (A + S - GP) & 0xFFFFFFFF; return MipsRelocator::OK; }