android13/frameworks/compile/mclinker/lib/Target/Mips/MipsRelocator.cpp

1225 lines
40 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===- 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: (signextend(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: signextend(A) + S - GP
// local : signextend(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