Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions lld/ELF/Arch/Mips.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "InputFiles.h"
#include "RelocScan.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
Expand All @@ -31,6 +32,9 @@ template <class ELFT> class MIPS final : public TargetInfo {
void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
template <class RelTy>
void scanSectionImpl(InputSectionBase &, Relocs<RelTy>);
void scanSection(InputSectionBase &) override;
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
uint64_t branchAddr, const Symbol &s,
int64_t a) const override;
Expand Down Expand Up @@ -570,6 +574,123 @@ static uint64_t fixupCrossModeJump(Ctx &ctx, uint8_t *loc, RelType type,
return val;
}

template <class RelTy>
static RelType getMipsN32RelType(Ctx &ctx, RelTy *&rel, RelTy *end) {
uint32_t type = 0;
uint64_t offset = rel->r_offset;
int n = 0;
while (rel != end && rel->r_offset == offset)
type |= (rel++)->getType(ctx.arg.isMips64EL) << (8 * n++);
return type;
}

static RelType getMipsPairType(RelType type, bool isLocal) {
switch (type) {
case R_MIPS_HI16:
return R_MIPS_LO16;
case R_MIPS_GOT16:
// In case of global symbol, the R_MIPS_GOT16 relocation does not
// have a pair. Each global symbol has a unique entry in the GOT
// and a corresponding instruction with help of the R_MIPS_GOT16
// relocation loads an address of the symbol. In case of local
// symbol, the R_MIPS_GOT16 relocation creates a GOT entry to hold
// the high 16 bits of the symbol's value. A paired R_MIPS_LO16
// relocations handle low 16 bits of the address. That allows
// to allocate only one GOT entry for every 64 KiB of local data.
return isLocal ? R_MIPS_LO16 : R_MIPS_NONE;
case R_MICROMIPS_GOT16:
return isLocal ? R_MICROMIPS_LO16 : R_MIPS_NONE;
case R_MIPS_PCHI16:
return R_MIPS_PCLO16;
case R_MICROMIPS_HI16:
return R_MICROMIPS_LO16;
default:
return R_MIPS_NONE;
}
}

template <class ELFT>
template <class RelTy>
void MIPS<ELFT>::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
RelocScan rs(ctx, &sec);
sec.relocations.reserve(rels.size());
RelType type;
for (auto it = rels.begin(); it != rels.end();) {
const RelTy &rel = *it;
uint64_t offset = rel.r_offset;
if constexpr (ELFT::Is64Bits) {
type = it->getType(ctx.arg.isMips64EL);
++it;
} else {
if (ctx.arg.mipsN32Abi) {
type = getMipsN32RelType(ctx, it, rels.end());
} else {
type = it->getType(ctx.arg.isMips64EL);
++it;
}
}

uint32_t symIdx = rel.getSymbol(ctx.arg.isMips64EL);
Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIdx);
RelExpr expr =
ctx.target->getRelExpr(type, sym, sec.content().data() + rel.r_offset);
if (expr == R_NONE)
continue;
if (sym.isUndefined() && symIdx != 0 &&
rs.maybeReportUndefined(cast<Undefined>(sym), offset))
continue;

auto addend = rs.getAddend<ELFT>(rel, type);
if (expr == RE_MIPS_GOTREL && sym.isLocal()) {
addend += sec.getFile<ELFT>()->mipsGp0;
} else if (!RelTy::HasAddend) {
// MIPS has an odd notion of "paired" relocations to calculate addends.
// For example, if a relocation is of R_MIPS_HI16, there must be a
// R_MIPS_LO16 relocation after that, and an addend is calculated using
// the two relocations.
RelType pairTy = getMipsPairType(type, sym.isLocal());
if (pairTy != R_MIPS_NONE) {
const uint8_t *buf = sec.content().data();
// To make things worse, paired relocations might not be contiguous in
// the relocation table, so we need to do linear search. *sigh*
bool found = false;
for (auto *ri = &rel; ri != rels.end(); ++ri) {
if (ri->getType(ctx.arg.isMips64EL) == pairTy &&
ri->getSymbol(ctx.arg.isMips64EL) == symIdx) {
addend += ctx.target->getImplicitAddend(buf + ri->r_offset, pairTy);
found = true;
break;
}
}

if (!found)
Warn(ctx) << "can't find matching " << pairTy << " relocation for "
<< type;
}
}

if (expr == RE_MIPS_TLSLD) {
ctx.in.mipsGot->addTlsIndex(*sec.file);
sec.addReloc({expr, type, offset, addend, &sym});
} else if (expr == RE_MIPS_TLSGD) {
ctx.in.mipsGot->addDynTlsEntry(*sec.file, sym);
sec.addReloc({expr, type, offset, addend, &sym});
} else {
if (expr == R_TPREL && rs.checkTlsLe(offset, sym, type))
continue;
rs.process(expr, type, offset, sym, addend);
}
}
}

template <class ELFT> void MIPS<ELFT>::scanSection(InputSectionBase &sec) {
auto relocs = sec.template relsOrRelas<ELFT>();
if (relocs.areRelocsRel())
scanSectionImpl(sec, relocs.rels);
else
scanSectionImpl(sec, relocs.relas);
}

