[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