[lld] [ELF] Add target-specific relocation scanning for RISC-V (PR #181332)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Sun Feb 15 20:01:18 PST 2026
================
@@ -361,24 +325,148 @@ void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
RelType type = it->getType(false);
uint32_t symIndex = it->getSymbol(false);
Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIndex);
- const uint8_t *loc = sec.content().data() + it->r_offset;
+ uint64_t offset = it->r_offset;
if (type == R_RISCV_VENDOR) {
if (!rvVendor.empty())
- Err(ctx) << getErrorLoc(ctx, loc)
+ Err(ctx) << getErrorLoc(ctx, sec.content().data() + offset)
<< "malformed consecutive R_RISCV_VENDOR relocations";
rvVendor = sym.getName();
continue;
} else if (!rvVendor.empty()) {
- Err(ctx) << getErrorLoc(ctx, loc)
+ Err(ctx) << getErrorLoc(ctx, sec.content().data() + offset)
<< "unknown vendor-specific relocation (" << type.v
<< ") in namespace '" << rvVendor << "' against symbol '" << &sym
<< "'";
rvVendor = "";
continue;
}
- rs.scan<ELFT, RelTy>(it, type, rs.getAddend<ELFT>(*it, type));
+ if (sym.isUndefined() && symIndex != 0 &&
+ rs.maybeReportUndefined(cast<Undefined>(sym), offset))
+ continue;
+ int64_t addend = rs.getAddend<ELFT>(*it, type);
+ RelExpr expr;
+ // Relocation types that only need a RelExpr set `expr` and break out of
+ // the switch to reach rs.process(). Types that need special handling
+ // (fast-path helpers, TLS) call a handler and use `continue`.
+ switch (type) {
+ case R_RISCV_NONE:
+ continue;
+
+ // Absolute relocations:
+ case R_RISCV_32:
+ case R_RISCV_64:
+ case R_RISCV_HI20:
+ case R_RISCV_LO12_I:
+ case R_RISCV_LO12_S:
+ expr = R_ABS;
+ break;
+
+ // ADD/SET/SUB:
+ case R_RISCV_ADD8:
+ case R_RISCV_ADD16:
+ case R_RISCV_ADD32:
+ case R_RISCV_ADD64:
+ case R_RISCV_SET6:
+ case R_RISCV_SET8:
+ case R_RISCV_SET16:
+ case R_RISCV_SET32:
+ case R_RISCV_SUB6:
+ case R_RISCV_SUB8:
+ case R_RISCV_SUB16:
+ case R_RISCV_SUB32:
+ case R_RISCV_SUB64:
+ expr = RE_RISCV_ADD;
+ break;
+
+ // PC-relative relocations:
+ case R_RISCV_JAL:
+ case R_RISCV_BRANCH:
+ case R_RISCV_PCREL_HI20:
+ case R_RISCV_RVC_BRANCH:
+ case R_RISCV_RVC_JUMP:
+ case R_RISCV_32_PCREL:
+ rs.processR_PC(type, offset, addend, sym);
+ continue;
+ case R_RISCV_PCREL_LO12_I:
+ case R_RISCV_PCREL_LO12_S:
+ expr = RE_RISCV_PC_INDIRECT;
+ break;
+
+ // GOT-generating relocations:
+ case R_RISCV_GOT_HI20:
+ case R_RISCV_GOT32_PCREL:
+ expr = R_GOT_PC;
+ break;
+
+ // PLT-generating relocations:
+ case R_RISCV_CALL:
+ case R_RISCV_CALL_PLT:
+ case R_RISCV_PLT32:
+ rs.processR_PLT_PC(type, offset, addend, sym);
+ continue;
+
+ // TLS relocations:
+ case R_RISCV_TPREL_HI20:
+ case R_RISCV_TPREL_LO12_I:
+ case R_RISCV_TPREL_LO12_S:
+ if (rs.checkTlsLe(offset, sym, type))
+ continue;
+ expr = R_TPREL;
+ break;
+ case R_RISCV_TLS_GOT_HI20:
+ // There is no IE to LE optimization.
+ ctx.hasTlsIe.store(true, std::memory_order_relaxed);
+ sym.setFlags(NEEDS_TLSIE);
+ sec.addReloc({R_GOT_PC, type, offset, addend, &sym});
+ continue;
+ case R_RISCV_TLS_GD_HI20:
+ // There is no GD to IE/LE optimization.
+ sym.setFlags(NEEDS_TLSGD);
+ sec.addReloc({R_TLSGD_PC, type, offset, addend, &sym});
+ continue;
+
+ // TLSDESC relocations:
+ case R_RISCV_TLSDESC_HI20:
+ rs.handleTlsDesc(R_TLSDESC_PC, R_GOT_PC, type, offset, addend, sym);
+ continue;
+ case R_RISCV_TLSDESC_LOAD_LO12:
+ case R_RISCV_TLSDESC_ADD_LO12:
+ // R_RISCV_TLSDESC_{LOAD_LO12,ADD_LO12,CALL} reference a label, not the
+ // TLS symbol, so we cannot use handleTlsDesc (which sets NEEDS_TLSDESC).
+ // For TLSDESC->IE, use R_TPREL as well, but relocateAlloc uses isToLe
+ // (from HI20) to select the correct transform.
+ sec.addReloc({ctx.arg.shared ? R_TLSDESC_PC : R_TPREL, type, offset,
----------------
MaskRay wrote:
Previously, we use `R_RELAX_TLS_GD_TO_*` to refer to GD-to-IE and GD-to-LE optimizations. However, RISC-V is special as indicated by the previous comment (now deleted from `Relocations.cpp`):
> R_RISCV_TLSDESC_{LOAD_LO12,ADD_LO12_I,CALL} reference a non-preemptible label, so TLSDESC=>IE will be categorized as R_RELAX_TLS_GD_TO_LE. We fix the categorization in RISCV::relocateAlloc.
With the `[ELF] Add target-specific relocation scanning for *` patch series, we remove `R_RELAX_TLS_*_TO_*_` in favor of `R_TPREL` for LE/GD-to-LE/TLSDESC-to-LE and `R_GOT_PC` for IE/GD-to-IE/TLSDESC-to-IE.
The convoluted handleTlsRelocations code with various hooks is replaced with more straight-line code.
For RISC-V TLSDESC-to-IE, both are equally "wrong" since the actual value comes from the HI20's `tlsdescVal`, not from the expr's value computation. The HI20 itself correctly distinguishes (`R_GOT_PC` for IE, `R_TPREL` for LE).
(In addition, `R_TPREL` is arguably more-specific than `R_RELAX_TLS_GD_TO_LE` was: the LE value really is TP-relative (R_TPREL) instead of R_TPREL_NEG.)
https://github.com/llvm/llvm-project/pull/181332
More information about the llvm-commits
mailing list