[ELF] Refactor RelocScan::scan to be target-specific (#163138)

- Extract RelocScan to RelocScan.h. The file includes Target.h, and
cannot be merged with Relocations.h
- Add MIPS and PPC64 specific relocation scanners, removing runtime
checks for other targets.

This refactoring prepares the codebase for better target-specific
optimizations and easier addition of target-specific behavior.
This commit is contained in:
Fangrui Song
2025-10-13 23:32:34 -07:00
committed by GitHub
parent 5a560b306d
commit 22bf331933
7 changed files with 446 additions and 354 deletions

View File

@@ -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 {

View File

@@ -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);

126
lld/ELF/RelocScan.h Normal file
View File

@@ -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

View File

@@ -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);
}
template <class ELFT> void RelocationScanner::scanEhSection(EhInputSection &s) {
void TargetInfo::scanSection(InputSectionBase &sec) {
invokeELFT(scanSection1, sec);
}
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)

View File

@@ -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);

View File

@@ -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"

View File

@@ -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 {