[lld] [lld] Add infrastructure for handling RISCV vendor-specific relocations. (PR #159987)

Owen Anderson via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 23 18:53:00 PDT 2025


https://github.com/resistor updated https://github.com/llvm/llvm-project/pull/159987

>From a98c677280d5bfa2064149e08fe2275aed4b0a0d Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Mon, 22 Sep 2025 00:36:36 +0900
Subject: [PATCH 1/6] [lld] Add infrastructure for handling RISCV
 vendor-specific relocations.

This requires adding an additional parameter to getRelExpr(), which
unfortunately requires changes in non-RISCV targets. The alternative
is to make the getRelExpr() stateful on RISCV, which seems at odds to
the design intent. The design presented here keeps the state in the
explicitly stateful RelocationScanner.
---
 lld/ELF/Arch/AArch64.cpp                |  8 ++--
 lld/ELF/Arch/AMDGPU.cpp                 |  8 ++--
 lld/ELF/Arch/ARM.cpp                    |  8 ++--
 lld/ELF/Arch/AVR.cpp                    |  8 ++--
 lld/ELF/Arch/Hexagon.cpp                |  8 ++--
 lld/ELF/Arch/LoongArch.cpp              |  6 +--
 lld/ELF/Arch/MSP430.cpp                 |  8 ++--
 lld/ELF/Arch/Mips.cpp                   |  6 +--
 lld/ELF/Arch/PPC.cpp                    |  8 ++--
 lld/ELF/Arch/PPC64.cpp                  |  8 ++--
 lld/ELF/Arch/RISCV.cpp                  | 49 +++++++++++++++++++++++--
 lld/ELF/Arch/SPARCV9.cpp                |  8 ++--
 lld/ELF/Arch/SystemZ.cpp                |  8 ++--
 lld/ELF/Arch/X86.cpp                    |  8 ++--
 lld/ELF/Arch/X86_64.cpp                 |  8 ++--
 lld/ELF/Relocations.cpp                 | 15 +++++++-
 lld/ELF/Target.h                        |  4 +-
 lld/test/ELF/riscv-vendor-relocations.s | 28 ++++++++++++++
 18 files changed, 143 insertions(+), 61 deletions(-)
 create mode 100644 lld/test/ELF/riscv-vendor-relocations.s

diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 27e77e943c197..749c50fc57628 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -64,8 +64,8 @@ namespace {
 class AArch64 : public TargetInfo {
 public:
   AArch64(Ctx &);
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -131,8 +131,8 @@ AArch64::AArch64(Ctx &ctx) : TargetInfo(ctx) {
   needsThunks = true;
 }
 
-RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
-                            const uint8_t *loc) const {
+RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                            StringRef rv_vendor) const {
   switch (type) {
   case R_AARCH64_ABS16:
   case R_AARCH64_ABS32:
diff --git a/lld/ELF/Arch/AMDGPU.cpp b/lld/ELF/Arch/AMDGPU.cpp
index 52fc779855a36..10356370782a6 100644
--- a/lld/ELF/Arch/AMDGPU.cpp
+++ b/lld/ELF/Arch/AMDGPU.cpp
@@ -32,8 +32,8 @@ class AMDGPU final : public TargetInfo {
   uint32_t calcEFlags() const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
 };
@@ -176,8 +176,8 @@ void AMDGPU::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
   }
 }
 
-RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s,
-                           const uint8_t *loc) const {
+RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                           StringRef rv_vendor) const {
   switch (type) {
   case R_AMDGPU_ABS32:
   case R_AMDGPU_ABS64:
diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp
index 91a673f13d68e..35927f5ea7bad 100644
--- a/lld/ELF/Arch/ARM.cpp
+++ b/lld/ELF/Arch/ARM.cpp
@@ -29,8 +29,8 @@ class ARM final : public TargetInfo {
 public:
   ARM(Ctx &);
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -99,8 +99,8 @@ uint32_t ARM::calcEFlags() const {
   return EF_ARM_EABI_VER5 | abiFloatType | armBE8;
 }
 
-RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
-                        const uint8_t *loc) const {
+RelExpr ARM::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                        StringRef rv_vendor) const {
   switch (type) {
   case R_ARM_ABS32:
   case R_ARM_MOVW_ABS_NC:
diff --git a/lld/ELF/Arch/AVR.cpp b/lld/ELF/Arch/AVR.cpp
index 5276ff9840591..492733a07434f 100644
--- a/lld/ELF/Arch/AVR.cpp
+++ b/lld/ELF/Arch/AVR.cpp
@@ -44,8 +44,8 @@ class AVR final : public TargetInfo {
 public:
   AVR(Ctx &ctx) : TargetInfo(ctx) { needsThunks = true; }
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor) const override;
   bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
                   uint64_t branchAddr, const Symbol &s,
                   int64_t a) const override;
@@ -54,8 +54,8 @@ class AVR final : public TargetInfo {
 };
 } // namespace
 
-RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
-                        const uint8_t *loc) const {
+RelExpr AVR::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                        StringRef rv_vendor) const {
   switch (type) {
   case R_AVR_6:
   case R_AVR_6_ADIW:
diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 9b33e78731c97..41b084b157801 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -33,8 +33,8 @@ class Hexagon final : public TargetInfo {
 public:
   Hexagon(Ctx &);
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
@@ -99,8 +99,8 @@ static uint32_t applyMask(uint32_t mask, uint32_t data) {
   return result;
 }
 
-RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
-                            const uint8_t *loc) const {
+RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                            StringRef rv_vendor) const {
   switch (type) {
   case R_HEX_NONE:
     return R_NONE;
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index db2c71c3b42b9..1cf6a68bd6e9b 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -33,8 +33,8 @@ class LoongArch final : public TargetInfo {
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
   RelType getDynRel(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   bool usesOnlyLowPageBits(RelType type) const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
@@ -416,7 +416,7 @@ RelType LoongArch::getDynRel(RelType type) const {
 }
 
 RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
-                              const uint8_t *loc) const {
+                              const uint8_t *loc, StringRef rv_vendor) const {
   switch (type) {
   case R_LARCH_NONE:
   case R_LARCH_MARK_LA:
diff --git a/lld/ELF/Arch/MSP430.cpp b/lld/ELF/Arch/MSP430.cpp
index 03d34436d2959..5580cea603a81 100644
--- a/lld/ELF/Arch/MSP430.cpp
+++ b/lld/ELF/Arch/MSP430.cpp
@@ -31,8 +31,8 @@ namespace {
 class MSP430 final : public TargetInfo {
 public:
   MSP430(Ctx &);
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
 };
@@ -43,8 +43,8 @@ MSP430::MSP430(Ctx &ctx) : TargetInfo(ctx) {
   trapInstr = {0x43, 0x43, 0x43, 0x43};
 }
 
-RelExpr MSP430::getRelExpr(RelType type, const Symbol &s,
-                           const uint8_t *loc) const {
+RelExpr MSP430::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                           StringRef rv_vendor) const {
   switch (type) {
   case R_MSP430_10_PCREL:
   case R_MSP430_16_PCREL:
diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index f88b021c8ba39..a713980631187 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -23,8 +23,8 @@ template <class ELFT> class MIPS final : public TargetInfo {
 public:
   MIPS(Ctx &);
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   RelType getDynRel(RelType type) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -77,7 +77,7 @@ template <class ELFT> uint32_t MIPS<ELFT>::calcEFlags() const {
 
 template <class ELFT>
 RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
-                               const uint8_t *loc) const {
+                               const uint8_t *loc, StringRef rv_vendor) const {
   // See comment in the calculateMipsRelChain.
   if (ELFT::Is64Bits || ctx.arg.mipsN32Abi)
     type.v &= 0xff;
diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp
index 60a0a38d5f23a..6342574bbd127 100644
--- a/lld/ELF/Arch/PPC.cpp
+++ b/lld/ELF/Arch/PPC.cpp
@@ -25,8 +25,8 @@ namespace {
 class PPC final : public TargetInfo {
 public:
   PPC(Ctx &);
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writeGotHeader(uint8_t *buf) const override;
@@ -219,8 +219,8 @@ bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
   llvm_unreachable("unsupported relocation type used in branch");
 }
 
-RelExpr PPC::getRelExpr(RelType type, const Symbol &s,
-                        const uint8_t *loc) const {
+RelExpr PPC::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                        StringRef rv_vendor) const {
   switch (type) {
   case R_PPC_NONE:
     return R_NONE;
diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index 3cd4a6294e2a8..b406122c9a46f 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -169,8 +169,8 @@ class PPC64 final : public TargetInfo {
   PPC64(Ctx &);
   int getTlsGdRelaxSkip(RelType type) const override;
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writePltHeader(uint8_t *buf) const override;
@@ -990,8 +990,8 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
   }
 }
 
-RelExpr PPC64::getRelExpr(RelType type, const Symbol &s,
-                          const uint8_t *loc) const {
+RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                          StringRef rv_vendor) const {
   switch (type) {
   case R_PPC64_NONE:
     return R_NONE;
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 7f2bfefa5578a..be42d77800c80 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -39,8 +39,8 @@ class RISCV final : public TargetInfo {
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
   RelType getDynRel(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
   void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
@@ -56,6 +56,10 @@ class RISCV final : public TargetInfo {
   bool synthesizeAlign(uint64_t &dot, InputSection *sec) override;
   void finalizeRelax(int passes) const override;
 
+  // For vendor-specific relocations.
+  void relocateVendor(uint8_t *loc, const Relocation &rel, uint64_t val,
+                      StringRef vendor = "") const;
+
   // The following two variables are used by synthesized ALIGN relocations.
   InputSection *baseSec = nullptr;
   // r_offset and r_addend pairs.
@@ -275,7 +279,15 @@ RelType RISCV::getDynRel(RelType type) const {
 }
 
 RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
-                          const uint8_t *loc) const {
+                          const uint8_t *loc, StringRef rv_vendor) const {
+  if (!rv_vendor.empty()) {
+    // TODO: Dispatch to vendor-specific relocation handling.
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown vendor-specific relocation ("
+             << type.v << ") in vendor namespace \"" << rv_vendor
+             << "\" against symbol " << &s;
+    return R_NONE;
+  }
+
   switch (type) {
   case R_RISCV_NONE:
     return R_NONE;
@@ -338,6 +350,8 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
   case R_RISCV_SET_ULEB128:
   case R_RISCV_SUB_ULEB128:
     return RE_RISCV_LEB128;
+  case R_RISCV_VENDOR:
+    return R_NONE;
   default:
     Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v
              << ") against symbol " << &s;
@@ -555,6 +569,15 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
   }
 }
 
+void RISCV::relocateVendor(uint8_t *loc, const Relocation &rel, uint64_t val,
+                           StringRef vendor) const {
+  llvm_unreachable("unknown vendor relocation");
+  Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation type "
+           << llvm::utostr(rel.type.v) << " in vendor namespace \"" << vendor
+           << "\"";
+  return;
+}
+
 static bool relaxable(ArrayRef<Relocation> relocs, size_t i) {
   return i + 1 != relocs.size() && relocs[i + 1].type == R_RISCV_RELAX;
 }
@@ -617,6 +640,26 @@ void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
     uint8_t *loc = buf + rel.offset;
     uint64_t val = sec.getRelocTargetVA(ctx, rel, secAddr + rel.offset);
 
+    if (rel.type == R_RISCV_VENDOR) {
+      // Vendor-specific relocations are indicated by a pair of a R_RISCV_VENDOR
+      // relocation followed by relocation in the vendor's private namespace.
+      // The vendor name is identified by the symbol name referenced by the
+      // R_RISCV_VENDOR relocation.
+      StringRef vendor = rel.sym->getName();
+
+      // Consume the second relocation as well.
+      assert(i != size - 1 &&
+             "R_RISCV_VENDOR relocation cannot be the final relocation!");
+      i += 1;
+
+      const Relocation &rel2 = relocs[i];
+      uint8_t *loc2 = buf + rel2.offset;
+      uint64_t val2 = sec.getRelocTargetVA(ctx, rel2, secAddr + rel2.offset);
+
+      relocateVendor(loc2, rel2, val2, vendor);
+      continue;
+    }
+
     switch (rel.expr) {
     case R_RELAX_HINT:
       continue;
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index 86668c50d3c78..c34f433d303ba 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -21,8 +21,8 @@ namespace {
 class SPARCV9 final : public TargetInfo {
 public:
   SPARCV9(Ctx &);
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
   void relocate(uint8_t *loc, const Relocation &rel,
@@ -44,8 +44,8 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo(ctx) {
   defaultImageBase = 0x100000;
 }
 
-RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
-                            const uint8_t *loc) const {
+RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                            StringRef rv_vendor) const {
   switch (type) {
   case R_SPARC_32:
   case R_SPARC_UA32:
diff --git a/lld/ELF/Arch/SystemZ.cpp b/lld/ELF/Arch/SystemZ.cpp
index a9125806c0952..8b3ecb0fb8f98 100644
--- a/lld/ELF/Arch/SystemZ.cpp
+++ b/lld/ELF/Arch/SystemZ.cpp
@@ -24,8 +24,8 @@ class SystemZ : public TargetInfo {
 public:
   SystemZ(Ctx &);
   int getTlsGdRelaxSkip(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   RelType getDynRel(RelType type) const override;
   void writeGotHeader(uint8_t *buf) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -79,8 +79,8 @@ SystemZ::SystemZ(Ctx &ctx) : TargetInfo(ctx) {
   defaultImageBase = 0x1000000;
 }
 
-RelExpr SystemZ::getRelExpr(RelType type, const Symbol &s,
-                            const uint8_t *loc) const {
+RelExpr SystemZ::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                            StringRef rv_vendor) const {
   switch (type) {
   case R_390_NONE:
     return R_NONE;
diff --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp
index c1980d6e0538f..d75c089d0b767 100644
--- a/lld/ELF/Arch/X86.cpp
+++ b/lld/ELF/Arch/X86.cpp
@@ -23,8 +23,8 @@ class X86 : public TargetInfo {
 public:
   X86(Ctx &);
   int getTlsGdRelaxSkip(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writeGotPltHeader(uint8_t *buf) const override;
   RelType getDynRel(RelType type) const override;
@@ -74,8 +74,8 @@ int X86::getTlsGdRelaxSkip(RelType type) const {
   return type == R_386_TLS_GOTDESC || type == R_386_TLS_DESC_CALL ? 1 : 2;
 }
 
-RelExpr X86::getRelExpr(RelType type, const Symbol &s,
-                        const uint8_t *loc) const {
+RelExpr X86::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                        StringRef rv_vendor) const {
   switch (type) {
   case R_386_8:
   case R_386_16:
diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index 488f4803b2cb4..a6eb7bae4696a 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -28,8 +28,8 @@ class X86_64 : public TargetInfo {
 public:
   X86_64(Ctx &);
   int getTlsGdRelaxSkip(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s,
-                     const uint8_t *loc) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                     StringRef rv_vendor = "") const override;
   RelType getDynRel(RelType type) const override;
   void writeGotPltHeader(uint8_t *buf) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -361,8 +361,8 @@ bool X86_64::relaxOnce(int pass) const {
   return changed;
 }
 
-RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
-                           const uint8_t *loc) const {
+RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                           StringRef rv_vendor) const {
   switch (type) {
   case R_X86_64_8:
   case R_X86_64_16:
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 6f55bac2ecf16..f58985115d826 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -457,6 +457,7 @@ class RelocationScanner {
   Ctx &ctx;
   InputSectionBase *sec;
   OffsetGetter getter;
+  StringRef rv_vendor = "";
 
   // End of relocations, used by Mips/PPC64.
   const void *end = nullptr;
@@ -1510,13 +1511,20 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
       ++i;
     }
   }
+
+  // Stash the RISCV vendor namespace for the subsequent relocation.
+  if (LLVM_UNLIKELY(ctx.arg.emachine == EM_RISCV) && type == R_RISCV_VENDOR) {
+    rv_vendor = sym.getName();
+    return;
+  }
+
   // Get an offset in an output section this relocation is applied to.
   uint64_t offset = getter.get(ctx, rel.r_offset);
   if (offset == uint64_t(-1))
     return;
 
-  RelExpr expr =
-      ctx.target->getRelExpr(type, sym, sec->content().data() + offset);
+  RelExpr expr = ctx.target->getRelExpr(
+      type, sym, sec->content().data() + offset, rv_vendor);
   int64_t addend = RelTy::HasAddend
                        ? getAddend<ELFT>(rel)
                        : ctx.target->getImplicitAddend(
@@ -1526,6 +1534,9 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
   else if (ctx.arg.emachine == EM_PPC64 && ctx.arg.isPic && type == R_PPC64_TOC)
     addend += getPPC64TocBase(ctx);
 
+  // A RISCV vendor namespace only applies to a single relocation.
+  rv_vendor = "";
+
   // Ignore R_*_NONE and other marker relocations.
   if (expr == R_NONE)
     return;
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index bb8c24f052aa2..6aad5353cc1dc 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -31,8 +31,8 @@ class TargetInfo {
 public:
   TargetInfo(Ctx &ctx) : ctx(ctx) {}
   virtual uint32_t calcEFlags() const { return 0; }
-  virtual RelExpr getRelExpr(RelType type, const Symbol &s,
-                             const uint8_t *loc) const = 0;
+  virtual RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                             StringRef rv_vendor = "") const = 0;
   virtual RelType getDynRel(RelType type) const { return 0; }
   virtual void writeGotPltHeader(uint8_t *buf) const {}
   virtual void writeGotHeader(uint8_t *buf) const {}
diff --git a/lld/test/ELF/riscv-vendor-relocations.s b/lld/test/ELF/riscv-vendor-relocations.s
new file mode 100644
index 0000000000000..69e2cf6f83e64
--- /dev/null
+++ b/lld/test/ELF/riscv-vendor-relocations.s
@@ -0,0 +1,28 @@
+# RUN: llvm-mc -triple riscv32 -mattr=+experimental-xqcibi,+xandesperf %s -filetype=obj -o %t.o
+# RUN: not ld.lld -pie %t.o -o /dev/null 2>&1 | FileCheck %s
+
+  .option exact
+
+  qc.e.bgeui s0, 20, TARGET
+# CHECK: unknown vendor-specific relocation (193) in vendor namespace "QUALCOMM" against symbol TARGET
+
+  .global QUALCOMM
+QUALCOMM:
+  nop
+
+  qc.e.bgeui s0, 20, TARGET
+# CHECK: unknown vendor-specific relocation (193) in vendor namespace "QUALCOMM" against symbol TARGET
+
+  nds.bbc t0, 7, TARGET
+# CHECK: unknown vendor-specific relocation (241) in vendor namespace "ANDES" against symbol TARGET
+
+  .global ANDES
+ANDES:
+  nop
+
+  nds.bbs t0, 7, TARGET
+# CHECK: unknown vendor-specific relocation (241) in vendor namespace "ANDES" against symbol TARGET
+
+  .global TARGET
+TARGET:
+  nop

>From 046b6cc9d1014f1593cd51d6725940617d37ac4b Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Mon, 22 Sep 2025 21:14:00 +0900
Subject: [PATCH 2/6] Switch to using getVendorRelExpr to avoid adding a new
 argument to getRelExpr.

---
 lld/ELF/Arch/AArch64.cpp   |  8 ++++----
 lld/ELF/Arch/AMDGPU.cpp    |  8 ++++----
 lld/ELF/Arch/ARM.cpp       |  8 ++++----
 lld/ELF/Arch/AVR.cpp       |  8 ++++----
 lld/ELF/Arch/Hexagon.cpp   |  8 ++++----
 lld/ELF/Arch/LoongArch.cpp |  6 +++---
 lld/ELF/Arch/MSP430.cpp    |  8 ++++----
 lld/ELF/Arch/Mips.cpp      |  6 +++---
 lld/ELF/Arch/PPC.cpp       |  8 ++++----
 lld/ELF/Arch/PPC64.cpp     |  8 ++++----
 lld/ELF/Arch/RISCV.cpp     | 25 ++++++++++++++-----------
 lld/ELF/Arch/SPARCV9.cpp   |  8 ++++----
 lld/ELF/Arch/SystemZ.cpp   |  8 ++++----
 lld/ELF/Arch/X86.cpp       |  8 ++++----
 lld/ELF/Arch/X86_64.cpp    |  8 ++++----
 lld/ELF/Relocations.cpp    | 11 ++++++++---
 lld/ELF/Target.h           |  8 ++++++--
 17 files changed, 82 insertions(+), 70 deletions(-)

diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 749c50fc57628..27e77e943c197 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -64,8 +64,8 @@ namespace {
 class AArch64 : public TargetInfo {
 public:
   AArch64(Ctx &);
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -131,8 +131,8 @@ AArch64::AArch64(Ctx &ctx) : TargetInfo(ctx) {
   needsThunks = true;
 }
 
-RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                            StringRef rv_vendor) const {
+RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
+                            const uint8_t *loc) const {
   switch (type) {
   case R_AARCH64_ABS16:
   case R_AARCH64_ABS32:
diff --git a/lld/ELF/Arch/AMDGPU.cpp b/lld/ELF/Arch/AMDGPU.cpp
index 10356370782a6..52fc779855a36 100644
--- a/lld/ELF/Arch/AMDGPU.cpp
+++ b/lld/ELF/Arch/AMDGPU.cpp
@@ -32,8 +32,8 @@ class AMDGPU final : public TargetInfo {
   uint32_t calcEFlags() const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
 };
@@ -176,8 +176,8 @@ void AMDGPU::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
   }
 }
 
-RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                           StringRef rv_vendor) const {
+RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s,
+                           const uint8_t *loc) const {
   switch (type) {
   case R_AMDGPU_ABS32:
   case R_AMDGPU_ABS64:
diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp
index 35927f5ea7bad..91a673f13d68e 100644
--- a/lld/ELF/Arch/ARM.cpp
+++ b/lld/ELF/Arch/ARM.cpp
@@ -29,8 +29,8 @@ class ARM final : public TargetInfo {
 public:
   ARM(Ctx &);
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -99,8 +99,8 @@ uint32_t ARM::calcEFlags() const {
   return EF_ARM_EABI_VER5 | abiFloatType | armBE8;
 }
 
-RelExpr ARM::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                        StringRef rv_vendor) const {
+RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
+                        const uint8_t *loc) const {
   switch (type) {
   case R_ARM_ABS32:
   case R_ARM_MOVW_ABS_NC:
diff --git a/lld/ELF/Arch/AVR.cpp b/lld/ELF/Arch/AVR.cpp
index 492733a07434f..5276ff9840591 100644
--- a/lld/ELF/Arch/AVR.cpp
+++ b/lld/ELF/Arch/AVR.cpp
@@ -44,8 +44,8 @@ class AVR final : public TargetInfo {
 public:
   AVR(Ctx &ctx) : TargetInfo(ctx) { needsThunks = true; }
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
                   uint64_t branchAddr, const Symbol &s,
                   int64_t a) const override;
@@ -54,8 +54,8 @@ class AVR final : public TargetInfo {
 };
 } // namespace
 
-RelExpr AVR::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                        StringRef rv_vendor) const {
+RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
+                        const uint8_t *loc) const {
   switch (type) {
   case R_AVR_6:
   case R_AVR_6_ADIW:
diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 41b084b157801..9b33e78731c97 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -33,8 +33,8 @@ class Hexagon final : public TargetInfo {
 public:
   Hexagon(Ctx &);
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
@@ -99,8 +99,8 @@ static uint32_t applyMask(uint32_t mask, uint32_t data) {
   return result;
 }
 
-RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                            StringRef rv_vendor) const {
+RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
+                            const uint8_t *loc) const {
   switch (type) {
   case R_HEX_NONE:
     return R_NONE;
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 1cf6a68bd6e9b..db2c71c3b42b9 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -33,8 +33,8 @@ class LoongArch final : public TargetInfo {
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
   RelType getDynRel(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   bool usesOnlyLowPageBits(RelType type) const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
@@ -416,7 +416,7 @@ RelType LoongArch::getDynRel(RelType type) const {
 }
 
 RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
-                              const uint8_t *loc, StringRef rv_vendor) const {
+                              const uint8_t *loc) const {
   switch (type) {
   case R_LARCH_NONE:
   case R_LARCH_MARK_LA:
diff --git a/lld/ELF/Arch/MSP430.cpp b/lld/ELF/Arch/MSP430.cpp
index 5580cea603a81..03d34436d2959 100644
--- a/lld/ELF/Arch/MSP430.cpp
+++ b/lld/ELF/Arch/MSP430.cpp
@@ -31,8 +31,8 @@ namespace {
 class MSP430 final : public TargetInfo {
 public:
   MSP430(Ctx &);
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
 };
@@ -43,8 +43,8 @@ MSP430::MSP430(Ctx &ctx) : TargetInfo(ctx) {
   trapInstr = {0x43, 0x43, 0x43, 0x43};
 }
 
-RelExpr MSP430::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                           StringRef rv_vendor) const {
+RelExpr MSP430::getRelExpr(RelType type, const Symbol &s,
+                           const uint8_t *loc) const {
   switch (type) {
   case R_MSP430_10_PCREL:
   case R_MSP430_16_PCREL:
diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index a713980631187..f88b021c8ba39 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -23,8 +23,8 @@ template <class ELFT> class MIPS final : public TargetInfo {
 public:
   MIPS(Ctx &);
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor) const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   RelType getDynRel(RelType type) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -77,7 +77,7 @@ template <class ELFT> uint32_t MIPS<ELFT>::calcEFlags() const {
 
 template <class ELFT>
 RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
-                               const uint8_t *loc, StringRef rv_vendor) const {
+                               const uint8_t *loc) const {
   // See comment in the calculateMipsRelChain.
   if (ELFT::Is64Bits || ctx.arg.mipsN32Abi)
     type.v &= 0xff;
diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp
index 6342574bbd127..60a0a38d5f23a 100644
--- a/lld/ELF/Arch/PPC.cpp
+++ b/lld/ELF/Arch/PPC.cpp
@@ -25,8 +25,8 @@ namespace {
 class PPC final : public TargetInfo {
 public:
   PPC(Ctx &);
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writeGotHeader(uint8_t *buf) const override;
@@ -219,8 +219,8 @@ bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
   llvm_unreachable("unsupported relocation type used in branch");
 }
 
-RelExpr PPC::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                        StringRef rv_vendor) const {
+RelExpr PPC::getRelExpr(RelType type, const Symbol &s,
+                        const uint8_t *loc) const {
   switch (type) {
   case R_PPC_NONE:
     return R_NONE;
diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index b406122c9a46f..3cd4a6294e2a8 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -169,8 +169,8 @@ class PPC64 final : public TargetInfo {
   PPC64(Ctx &);
   int getTlsGdRelaxSkip(RelType type) const override;
   uint32_t calcEFlags() const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writePltHeader(uint8_t *buf) const override;
@@ -990,8 +990,8 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
   }
 }
 
-RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                          StringRef rv_vendor) const {
+RelExpr PPC64::getRelExpr(RelType type, const Symbol &s,
+                          const uint8_t *loc) const {
   switch (type) {
   case R_PPC64_NONE:
     return R_NONE;
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index be42d77800c80..4c2e3b64aad9b 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -39,8 +39,10 @@ class RISCV final : public TargetInfo {
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
   RelType getDynRel(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
+  RelExpr getVendorRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
+                           StringRef vendor) const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
   void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
@@ -279,15 +281,7 @@ RelType RISCV::getDynRel(RelType type) const {
 }
 
 RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
-                          const uint8_t *loc, StringRef rv_vendor) const {
-  if (!rv_vendor.empty()) {
-    // TODO: Dispatch to vendor-specific relocation handling.
-    Err(ctx) << getErrorLoc(ctx, loc) << "unknown vendor-specific relocation ("
-             << type.v << ") in vendor namespace \"" << rv_vendor
-             << "\" against symbol " << &s;
-    return R_NONE;
-  }
-
+                          const uint8_t *loc) const {
   switch (type) {
   case R_RISCV_NONE:
     return R_NONE;
@@ -569,6 +563,15 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
   }
 }
 
+RelExpr RISCV::getVendorRelExpr(const RelType type, const Symbol &s,
+                                const uint8_t *loc, StringRef vendor) const {
+  // TODO: Dispatch to vendor-specific relocation handling.
+  Err(ctx) << getErrorLoc(ctx, loc) << "unknown vendor-specific relocation ("
+           << type.v << ") in vendor namespace \"" << vendor
+           << "\" against symbol " << &s;
+  return R_NONE;
+}
+
 void RISCV::relocateVendor(uint8_t *loc, const Relocation &rel, uint64_t val,
                            StringRef vendor) const {
   llvm_unreachable("unknown vendor relocation");
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index c34f433d303ba..86668c50d3c78 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -21,8 +21,8 @@ namespace {
 class SPARCV9 final : public TargetInfo {
 public:
   SPARCV9(Ctx &);
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
   void relocate(uint8_t *loc, const Relocation &rel,
@@ -44,8 +44,8 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo(ctx) {
   defaultImageBase = 0x100000;
 }
 
-RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                            StringRef rv_vendor) const {
+RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
+                            const uint8_t *loc) const {
   switch (type) {
   case R_SPARC_32:
   case R_SPARC_UA32:
diff --git a/lld/ELF/Arch/SystemZ.cpp b/lld/ELF/Arch/SystemZ.cpp
index 8b3ecb0fb8f98..a9125806c0952 100644
--- a/lld/ELF/Arch/SystemZ.cpp
+++ b/lld/ELF/Arch/SystemZ.cpp
@@ -24,8 +24,8 @@ class SystemZ : public TargetInfo {
 public:
   SystemZ(Ctx &);
   int getTlsGdRelaxSkip(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   RelType getDynRel(RelType type) const override;
   void writeGotHeader(uint8_t *buf) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -79,8 +79,8 @@ SystemZ::SystemZ(Ctx &ctx) : TargetInfo(ctx) {
   defaultImageBase = 0x1000000;
 }
 
-RelExpr SystemZ::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                            StringRef rv_vendor) const {
+RelExpr SystemZ::getRelExpr(RelType type, const Symbol &s,
+                            const uint8_t *loc) const {
   switch (type) {
   case R_390_NONE:
     return R_NONE;
diff --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp
index d75c089d0b767..c1980d6e0538f 100644
--- a/lld/ELF/Arch/X86.cpp
+++ b/lld/ELF/Arch/X86.cpp
@@ -23,8 +23,8 @@ class X86 : public TargetInfo {
 public:
   X86(Ctx &);
   int getTlsGdRelaxSkip(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void writeGotPltHeader(uint8_t *buf) const override;
   RelType getDynRel(RelType type) const override;
@@ -74,8 +74,8 @@ int X86::getTlsGdRelaxSkip(RelType type) const {
   return type == R_386_TLS_GOTDESC || type == R_386_TLS_DESC_CALL ? 1 : 2;
 }
 
-RelExpr X86::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                        StringRef rv_vendor) const {
+RelExpr X86::getRelExpr(RelType type, const Symbol &s,
+                        const uint8_t *loc) const {
   switch (type) {
   case R_386_8:
   case R_386_16:
diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index a6eb7bae4696a..488f4803b2cb4 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -28,8 +28,8 @@ class X86_64 : public TargetInfo {
 public:
   X86_64(Ctx &);
   int getTlsGdRelaxSkip(RelType type) const override;
-  RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                     StringRef rv_vendor = "") const override;
+  RelExpr getRelExpr(RelType type, const Symbol &s,
+                     const uint8_t *loc) const override;
   RelType getDynRel(RelType type) const override;
   void writeGotPltHeader(uint8_t *buf) const override;
   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
@@ -361,8 +361,8 @@ bool X86_64::relaxOnce(int pass) const {
   return changed;
 }
 
-RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                           StringRef rv_vendor) const {
+RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
+                           const uint8_t *loc) const {
   switch (type) {
   case R_X86_64_8:
   case R_X86_64_16:
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index f58985115d826..3fa2f6010b052 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -1513,7 +1513,7 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
   }
 
   // Stash the RISCV vendor namespace for the subsequent relocation.
-  if (LLVM_UNLIKELY(ctx.arg.emachine == EM_RISCV) && type == R_RISCV_VENDOR) {
+  if (LLVM_UNLIKELY(ctx.arg.emachine == EM_RISCV && type == R_RISCV_VENDOR)) {
     rv_vendor = sym.getName();
     return;
   }
@@ -1523,8 +1523,13 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
   if (offset == uint64_t(-1))
     return;
 
-  RelExpr expr = ctx.target->getRelExpr(
-      type, sym, sec->content().data() + offset, rv_vendor);
+  RelExpr expr;
+  if (rv_vendor.empty()) {
+    expr = ctx.target->getRelExpr(type, sym, sec->content().data() + offset);
+  } else {
+    expr = ctx.target->getVendorRelExpr(
+        type, sym, sec->content().data() + offset, rv_vendor);
+  }
   int64_t addend = RelTy::HasAddend
                        ? getAddend<ELFT>(rel)
                        : ctx.target->getImplicitAddend(
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 6aad5353cc1dc..8277d20b87f0b 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -31,8 +31,12 @@ class TargetInfo {
 public:
   TargetInfo(Ctx &ctx) : ctx(ctx) {}
   virtual uint32_t calcEFlags() const { return 0; }
-  virtual RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc,
-                             StringRef rv_vendor = "") const = 0;
+  virtual RelExpr getRelExpr(RelType type, const Symbol &s,
+                             const uint8_t *loc) const = 0;
+  virtual RelExpr getVendorRelExpr(RelType type, const Symbol &s,
+                                   const uint8_t *loc, StringRef vendor) const {
+    return R_NONE;
+  };
   virtual RelType getDynRel(RelType type) const { return 0; }
   virtual void writeGotPltHeader(uint8_t *buf) const {}
   virtual void writeGotHeader(uint8_t *buf) const {}

>From d25900565e7617776457496e2b20177f9b3ffa15 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Mon, 22 Sep 2025 23:13:54 +0900
Subject: [PATCH 3/6] Remove remnant of an earlier attempt that was
 accidentally included.

---
 lld/ELF/Arch/RISCV.cpp | 33 ---------------------------------
 1 file changed, 33 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 4c2e3b64aad9b..f09d4a8febf8e 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -58,10 +58,6 @@ class RISCV final : public TargetInfo {
   bool synthesizeAlign(uint64_t &dot, InputSection *sec) override;
   void finalizeRelax(int passes) const override;
 
-  // For vendor-specific relocations.
-  void relocateVendor(uint8_t *loc, const Relocation &rel, uint64_t val,
-                      StringRef vendor = "") const;
-
   // The following two variables are used by synthesized ALIGN relocations.
   InputSection *baseSec = nullptr;
   // r_offset and r_addend pairs.
@@ -572,15 +568,6 @@ RelExpr RISCV::getVendorRelExpr(const RelType type, const Symbol &s,
   return R_NONE;
 }
 
-void RISCV::relocateVendor(uint8_t *loc, const Relocation &rel, uint64_t val,
-                           StringRef vendor) const {
-  llvm_unreachable("unknown vendor relocation");
-  Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation type "
-           << llvm::utostr(rel.type.v) << " in vendor namespace \"" << vendor
-           << "\"";
-  return;
-}
-
 static bool relaxable(ArrayRef<Relocation> relocs, size_t i) {
   return i + 1 != relocs.size() && relocs[i + 1].type == R_RISCV_RELAX;
 }
@@ -643,26 +630,6 @@ void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
     uint8_t *loc = buf + rel.offset;
     uint64_t val = sec.getRelocTargetVA(ctx, rel, secAddr + rel.offset);
 
-    if (rel.type == R_RISCV_VENDOR) {
-      // Vendor-specific relocations are indicated by a pair of a R_RISCV_VENDOR
-      // relocation followed by relocation in the vendor's private namespace.
-      // The vendor name is identified by the symbol name referenced by the
-      // R_RISCV_VENDOR relocation.
-      StringRef vendor = rel.sym->getName();
-
-      // Consume the second relocation as well.
-      assert(i != size - 1 &&
-             "R_RISCV_VENDOR relocation cannot be the final relocation!");
-      i += 1;
-
-      const Relocation &rel2 = relocs[i];
-      uint8_t *loc2 = buf + rel2.offset;
-      uint64_t val2 = sec.getRelocTargetVA(ctx, rel2, secAddr + rel2.offset);
-
-      relocateVendor(loc2, rel2, val2, vendor);
-      continue;
-    }
-
     switch (rel.expr) {
     case R_RELAX_HINT:
       continue;

>From 3626594f222244137f59f97b225d6abc2a11f86c Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Tue, 23 Sep 2025 18:27:55 +0900
Subject: [PATCH 4/6] Update for review comments.

---
 lld/ELF/Relocations.cpp | 2 +-
 lld/ELF/Target.h        | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 3fa2f6010b052..4a4e1de632491 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -1524,7 +1524,7 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
     return;
 
   RelExpr expr;
-  if (rv_vendor.empty()) {
+  if (LLVM_LIKELY(rv_vendor.empty())) {
     expr = ctx.target->getRelExpr(type, sym, sec->content().data() + offset);
   } else {
     expr = ctx.target->getVendorRelExpr(
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 8277d20b87f0b..8e58681db44f1 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -35,6 +35,7 @@ class TargetInfo {
                              const uint8_t *loc) const = 0;
   virtual RelExpr getVendorRelExpr(RelType type, const Symbol &s,
                                    const uint8_t *loc, StringRef vendor) const {
+    Err(ctx) << "target doesn't support vendor-specific relocations";
     return R_NONE;
   };
   virtual RelType getDynRel(RelType type) const { return 0; }

>From d27799a0fc1e52b0db0c000e0f16a47e0b953a31 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Wed, 24 Sep 2025 10:27:29 +0900
Subject: [PATCH 5/6] Update for review feedback.

---
 lld/ELF/Relocations.cpp                 | 10 +++++-----
 lld/test/ELF/riscv-vendor-relocations.s |  8 ++++----
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 4a4e1de632491..178dd9f05278f 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -457,7 +457,7 @@ class RelocationScanner {
   Ctx &ctx;
   InputSectionBase *sec;
   OffsetGetter getter;
-  StringRef rv_vendor = "";
+  StringRef rvVendor;
 
   // End of relocations, used by Mips/PPC64.
   const void *end = nullptr;
@@ -1514,7 +1514,7 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
 
   // Stash the RISCV vendor namespace for the subsequent relocation.
   if (LLVM_UNLIKELY(ctx.arg.emachine == EM_RISCV && type == R_RISCV_VENDOR)) {
-    rv_vendor = sym.getName();
+    rvVendor = sym.getName();
     return;
   }
 
@@ -1524,11 +1524,11 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
     return;
 
   RelExpr expr;
-  if (LLVM_LIKELY(rv_vendor.empty())) {
+  if (LLVM_LIKELY(rvVendor.empty())) {
     expr = ctx.target->getRelExpr(type, sym, sec->content().data() + offset);
   } else {
     expr = ctx.target->getVendorRelExpr(
-        type, sym, sec->content().data() + offset, rv_vendor);
+        type, sym, sec->content().data() + offset, rvVendor);
   }
   int64_t addend = RelTy::HasAddend
                        ? getAddend<ELFT>(rel)
@@ -1540,7 +1540,7 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
     addend += getPPC64TocBase(ctx);
 
   // A RISCV vendor namespace only applies to a single relocation.
-  rv_vendor = "";
+  rvVendor = "";
 
   // Ignore R_*_NONE and other marker relocations.
   if (expr == R_NONE)
diff --git a/lld/test/ELF/riscv-vendor-relocations.s b/lld/test/ELF/riscv-vendor-relocations.s
index 69e2cf6f83e64..93ec761f52238 100644
--- a/lld/test/ELF/riscv-vendor-relocations.s
+++ b/lld/test/ELF/riscv-vendor-relocations.s
@@ -4,24 +4,24 @@
   .option exact
 
   qc.e.bgeui s0, 20, TARGET
-# CHECK: unknown vendor-specific relocation (193) in vendor namespace "QUALCOMM" against symbol TARGET
+# CHECK: error: {{.*}} unknown vendor-specific relocation (193) in vendor namespace "QUALCOMM" against symbol TARGET
 
   .global QUALCOMM
 QUALCOMM:
   nop
 
   qc.e.bgeui s0, 20, TARGET
-# CHECK: unknown vendor-specific relocation (193) in vendor namespace "QUALCOMM" against symbol TARGET
+# CHECK: error: {{.*}} unknown vendor-specific relocation (193) in vendor namespace "QUALCOMM" against symbol TARGET
 
   nds.bbc t0, 7, TARGET
-# CHECK: unknown vendor-specific relocation (241) in vendor namespace "ANDES" against symbol TARGET
+# CHECK: error: {{.*}} unknown vendor-specific relocation (241) in vendor namespace "ANDES" against symbol TARGET
 
   .global ANDES
 ANDES:
   nop
 
   nds.bbs t0, 7, TARGET
-# CHECK: unknown vendor-specific relocation (241) in vendor namespace "ANDES" against symbol TARGET
+# CHECK: error: {{.*}} unknown vendor-specific relocation (241) in vendor namespace "ANDES" against symbol TARGET
 
   .global TARGET
 TARGET:

>From f8664fd576f20b432b3096d41e7e2b983a744313 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Wed, 24 Sep 2025 10:52:23 +0900
Subject: [PATCH 6/6] Use a fake vendor relocation in the testcase.

---
 lld/test/ELF/riscv-vendor-relocations.s | 28 +++++++------------------
 1 file changed, 8 insertions(+), 20 deletions(-)

diff --git a/lld/test/ELF/riscv-vendor-relocations.s b/lld/test/ELF/riscv-vendor-relocations.s
index 93ec761f52238..d9bc641a38e7d 100644
--- a/lld/test/ELF/riscv-vendor-relocations.s
+++ b/lld/test/ELF/riscv-vendor-relocations.s
@@ -1,28 +1,16 @@
-# RUN: llvm-mc -triple riscv32 -mattr=+experimental-xqcibi,+xandesperf %s -filetype=obj -o %t.o
+# RUN: llvm-mc -triple riscv32 %s -filetype=obj -o %t.o
 # RUN: not ld.lld -pie %t.o -o /dev/null 2>&1 | FileCheck %s
 
   .option exact
 
-  qc.e.bgeui s0, 20, TARGET
-# CHECK: error: {{.*}} unknown vendor-specific relocation (193) in vendor namespace "QUALCOMM" against symbol TARGET
-
-  .global QUALCOMM
-QUALCOMM:
+  .global TARGET
+TARGET:
   nop
 
-  qc.e.bgeui s0, 20, TARGET
-# CHECK: error: {{.*}} unknown vendor-specific relocation (193) in vendor namespace "QUALCOMM" against symbol TARGET
-
-  nds.bbc t0, 7, TARGET
-# CHECK: error: {{.*}} unknown vendor-specific relocation (241) in vendor namespace "ANDES" against symbol TARGET
-
-  .global ANDES
-ANDES:
+.global INVALID_VENDOR
+.reloc 1f, R_RISCV_VENDOR, INVALID_VENDOR+0
+.reloc 1f, R_RISCV_CUSTOM255, TARGET
+1:
   nop
 
-  nds.bbs t0, 7, TARGET
-# CHECK: error: {{.*}} unknown vendor-specific relocation (241) in vendor namespace "ANDES" against symbol TARGET
-
-  .global TARGET
-TARGET:
-  nop
+# CHECK: error: {{.*}} unknown vendor-specific relocation (255) in vendor namespace "INVALID_VENDOR" against symbol TARGET



More information about the llvm-commits mailing list