From 75183d82570a4da970fda2d17c85b1c77caa36f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=94=BF=E5=90=AB?= Date: Sun, 31 May 2026 23:53:03 +0800 Subject: [PATCH] lld: fix Linx HI/LO relocation pairing --- lld/ELF/InputSection.cpp | 86 ++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 8448beb0841c4..766c5a022569d 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -765,7 +765,8 @@ static const Relocation *findLinxPCRelHiByOffset(const InputSection *isec, static const Relocation *findLinxPCRelHiBySymbol(const InputSectionBase *sec, uint64_t loOffset, - const Symbol *sym) { + const Symbol *sym, + int64_t addend) { auto *isec = dyn_cast_or_null(sec); if (!isec) return nullptr; @@ -773,25 +774,48 @@ static const Relocation *findLinxPCRelHiBySymbol(const InputSectionBase *sec, auto it = llvm::lower_bound( isec->relocs(), loOffset, [](const Relocation &lhs, uint64_t rhs) { return lhs.offset < rhs; }); + + const Relocation *before = nullptr; while (it != isec->relocs().begin()) { --it; - if (it->sym == sym && isLinxPCRelHi(it->type)) - return &*it; + if (it->sym == sym && it->addend == addend && isLinxPCRelHi(it->type)) { + before = &*it; + break; + } } - return nullptr; + + const Relocation *after = nullptr; + it = llvm::lower_bound( + isec->relocs(), loOffset, + [](const Relocation &lhs, uint64_t rhs) { return lhs.offset < rhs; }); + for (; it != isec->relocs().end(); ++it) { + if (it->sym == sym && it->addend == addend && isLinxPCRelHi(it->type)) { + after = &*it; + break; + } + } + + if (!before) + return after; + if (!after) + return before; + return loOffset - before->offset <= after->offset - loOffset ? before : after; } // Most R_LINX_LO12 relocations point at a local anchor symbol whose value // matches the paired R_LINX_PCREL_HI20 relocation offset. Some startup objects // instead target the final symbol directly (for example _DYNAMIC in static -// PIE), so the linker has to recover the HI relocation by symbol and then use -// the HI relocation's place when reconstructing the PC-relative target. +// PIE). Linx block layout can also place the HI in a later predecessor block, +// so the linker has to recover the HI relocation by exact symbol/addend in +// either direction before reconstructing the PC-relative target. static const Relocation *getLinxPCRelHi20(Ctx &ctx, const InputSectionBase *loSec, const Relocation &loReloc) { int64_t addend = loReloc.addend; Symbol *sym = loReloc.sym; std::string anchor = toStr(ctx, *sym); + bool unsupportedDefinedAnchor = false; + StringRef unsupportedSectionName; if (const auto *d = dyn_cast(sym)) { if (d->section) { @@ -803,31 +827,36 @@ static const Relocation *getLinxPCRelHi20(Ctx &ctx, hiSec = eh->getParent(); } if (!hiSec) { - Err(ctx) << loSec->getLocation(loReloc.offset) - << ": R_LINX_LO12 relocation " - "points to unsupported anchor section '" - << d->section->name << "' for symbol '" << sym->getName() - << "'"; - return nullptr; + unsupportedDefinedAnchor = true; + unsupportedSectionName = d->section->name; + } else { + anchor = sym->getName().empty() ? "offset 0x" + utohexstr(d->value) + : toStr(ctx, *sym) + "+0x" + + utohexstr(d->value); + if (const Relocation *hiRel = findLinxPCRelHiByOffset(hiSec, d->value)) { + if (addend != 0) + Warn(ctx) << loSec->getLocation(loReloc.offset) + << ": non-zero addend in " + "R_LINX_LO12 relocation to " + << anchor << " is ignored"; + return hiRel; + } } - - anchor = sym->getName().empty() ? "offset 0x" + utohexstr(d->value) - : toStr(ctx, *sym) + "+0x" + - utohexstr(d->value); - if (addend != 0) - Warn(ctx) << loSec->getLocation(loReloc.offset) - << ": non-zero addend in " - "R_LINX_LO12 relocation to " - << anchor << " is ignored"; - if (const Relocation *hiRel = findLinxPCRelHiByOffset(hiSec, d->value)) - return hiRel; } } if (const Relocation *hiRel = - findLinxPCRelHiBySymbol(loSec, loReloc.offset, sym)) + findLinxPCRelHiBySymbol(loSec, loReloc.offset, sym, addend)) return hiRel; + if (unsupportedDefinedAnchor) { + Err(ctx) << loSec->getLocation(loReloc.offset) + << ": R_LINX_LO12 relocation points to unsupported anchor section '" + << unsupportedSectionName << "' for symbol '" << sym->getName() + << "'"; + return nullptr; + } + Err(ctx) << loSec->getLocation(loReloc.offset) << ": R_LINX_LO12 relocation points to " << anchor << " without an associated R_LINX_PCREL_HI20 relocation"; @@ -1014,8 +1043,13 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r, return 0; } case RE_LINX_PC_INDIRECT: { - if (const Relocation *hiRel = getLinxPCRelHi20(ctx, this, r)) - return getRelocTargetVA(ctx, *hiRel, getVA(hiRel->offset)); + if (const Relocation *hiRel = getLinxPCRelHi20(ctx, this, r)) { + uint64_t p = getVA(hiRel->offset); + uint64_t val = hiRel->sym->isUndefWeak() + ? p + hiRel->addend + : hiRel->sym->getVA(ctx, hiRel->addend); + return val; + } return 0; } case RE_LOONGARCH_PC_INDIRECT: {