//===- 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