template <class ELFT>
void MIPS<ELFT>::relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
Expand Down
131 changes: 131 additions & 0 deletions lld/ELF/Arch/PPC64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "InputFiles.h"
#include "OutputSections.h"
#include "RelocScan.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
Expand Down Expand Up @@ -178,6 +179,10 @@ class PPC64 final : public TargetInfo {
uint64_t pltEntryAddr) const override;
void writeIplt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
template <class ELFT, class RelTy>
void scanSectionImpl(InputSectionBase &, Relocs<RelTy>);
template <class ELFT> void scanSection1(InputSectionBase &);
void scanSection(InputSectionBase &) override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void writeGotHeader(uint8_t *buf) const override;
Expand Down Expand Up @@ -1257,6 +1262,132 @@ static bool isTocOptType(RelType type) {
}
}

// R_PPC64_TLSGD/R_PPC64_TLSLD is required to mark `bl __tls_get_addr` for
// General Dynamic/Local Dynamic code sequences. If a GD/LD GOT relocation is
// found but no R_PPC64_TLSGD/R_PPC64_TLSLD is seen, we assume that the
// instructions are generated by very old IBM XL compilers. Work around the
// issue by disabling GD/LD to IE/LE relaxation.
template <class RelTy>
static void checkPPC64TLSRelax(InputSectionBase &sec, Relocs<RelTy> rels) {
// Skip if sec is synthetic (sec.file is null) or if sec has been marked.
if (!sec.file || sec.file->ppc64DisableTLSRelax)
return;
bool hasGDLD = false;
for (const RelTy &rel : rels) {
RelType type = rel.getType(false);
switch (type) {
case R_PPC64_TLSGD:
case R_PPC64_TLSLD:
return; // Found a marker
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_HA:
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_GOT_TLSGD16_LO:
case R_PPC64_GOT_TLSLD16:
case R_PPC64_GOT_TLSLD16_HA:
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_GOT_TLSLD16_LO:
hasGDLD = true;
break;
}
}
if (hasGDLD) {
sec.file->ppc64DisableTLSRelax = true;
Warn(sec.file->ctx)
<< sec.file
<< ": disable TLS relaxation due to R_PPC64_GOT_TLS* relocations "
"without "
"R_PPC64_TLSGD/R_PPC64_TLSLD relocations";
}
}

template <class ELFT, class RelTy>
void PPC64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
RelocScan rs(ctx, &sec);
sec.relocations.reserve(rels.size());
checkPPC64TLSRelax<RelTy>(sec, rels);
for (auto it = rels.begin(); it != rels.end(); ++it) {
const RelTy &rel = *it;
uint64_t offset = rel.r_offset;
uint32_t symIdx = rel.getSymbol(false);
Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIdx);
RelType type = rel.getType(false);
RelExpr expr =
ctx.target->getRelExpr(type, sym, sec.content().data() + offset);
if (expr == R_NONE)
continue;
if (sym.isUndefined() && symIdx != 0 &&
rs.maybeReportUndefined(cast<Undefined>(sym), offset))
continue;

auto addend = getAddend<ELFT>(rel);
if (ctx.arg.isPic && type == R_PPC64_TOC)
addend += getPPC64TocBase(ctx);

// We can separate the small code model relocations into 2 categories:
// 1) Those that access the compiler generated .toc sections.
// 2) Those that access the linker allocated got entries.
// lld allocates got entries to symbols on demand. Since we don't try to
// sort the got entries in any way, we don't have to track which objects
// have got-based small code model relocs. The .toc sections get placed
// after the end of the linker allocated .got section and we do sort those
// so sections addressed with small code model relocations come first.
if (type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS)
sec.file->ppc64SmallCodeModelTocRelocs = true;

// Record the TOC entry (.toc + addend) as not relaxable. See the comment in
// PPC64::relocateAlloc().
if (type == R_PPC64_TOC16_LO && sym.isSection() && isa<Defined>(sym) &&
cast<Defined>(sym).section->name == ".toc")
ctx.ppc64noTocRelax.insert({&sym, addend});

if ((type == R_PPC64_TLSGD && expr == R_TLSDESC_CALL) ||
(type == R_PPC64_TLSLD && expr == R_TLSLD_HINT)) {
auto it1 = it;
++it1;
if (it1 == rels.end()) {
auto diag = Err(ctx);
diag << "R_PPC64_TLSGD/R_PPC64_TLSLD may not be the last "
"relocation";
printLocation(diag, sec, sym, offset);
continue;
}

// Offset the 4-byte aligned R_PPC64_TLSGD by one byte in the NOTOC
// case, so we can discern it later from the toc-case.
if (it1->getType(/*isMips64EL=*/false) == R_PPC64_REL24_NOTOC)
++offset;
}

if (oneof<R_GOTREL, RE_PPC64_TOCBASE, RE_PPC64_RELAX_TOC>(expr))
ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);

if (sym.isTls()) {
if (unsigned processed =
rs.handleTlsRelocation(expr, type, offset, sym, addend)) {
it += processed - 1;
continue;
}
}
rs.process(expr, type, offset, sym, addend);
}
}

template <class ELFT> void PPC64::scanSection1(InputSectionBase &sec) {
auto relocs = sec.template relsOrRelas<ELFT>();
if (relocs.areRelocsCrel())
scanSectionImpl<ELFT>(sec, relocs.crels);
else
scanSectionImpl<ELFT>(sec, relocs.relas);
}

void PPC64::scanSection(InputSectionBase &sec) {
if (ctx.arg.isLE)
scanSection1<ELF64LE>(sec);
else
scanSection1<ELF64BE>(sec);
}

void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
RelType type = rel.type;
bool shouldTocOptimize = isTocOptType(type);
Expand Down
Loading