aboutsummaryrefslogtreecommitdiff
path: root/lld
diff options
context:
space:
mode:
Diffstat (limited to 'lld')
-rw-r--r--lld/ELF/Arch/Mips.cpp121
-rw-r--r--lld/ELF/Arch/PPC64.cpp131
-rw-r--r--lld/ELF/RelocScan.h126
-rw-r--r--lld/ELF/Relocations.cpp404
-rw-r--r--lld/ELF/Relocations.h7
-rw-r--r--lld/ELF/Target.cpp1
-rw-r--r--lld/ELF/Target.h10
7 files changed, 446 insertions, 354 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);
diff --git a/lld/ELF/RelocScan.h b/lld/ELF/RelocScan.h
new file mode 100644
index 0000000..43f337d
--- /dev/null
+++ b/lld/ELF/RelocScan.h
@@ -0,0 +1,126 @@
+//===------------------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_RELOCSCAN_H
+#define LLD_ELF_RELOCSCAN_H
+
+#include "Config.h"
+#include "InputFiles.h"
+#include "InputSection.h"
+#include "Relocations.h"
+#include "SyntheticSections.h"
+#include "Target.h"
+
+using namespace llvm;
+using namespace llvm::ELF;
+using namespace llvm::object;
+
+namespace lld::elf {
+
+// Build a bitmask with one bit set for each 64 subset of RelExpr.
+inline constexpr uint64_t buildMask() { return 0; }
+
+template <typename... Tails>
+inline constexpr uint64_t buildMask(int head, Tails... tails) {
+ return (0 <= head && head < 64 ? uint64_t(1) << head : 0) |
+ buildMask(tails...);
+}
+
+// Return true if `Expr` is one of `Exprs`.
+// There are more than 64 but less than 128 RelExprs, so we divide the set of
+// exprs into [0, 64) and [64, 128) and represent each range as a constant
+// 64-bit mask. Then we decide which mask to test depending on the value of
+// expr and use a simple shift and bitwise-and to test for membership.
+template <RelExpr... Exprs> bool oneof(RelExpr expr) {
+ assert(0 <= expr && (int)expr < 128 &&
+ "RelExpr is too large for 128-bit mask!");
+
+ if (expr >= 64)
+ return (uint64_t(1) << (expr - 64)) & buildMask((Exprs - 64)...);
+ return (uint64_t(1) << expr) & buildMask(Exprs...);
+}
+
+// This class encapsulates states needed to scan relocations for one
+// InputSectionBase.
+class RelocScan {
+public:
+ Ctx &ctx;
+ InputSectionBase *sec;
+
+ RelocScan(Ctx &ctx, InputSectionBase *sec = nullptr) : ctx(ctx), sec(sec) {}
+ template <class ELFT, class RelTy>
+ void scan(typename Relocs<RelTy>::const_iterator &i, RelType type,
+ int64_t addend);
+ void scanEhSection(EhInputSection &s);
+
+ template <class ELFT, class RelTy>
+ int64_t getAddend(const RelTy &r, RelType type);
+ bool maybeReportUndefined(Undefined &sym, uint64_t offset);
+ bool checkTlsLe(uint64_t offset, Symbol &sym, RelType type);
+ bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
+ uint64_t relOff) const;
+ void process(RelExpr expr, RelType type, uint64_t offset, Symbol &sym,
+ int64_t addend) const;
+ unsigned handleTlsRelocation(RelExpr expr, RelType type, uint64_t offset,
+ Symbol &sym, int64_t addend);
+};
+
+template <class ELFT, class RelTy>
+int64_t RelocScan::getAddend(const RelTy &r, RelType type) {
+ return RelTy::HasAddend ? elf::getAddend<ELFT>(r)
+ : ctx.target->getImplicitAddend(
+ sec->content().data() + r.r_offset, type);
+}
+
+template <class ELFT, class RelTy>
+void RelocScan::scan(typename Relocs<RelTy>::const_iterator &it, RelType type,
+ int64_t addend) {
+ const RelTy &rel = *it;
+ uint32_t symIdx = rel.getSymbol(false);
+ Symbol &sym = sec->getFile<ELFT>()->getSymbol(symIdx);
+ uint64_t offset = rel.r_offset;
+ RelExpr expr =
+ ctx.target->getRelExpr(type, sym, sec->content().data() + offset);
+
+ // Ignore R_*_NONE and other marker relocations.
+ if (expr == R_NONE)
+ return;
+
+ // Error if the target symbol is undefined. Symbol index 0 may be used by
+ // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them.
+ if (sym.isUndefined() && symIdx != 0 &&
+ maybeReportUndefined(cast<Undefined>(sym), offset))
+ return;
+
+ // Ensure GOT or GOTPLT is created for relocations that reference their base
+ // addresses without directly creating entries.
+ if (oneof<R_GOTPLTONLY_PC, R_GOTPLTREL, R_GOTPLT, R_PLT_GOTPLT,
+ R_TLSDESC_GOTPLT, R_TLSGD_GOTPLT>(expr)) {
+ ctx.in.gotPlt->hasGotPltOffRel.store(true, std::memory_order_relaxed);
+ } else if (oneof<R_GOTONLY_PC, R_GOTREL, RE_PPC32_PLTREL>(expr)) {
+ ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
+ }
+
+ // Process TLS relocations, including TLS optimizations. Note that
+ // R_TPREL and R_TPREL_NEG relocations are resolved in processAux.
+ //
+ // Some RISCV TLSDESC relocations reference a local NOTYPE symbol,
+ // but we need to process them in handleTlsRelocation.
+ if (sym.isTls() || oneof<R_TLSDESC_PC, R_TLSDESC_CALL>(expr)) {
+ if (unsigned processed =
+ handleTlsRelocation(expr, type, offset, sym, addend)) {
+ it += processed - 1;
+ return;
+ }
+ }
+
+ process(expr, type, offset, sym, addend);
+}
+} // namespace lld::elf
+
+#endif
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 84b9b5e..d21376f 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -30,6 +30,7 @@
#include "InputFiles.h"
#include "LinkerScript.h"
#include "OutputSections.h"
+#include "RelocScan.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
@@ -58,8 +59,8 @@ static void printDefinedLocation(ELFSyncStream &s, const Symbol &sym) {
// >>> defined in /home/alice/src/foo.o
// >>> referenced by bar.c:12 (/home/alice/src/bar.c:12)
// >>> /home/alice/src/bar.o:(.text+0x1)
-static void printLocation(ELFSyncStream &s, InputSectionBase &sec,
- const Symbol &sym, uint64_t off) {
+void elf::printLocation(ELFSyncStream &s, InputSectionBase &sec,
+ const Symbol &sym, uint64_t off) {
printDefinedLocation(s, sym);
s << "\n>>> referenced by ";
auto tell = s.tell();
@@ -111,54 +112,6 @@ void elf::reportRangeError(Ctx &ctx, uint8_t *loc, int64_t v, int n,
}
}
-// Build a bitmask with one bit set for each 64 subset of RelExpr.
-static constexpr uint64_t buildMask() { return 0; }
-
-template <typename... Tails>
-static constexpr uint64_t buildMask(int head, Tails... tails) {
- return (0 <= head && head < 64 ? uint64_t(1) << head : 0) |
- buildMask(tails...);
-}
-
-// Return true if `Expr` is one of `Exprs`.
-// There are more than 64 but less than 128 RelExprs, so we divide the set of
-// exprs into [0, 64) and [64, 128) and represent each range as a constant
-// 64-bit mask. Then we decide which mask to test depending on the value of
-// expr and use a simple shift and bitwise-and to test for membership.
-template <RelExpr... Exprs> static bool oneof(RelExpr expr) {
- assert(0 <= expr && (int)expr < 128 &&
- "RelExpr is too large for 128-bit mask!");
-
- if (expr >= 64)
- return (uint64_t(1) << (expr - 64)) & buildMask((Exprs - 64)...);
- return (uint64_t(1) << expr) & buildMask(Exprs...);
-}
-
-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;
- }
-}
-
// True if non-preemptable symbol always has the same value regardless of where
// the DSO is loaded.
bool elf::isAbsolute(const Symbol &sym) {
@@ -424,73 +377,8 @@ private:
ArrayRef<EhSectionPiece> cies, fdes;
ArrayRef<EhSectionPiece>::iterator i, j;
};
-
-// This class encapsulates states needed to scan relocations for one
-// InputSectionBase.
-class RelocationScanner {
-public:
- RelocationScanner(Ctx &ctx) : ctx(ctx) {}
- template <class ELFT> void scanSection(InputSectionBase &s);
- template <class ELFT> void scanEhSection(EhInputSection &s);
-
-private:
- Ctx &ctx;
- InputSectionBase *sec;
-
- // End of relocations, used by Mips/PPC64.
- const void *end = nullptr;
-
- template <class RelTy> RelType getMipsN32RelType(RelTy *&rel) const;
- template <class ELFT, class RelTy>
- int64_t computeMipsAddend(const RelTy &rel, RelExpr expr, bool isLocal) const;
- bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
- uint64_t relOff) const;
- void process(RelExpr expr, RelType type, uint64_t offset, Symbol &sym,
- int64_t addend) const;
- unsigned handleTlsRelocation(RelExpr expr, RelType type, uint64_t offset,
- Symbol &sym, int64_t addend);
-
- template <class ELFT, class RelTy>
- void scan(typename Relocs<RelTy>::const_iterator &i);
- template <class ELFT, class RelTy> void scanSectionImpl(Relocs<RelTy> rels);
-};
} // namespace
-// 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.
-template <class ELFT, class RelTy>
-int64_t RelocationScanner::computeMipsAddend(const RelTy &rel, RelExpr expr,
- bool isLocal) const {
- if (expr == RE_MIPS_GOTREL && isLocal)
- return sec->getFile<ELFT>()->mipsGp0;
-
- // The ABI says that the paired relocation is used only for REL.
- // See p. 4-17 at ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- // This generalises to relocation types with implicit addends.
- if (RelTy::HasAddend)
- return 0;
-
- RelType type = rel.getType(ctx.arg.isMips64EL);
- RelType pairTy = getMipsPairType(type, isLocal);
- if (pairTy == R_MIPS_NONE)
- return 0;
-
- const uint8_t *buf = sec->content().data();
- uint32_t symIndex = rel.getSymbol(ctx.arg.isMips64EL);
-
- // To make things worse, paired relocations might not be contiguous in
- // the relocation table, so we need to do linear search. *sigh*
- for (const RelTy *ri = &rel; ri != static_cast<const RelTy *>(end); ++ri)
- if (ri->getType(ctx.arg.isMips64EL) == pairTy &&
- ri->getSymbol(ctx.arg.isMips64EL) == symIndex)
- return ctx.target->getImplicitAddend(buf + ri->r_offset, pairTy);
-
- Warn(ctx) << "can't find matching " << pairTy << " relocation for " << type;
- return 0;
-}
-
// Custom error message if Sym is defined in a discarded section.
template <class ELFT>
static void maybeReportDiscarded(Ctx &ctx, ELFSyncStream &msg, Undefined &sym) {
@@ -767,13 +655,12 @@ void elf::reportUndefinedSymbols(Ctx &ctx) {
// Report an undefined symbol if necessary.
// Returns true if the undefined symbol will produce an error message.
-static bool maybeReportUndefined(Ctx &ctx, Undefined &sym,
- InputSectionBase &sec, uint64_t offset) {
+bool RelocScan::maybeReportUndefined(Undefined &sym, uint64_t offset) {
std::lock_guard<std::mutex> lock(ctx.relocMutex);
// If versioned, issue an error (even if the symbol is weak) because we don't
// know the defining filename which is required to construct a Verneed entry.
if (sym.hasVersionSuffix) {
- ctx.undefErrs.push_back({&sym, {{&sec, offset}}, false});
+ ctx.undefErrs.push_back({&sym, {{sec, offset}}, false});
return true;
}
if (sym.isWeak())
@@ -792,30 +679,24 @@ static bool maybeReportUndefined(Ctx &ctx, Undefined &sym,
// PPC32 .got2 is similar but cannot be fixed. Multiple .got2 is infeasible
// because .LC0-.LTOC is not representable if the two labels are in different
// .got2
- if (sym.discardedSecIdx != 0 && (sec.name == ".got2" || sec.name == ".toc"))
+ if (sym.discardedSecIdx != 0 && (sec->name == ".got2" || sec->name == ".toc"))
return false;
bool isWarning =
(ctx.arg.unresolvedSymbols == UnresolvedPolicy::Warn && canBeExternal) ||
ctx.arg.noinhibitExec;
- ctx.undefErrs.push_back({&sym, {{&sec, offset}}, isWarning});
+ ctx.undefErrs.push_back({&sym, {{sec, offset}}, isWarning});
return !isWarning;
}
-// MIPS N32 ABI treats series of successive relocations with the same offset
-// as a single relocation. The similar approach used by N64 ABI, but this ABI
-// packs all relocations into the single relocation record. Here we emulate
-// this for the N32 ABI. Iterate over relocation with the same offset and put
-// theirs types into the single bit-set.
-template <class RelTy>
-RelType RelocationScanner::getMipsN32RelType(RelTy *&rel) const {
- uint32_t type = 0;
- uint64_t offset = rel->r_offset;
-
- int n = 0;
- while (rel != static_cast<const RelTy *>(end) && rel->r_offset == offset)
- type |= (rel++)->getType(ctx.arg.isMips64EL) << (8 * n++);
- return type;
+bool RelocScan::checkTlsLe(uint64_t offset, Symbol &sym, RelType type) {
+ if (!ctx.arg.shared)
+ return false;
+ auto diag = Err(ctx);
+ diag << "relocation " << type << " against " << &sym
+ << " cannot be used with -shared";
+ printLocation(diag, *sec, sym, offset);
+ return true;
}
template <bool shard = false>
@@ -949,9 +830,9 @@ static bool canDefineSymbolInExecutable(Ctx &ctx, Symbol &sym) {
//
// If this function returns false, that means we need to emit a
// dynamic relocation so that the relocation will be fixed at load-time.
-bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
- const Symbol &sym,
- uint64_t relOff) const {
+bool RelocScan::isStaticLinkTimeConstant(RelExpr e, RelType type,
+ const Symbol &sym,
+ uint64_t relOff) const {
// These expressions always compute a constant
if (oneof<
R_GOTPLT, R_GOT_OFF, R_RELAX_HINT, RE_MIPS_GOT_LOCAL_PAGE,
@@ -1010,7 +891,7 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
// We set the final symbols values for linker script defined symbols later.
// They always can be computed as a link time constant.
if (sym.scriptDefined)
- return true;
+ return true;
auto diag = Err(ctx);
diag << "relocation " << type << " cannot refer to absolute symbol: " << &sym;
@@ -1031,8 +912,8 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
// sections. Given that it is ro, we will need an extra PT_LOAD. This
// complicates things for the dynamic linker and means we would have to reserve
// space for the extra PT_LOAD even if we end up not using it.
-void RelocationScanner::process(RelExpr expr, RelType type, uint64_t offset,
- Symbol &sym, int64_t addend) const {
+void RelocScan::process(RelExpr expr, RelType type, uint64_t offset,
+ Symbol &sym, int64_t addend) const {
// If non-ifunc non-preemptible, change PLT to direct call and optimize GOT
// indirection.
const bool isIfunc = sym.isGnuIFunc();
@@ -1243,28 +1124,6 @@ void RelocationScanner::process(RelExpr expr, RelType type, uint64_t offset,
printLocation(diag, *sec, sym, offset);
}
-// This function is similar to the `handleTlsRelocation`. MIPS does not
-// support any relaxations for TLS relocations so by factoring out MIPS
-// handling in to the separate function we can simplify the code and do not
-// pollute other `handleTlsRelocation` by MIPS `ifs` statements.
-// Mips has a custom MipsGotSection that handles the writing of GOT entries
-// without dynamic relocations.
-static unsigned handleMipsTlsRelocation(Ctx &ctx, RelType type, Symbol &sym,
- InputSectionBase &c, uint64_t offset,
- int64_t addend, RelExpr expr) {
- if (expr == RE_MIPS_TLSLD) {
- ctx.in.mipsGot->addTlsIndex(*c.file);
- c.addReloc({expr, type, offset, addend, &sym});
- return 1;
- }
- if (expr == RE_MIPS_TLSGD) {
- ctx.in.mipsGot->addDynTlsEntry(*c.file, sym);
- c.addReloc({expr, type, offset, addend, &sym});
- return 1;
- }
- return 0;
-}
-
static unsigned handleAArch64PAuthTlsRelocation(InputSectionBase *sec,
RelExpr expr, RelType type,
uint64_t offset, Symbol &sym,
@@ -1293,9 +1152,9 @@ static unsigned handleAArch64PAuthTlsRelocation(InputSectionBase *sec,
// symbol in TLS block.
//
// Returns the number of relocations processed.
-unsigned RelocationScanner::handleTlsRelocation(RelExpr expr, RelType type,
- uint64_t offset, Symbol &sym,
- int64_t addend) {
+unsigned RelocScan::handleTlsRelocation(RelExpr expr, RelType type,
+ uint64_t offset, Symbol &sym,
+ int64_t addend) {
bool isAArch64 = ctx.arg.emachine == EM_AARCH64;
if (isAArch64)
@@ -1303,19 +1162,8 @@ unsigned RelocationScanner::handleTlsRelocation(RelExpr expr, RelType type,
sec, expr, type, offset, sym, addend))
return processed;
- if (expr == R_TPREL || expr == R_TPREL_NEG) {
- if (ctx.arg.shared) {
- auto diag = Err(ctx);
- diag << "relocation " << type << " against " << &sym
- << " cannot be used with -shared";
- printLocation(diag, *sec, sym, offset);
- return 1;
- }
- return 0;
- }
-
- if (ctx.arg.emachine == EM_MIPS)
- return handleMipsTlsRelocation(ctx, type, sym, *sec, offset, addend, expr);
+ if (expr == R_TPREL || expr == R_TPREL_NEG)
+ return checkTlsLe(offset, sym, type) ? 1 : 0;
bool isRISCV = ctx.arg.emachine == EM_RISCV;
@@ -1472,159 +1320,10 @@ unsigned RelocationScanner::handleTlsRelocation(RelExpr expr, RelType type,
}
template <class ELFT, class RelTy>
-void RelocationScanner::scan(typename Relocs<RelTy>::const_iterator &i) {
- const RelTy &rel = *i;
- uint32_t symIndex = rel.getSymbol(ctx.arg.isMips64EL);
- Symbol &sym = sec->getFile<ELFT>()->getSymbol(symIndex);
- RelType type;
- if constexpr (ELFT::Is64Bits || RelTy::IsCrel) {
- type = rel.getType(ctx.arg.isMips64EL);
- ++i;
- } else {
- // CREL is unsupported for MIPS N32.
- if (ctx.arg.mipsN32Abi) {
- type = getMipsN32RelType(i);
- } else {
- type = rel.getType(ctx.arg.isMips64EL);
- ++i;
- }
- }
- // Get an offset in an output section this relocation is applied to.
- uint64_t offset = rel.r_offset;
-
- RelExpr expr =
- ctx.target->getRelExpr(type, sym, sec->content().data() + offset);
- int64_t addend = RelTy::HasAddend
- ? getAddend<ELFT>(rel)
- : ctx.target->getImplicitAddend(
- sec->content().data() + rel.r_offset, type);
- if (LLVM_UNLIKELY(ctx.arg.emachine == EM_MIPS))
- addend += computeMipsAddend<ELFT>(rel, expr, sym.isLocal());
- else if (ctx.arg.emachine == EM_PPC64 && ctx.arg.isPic && type == R_PPC64_TOC)
- addend += getPPC64TocBase(ctx);
-
- // Ignore R_*_NONE and other marker relocations.
- if (expr == R_NONE)
- return;
-
- // Error if the target symbol is undefined. Symbol index 0 may be used by
- // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them.
- if (sym.isUndefined() && symIndex != 0 &&
- maybeReportUndefined(ctx, cast<Undefined>(sym), *sec, offset))
- return;
-
- if (ctx.arg.emachine == EM_PPC64) {
- // 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)) {
- // Skip the error check for CREL, which does not set `end`.
- if constexpr (!RelTy::IsCrel) {
- if (i == end) {
- auto diag = Err(ctx);
- diag << "R_PPC64_TLSGD/R_PPC64_TLSLD may not be the last "
- "relocation";
- printLocation(diag, *sec, sym, offset);
- return;
- }
- }
-
- // 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 (i->getType(/*isMips64EL=*/false) == R_PPC64_REL24_NOTOC)
- ++offset;
- }
- }
-
- // If the relocation does not emit a GOT or GOTPLT entry but its computation
- // uses their addresses, we need GOT or GOTPLT to be created.
- //
- // The 5 types that relative GOTPLT are all x86 and x86-64 specific.
- if (oneof<R_GOTPLTONLY_PC, R_GOTPLTREL, R_GOTPLT, R_PLT_GOTPLT,
- R_TLSDESC_GOTPLT, R_TLSGD_GOTPLT>(expr)) {
- ctx.in.gotPlt->hasGotPltOffRel.store(true, std::memory_order_relaxed);
- } else if (oneof<R_GOTONLY_PC, R_GOTREL, RE_PPC32_PLTREL, RE_PPC64_TOCBASE,
- RE_PPC64_RELAX_TOC>(expr)) {
- ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
- }
-
- // Process TLS relocations, including TLS optimizations. Note that
- // R_TPREL and R_TPREL_NEG relocations are resolved in process().
- //
- // Some RISCV TLSDESC relocations reference a local NOTYPE symbol,
- // but we need to process them in handleTlsRelocation.
- if (sym.isTls() || oneof<R_TLSDESC_PC, R_TLSDESC_CALL>(expr)) {
- if (unsigned processed =
- handleTlsRelocation(expr, type, offset, sym, addend)) {
- i += processed - 1;
- return;
- }
- }
-
- process(expr, type, offset, sym, addend);
-}
-
-// 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 RelocationScanner::scanSectionImpl(Relocs<RelTy> rels) {
- // Not all relocations end up in Sec->Relocations, but a lot do.
- sec->relocations.reserve(rels.size());
-
- if (ctx.arg.emachine == EM_PPC64)
- checkPPC64TLSRelax<RelTy>(*sec, rels);
+void TargetInfo::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
+ RelocScan rs(ctx, &sec);
+ // Many relocations end up in sec.relocations.
+ sec.relocations.reserve(rels.size());
// On SystemZ, all sections need to be sorted by r_offset, to allow TLS
// relaxation to be handled correctly - see SystemZ::getTlsGdRelaxSkip.
@@ -1632,40 +1331,38 @@ void RelocationScanner::scanSectionImpl(Relocs<RelTy> rels) {
if (ctx.arg.emachine == EM_S390)
rels = sortRels(rels, storage);
- if constexpr (RelTy::IsCrel) {
- for (auto i = rels.begin(); i != rels.end();)
- scan<ELFT, RelTy>(i);
- } else {
- // The non-CREL code path has additional check for PPC64 TLS.
- end = static_cast<const void *>(rels.end());
- for (auto i = rels.begin(); i != end;)
- scan<ELFT, RelTy>(i);
+ for (auto it = rels.begin(); it != rels.end(); ++it) {
+ auto type = it->getType(false);
+ rs.scan<ELFT, RelTy>(it, type, rs.getAddend<ELFT>(*it, type));
}
// Sort relocations by offset for more efficient searching for
// R_RISCV_PCREL_HI20, ALIGN relocations, R_PPC64_ADDR64 and the
// branch-to-branch optimization.
if (is_contained({EM_RISCV, EM_LOONGARCH}, ctx.arg.emachine) ||
- (ctx.arg.emachine == EM_PPC64 && sec->name == ".toc") ||
+ (ctx.arg.emachine == EM_PPC64 && sec.name == ".toc") ||
ctx.arg.branchToBranch)
- llvm::stable_sort(sec->relocs(),
+ llvm::stable_sort(sec.relocs(),
[](const Relocation &lhs, const Relocation &rhs) {
return lhs.offset < rhs.offset;
});
}
-template <class ELFT> void RelocationScanner::scanSection(InputSectionBase &s) {
- sec = &s;
- const RelsOrRelas<ELFT> rels = s.template relsOrRelas<ELFT>();
+template <class ELFT> void TargetInfo::scanSection1(InputSectionBase &sec) {
+ const RelsOrRelas<ELFT> rels = sec.template relsOrRelas<ELFT>();
if (rels.areRelocsCrel())
- scanSectionImpl<ELFT>(rels.crels);
+ scanSectionImpl<ELFT>(sec, rels.crels);
else if (rels.areRelocsRel())
- scanSectionImpl<ELFT>(rels.rels);
+ scanSectionImpl<ELFT>(sec, rels.rels);
else
- scanSectionImpl<ELFT>(rels.relas);
+ scanSectionImpl<ELFT>(sec, rels.relas);
+}
+
+void TargetInfo::scanSection(InputSectionBase &sec) {
+ invokeELFT(scanSection1, sec);
}
-template <class ELFT> void RelocationScanner::scanEhSection(EhInputSection &s) {
+void RelocScan::scanEhSection(EhInputSection &s) {
sec = &s;
OffsetGetter getter(s);
auto rels = s.rels;
@@ -1680,7 +1377,7 @@ template <class ELFT> void RelocationScanner::scanEhSection(EhInputSection &s) {
continue;
Symbol *sym = r.sym;
if (sym->isUndefined() &&
- maybeReportUndefined(ctx, cast<Undefined>(*sym), *sec, offset))
+ maybeReportUndefined(cast<Undefined>(*sym), offset))
continue;
process(r.expr, r.type, offset, *sym, r.addend);
}
@@ -1701,12 +1398,11 @@ template <class ELFT> void elf::scanRelocations(Ctx &ctx) {
auto outerFn = [&]() {
for (ELFFileBase *f : ctx.objectFiles) {
auto fn = [f, &ctx]() {
- RelocationScanner scanner(ctx);
for (InputSectionBase *s : f->getSections()) {
if (s && s->kind() == SectionBase::Regular && s->isLive() &&
(s->flags & SHF_ALLOC) &&
!(s->type == SHT_ARM_EXIDX && ctx.arg.emachine == EM_ARM))
- scanner.template scanSection<ELFT>(*s);
+ ctx.target->scanSection(*s);
}
};
if (serial)
@@ -1715,14 +1411,14 @@ template <class ELFT> void elf::scanRelocations(Ctx &ctx) {
tg.spawn(fn);
}
auto scanEH = [&] {
- RelocationScanner scanner(ctx);
+ RelocScan scanner(ctx);
for (Partition &part : ctx.partitions) {
for (EhInputSection *sec : part.ehFrame->sections)
- scanner.template scanEhSection<ELFT>(*sec);
+ scanner.scanEhSection(*sec);
if (part.armExidx && part.armExidx->isLive())
for (InputSection *sec : part.armExidx->exidxSections)
if (sec->isLive())
- scanner.template scanSection<ELFT>(*sec);
+ ctx.target->scanSection(*sec);
}
};
if (serial)
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 7ea03c3..86ca298 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -17,7 +17,9 @@
namespace lld::elf {
struct Ctx;
+struct ELFSyncStream;
class Defined;
+class Undefined;
class Symbol;
class InputSection;
class InputSectionBase;
@@ -153,12 +155,17 @@ struct JumpInstrMod {
unsigned size;
};
+void printLocation(ELFSyncStream &s, InputSectionBase &sec, const Symbol &sym,
+ uint64_t off);
+
// This function writes undefined symbol diagnostics to an internal buffer.
// Call reportUndefinedSymbols() after calling scanRelocations() to emit
// the diagnostics.
template <class ELFT> void scanRelocations(Ctx &ctx);
template <class ELFT> void checkNoCrossRefs(Ctx &ctx);
void reportUndefinedSymbols(Ctx &);
+bool maybeReportUndefined(Ctx &, Undefined &sym, InputSectionBase &sec,
+ uint64_t offset);
void postScanRelocations(Ctx &ctx);
void addGotEntry(Ctx &ctx, Symbol &sym);
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index fb79ee9..89e4dbe 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -26,6 +26,7 @@
#include "Target.h"
#include "InputFiles.h"
#include "OutputSections.h"
+#include "RelocScan.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 9f06051..90d8ddf 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -24,6 +24,7 @@ namespace elf {
class Defined;
class InputFile;
class Symbol;
+template <class RelTy> struct Relocs;
std::string toStr(Ctx &, RelType type);
@@ -87,6 +88,15 @@ public:
virtual bool inBranchRange(RelType type, uint64_t src,
uint64_t dst) const;
+ // Function for scanning relocation. Typically overridden by targets that
+ // require special type or addend adjustment.
+ virtual void scanSection(InputSectionBase &);
+ // Called by scanSection as a default implementation for specific ELF
+ // relocation types.
+ template <class ELFT> void scanSection1(InputSectionBase &);
+ template <class ELFT, class RelTy>
+ void scanSectionImpl(InputSectionBase &, Relocs<RelTy>);
+
virtual void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const = 0;
void relocateNoSym(uint8_t *loc, RelType type, uint64_t val) const {