aboutsummaryrefslogtreecommitdiff
path: root/lld/ELF/Arch
diff options
context:
space:
mode:
Diffstat (limited to 'lld/ELF/Arch')
-rw-r--r--lld/ELF/Arch/Mips.cpp121
-rw-r--r--lld/ELF/Arch/PPC64.cpp131
2 files changed, 252 insertions, 0 deletions
diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index f88b021c..091903b 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
+#include "RelocScan.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
@@ -31,6 +32,9 @@ public:
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;
@@ -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 {
diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index 550c091..6528cc5 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -8,6 +8,7 @@
#include "InputFiles.h"
#include "OutputSections.h"
+#include "RelocScan.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
@@ -178,6 +179,10 @@ public:
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;
@@ -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);