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

Owen Anderson via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 22 01:11:49 PDT 2025


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

>From fc463150f39b476fa73da260f75825d8f3b5fe82 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Tue, 14 Oct 2025 23:43:20 +0900
Subject: [PATCH 1/5] [lld] Add infrastructure for handling RISCV
 vendor-specific relocations.

---
 lld/ELF/Arch/RISCV.cpp              | 83 +++++++++++++++++++++++++++++
 lld/test/riscv-vendor-relocations.s | 16 ++++++
 2 files changed, 99 insertions(+)
 create mode 100644 lld/test/riscv-vendor-relocations.s

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index dc2ab97e9d9be..f7d570adc8b77 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -8,6 +8,7 @@
 
 #include "InputFiles.h"
 #include "OutputSections.h"
+#include "RelocScan.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -38,6 +39,10 @@ class RISCV final : public TargetInfo {
   void writePltHeader(uint8_t *buf) const override;
   void writePlt(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;
   RelType getDynRel(RelType type) const override;
   RelExpr getRelExpr(RelType type, const Symbol &s,
                      const uint8_t *loc) const override;
@@ -278,6 +283,7 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
                           const uint8_t *loc) const {
   switch (type) {
   case R_RISCV_NONE:
+  case R_RISCV_VENDOR:
     return R_NONE;
   case R_RISCV_32:
   case R_RISCV_64:
@@ -1476,3 +1482,80 @@ void elf::mergeRISCVAttributesSections(Ctx &ctx) {
 }
 
 void elf::setRISCVTargetInfo(Ctx &ctx) { ctx.target.reset(new RISCV(ctx)); }
+
+class RISCVRelocScan : public RelocScan {
+public:
+  StringRef rvVendor;
+
+  RISCVRelocScan(Ctx &ctx, InputSectionBase *sec = nullptr)
+      : RelocScan(ctx, sec) {}
+  template <class ELFT, class RelTy>
+  void scan(typename Relocs<RelTy>::const_iterator &it, RelType type,
+            int64_t addend);
+};
+
+template <class ELFT, class RelTy>
+void RISCVRelocScan::scan(typename Relocs<RelTy>::const_iterator &it,
+                          RelType type, int64_t addend) {
+  const RelTy &rel = *it;
+  uint32_t symIndex = rel.getSymbol(false);
+  Symbol &sym = sec->getFile<ELFT>()->getSymbol(symIndex);
+
+  if (type == R_RISCV_VENDOR) {
+    if (!rvVendor.empty())
+      Err(ctx) << "found consecutive R_RISCV_VENDOR relocations";
+    rvVendor = sym.getName();
+    return;
+  } else if (!rvVendor.empty()) {
+    Err(ctx) << getErrorLoc(ctx, sec->content().data() + it->r_offset)
+             << "unknown vendor-specific relocation (" << type.v
+             << ") in vendor namespace \"" << rvVendor << "\" against symbol "
+             << &sym;
+    return;
+  }
+
+  RelocScan::scan<ELFT, RelTy>(it, type, addend);
+}
+
+template <class ELFT, class RelTy>
+void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
+  RISCVRelocScan 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.
+  SmallVector<RelTy, 0> storage;
+  if (ctx.arg.emachine == EM_S390)
+    rels = sortRels(rels, storage);
+
+  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.branchToBranch)
+    llvm::stable_sort(sec.relocs(),
+                      [](const Relocation &lhs, const Relocation &rhs) {
+                        return lhs.offset < rhs.offset;
+                      });
+}
+
+template <class ELFT> void RISCV::scanSection1(InputSectionBase &sec) {
+  const RelsOrRelas<ELFT> rels = sec.template relsOrRelas<ELFT>();
+  if (rels.areRelocsCrel())
+    scanSectionImpl<ELFT>(sec, rels.crels);
+  else if (rels.areRelocsRel())
+    scanSectionImpl<ELFT>(sec, rels.rels);
+  else
+    scanSectionImpl<ELFT>(sec, rels.relas);
+}
+
+void RISCV::scanSection(InputSectionBase &sec) {
+  invokeELFT(scanSection1, sec);
+}
diff --git a/lld/test/riscv-vendor-relocations.s b/lld/test/riscv-vendor-relocations.s
new file mode 100644
index 0000000000000..9358f48104ffe
--- /dev/null
+++ b/lld/test/riscv-vendor-relocations.s
@@ -0,0 +1,16 @@
+# 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
+
+  .global TARGET
+TARGET:
+  nop
+
+.global INVALID_VENDOR
+.reloc 1f, R_RISCV_VENDOR, INVALID_VENDOR+0
+.reloc 1f, R_RISCV_CUSTOM255, TARGET
+1:
+  nop
+
+# CHECK: error: {{.*}} unknown vendor-specific relocation (255) in vendor namespace "INVALID_VENDOR" against symbol TARGET
\ No newline at end of file

>From 6a20ab874831cbbab868301dbaee38606251ad33 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Wed, 15 Oct 2025 20:59:49 +0900
Subject: [PATCH 2/5] Update for review feedback

---
 lld/ELF/Arch/RISCV.cpp              | 23 +++++++----------------
 lld/test/riscv-vendor-relocations.s |  4 +++-
 2 files changed, 10 insertions(+), 17 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index f7d570adc8b77..40965a38e4856 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -1503,7 +1503,8 @@ void RISCVRelocScan::scan(typename Relocs<RelTy>::const_iterator &it,
 
   if (type == R_RISCV_VENDOR) {
     if (!rvVendor.empty())
-      Err(ctx) << "found consecutive R_RISCV_VENDOR relocations";
+      Err(ctx) << getErrorLoc(ctx, sec->content().data() + it->r_offset)
+               << "malformed consecutive R_RISCV_VENDOR relocations";
     rvVendor = sym.getName();
     return;
   } else if (!rvVendor.empty()) {
@@ -1511,6 +1512,7 @@ void RISCVRelocScan::scan(typename Relocs<RelTy>::const_iterator &it,
              << "unknown vendor-specific relocation (" << type.v
              << ") in vendor namespace \"" << rvVendor << "\" against symbol "
              << &sym;
+    rvVendor = "";
     return;
   }
 
@@ -1523,12 +1525,6 @@ void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
   // 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.
-  SmallVector<RelTy, 0> storage;
-  if (ctx.arg.emachine == EM_S390)
-    rels = sortRels(rels, storage);
-
   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));
@@ -1537,21 +1533,16 @@ void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
   // 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.branchToBranch)
-    llvm::stable_sort(sec.relocs(),
-                      [](const Relocation &lhs, const Relocation &rhs) {
-                        return lhs.offset < rhs.offset;
-                      });
+  llvm::stable_sort(sec.relocs(),
+                    [](const Relocation &lhs, const Relocation &rhs) {
+                      return lhs.offset < rhs.offset;
+                    });
 }
 
 template <class ELFT> void RISCV::scanSection1(InputSectionBase &sec) {
   const RelsOrRelas<ELFT> rels = sec.template relsOrRelas<ELFT>();
   if (rels.areRelocsCrel())
     scanSectionImpl<ELFT>(sec, rels.crels);
-  else if (rels.areRelocsRel())
-    scanSectionImpl<ELFT>(sec, rels.rels);
   else
     scanSectionImpl<ELFT>(sec, rels.relas);
 }
diff --git a/lld/test/riscv-vendor-relocations.s b/lld/test/riscv-vendor-relocations.s
index 9358f48104ffe..68a6364ed18b1 100644
--- a/lld/test/riscv-vendor-relocations.s
+++ b/lld/test/riscv-vendor-relocations.s
@@ -9,8 +9,10 @@ TARGET:
 
 .global INVALID_VENDOR
 .reloc 1f, R_RISCV_VENDOR, INVALID_VENDOR+0
+.reloc 1f, R_RISCV_VENDOR, INVALID_VENDOR+0
 .reloc 1f, R_RISCV_CUSTOM255, TARGET
 1:
   nop
 
-# CHECK: error: {{.*}} unknown vendor-specific relocation (255) in vendor namespace "INVALID_VENDOR" against symbol TARGET
\ No newline at end of file
+# CHECK: error: {{.*}} malformed consecutive R_RISCV_VENDOR relocations
+# CHECK: error: {{.*}} unknown vendor-specific relocation (255) in vendor namespace "INVALID_VENDOR" against symbol TARGET

>From 1b6be66b7319fb919a1faf2fbd97187599a71086 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Thu, 16 Oct 2025 10:59:10 +0900
Subject: [PATCH 3/5] Update for review feedback.

---
 lld/ELF/Arch/RISCV.cpp | 63 +++++++++++++++---------------------------
 1 file changed, 23 insertions(+), 40 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 40965a38e4856..6b226255e345b 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -1483,56 +1483,39 @@ void elf::mergeRISCVAttributesSections(Ctx &ctx) {
 
 void elf::setRISCVTargetInfo(Ctx &ctx) { ctx.target.reset(new RISCV(ctx)); }
 
-class RISCVRelocScan : public RelocScan {
-public:
-  StringRef rvVendor;
-
-  RISCVRelocScan(Ctx &ctx, InputSectionBase *sec = nullptr)
-      : RelocScan(ctx, sec) {}
-  template <class ELFT, class RelTy>
-  void scan(typename Relocs<RelTy>::const_iterator &it, RelType type,
-            int64_t addend);
-};
-
-template <class ELFT, class RelTy>
-void RISCVRelocScan::scan(typename Relocs<RelTy>::const_iterator &it,
-                          RelType type, int64_t addend) {
-  const RelTy &rel = *it;
-  uint32_t symIndex = rel.getSymbol(false);
-  Symbol &sym = sec->getFile<ELFT>()->getSymbol(symIndex);
-
-  if (type == R_RISCV_VENDOR) {
-    if (!rvVendor.empty())
-      Err(ctx) << getErrorLoc(ctx, sec->content().data() + it->r_offset)
-               << "malformed consecutive R_RISCV_VENDOR relocations";
-    rvVendor = sym.getName();
-    return;
-  } else if (!rvVendor.empty()) {
-    Err(ctx) << getErrorLoc(ctx, sec->content().data() + it->r_offset)
-             << "unknown vendor-specific relocation (" << type.v
-             << ") in vendor namespace \"" << rvVendor << "\" against symbol "
-             << &sym;
-    rvVendor = "";
-    return;
-  }
-
-  RelocScan::scan<ELFT, RelTy>(it, type, addend);
-}
-
 template <class ELFT, class RelTy>
 void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
-  RISCVRelocScan rs(ctx, &sec);
+  RelocScan rs(ctx, &sec);
   // Many relocations end up in sec.relocations.
   sec.relocations.reserve(rels.size());
 
+  StringRef rvVendor;
   for (auto it = rels.begin(); it != rels.end(); ++it) {
-    auto type = it->getType(false);
+    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;
+
+    if (type == R_RISCV_VENDOR) {
+      if (!rvVendor.empty())
+        Err(ctx) << getErrorLoc(ctx, loc)
+                 << "malformed consecutive R_RISCV_VENDOR relocations";
+      rvVendor = sym.getName();
+      continue;
+    } else if (!rvVendor.empty()) {
+      Err(ctx) << getErrorLoc(ctx, loc)
+               << "unknown vendor-specific relocation (" << type.v
+               << ") in vendor namespace \"" << rvVendor << "\" against symbol "
+               << &sym;
+      rvVendor = "";
+      continue;
+    }
+
     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.
+  // R_RISCV_PCREL_HI20.
   llvm::stable_sort(sec.relocs(),
                     [](const Relocation &lhs, const Relocation &rhs) {
                       return lhs.offset < rhs.offset;

>From a269770e6bf36e2c0976195c81d1e584d4250a8c Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Mon, 20 Oct 2025 18:07:36 +0900
Subject: [PATCH 4/5] Update test for review feedback

---
 lld/test/riscv-vendor-relocations.s | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lld/test/riscv-vendor-relocations.s b/lld/test/riscv-vendor-relocations.s
index 68a6364ed18b1..f78f0e3b61911 100644
--- a/lld/test/riscv-vendor-relocations.s
+++ b/lld/test/riscv-vendor-relocations.s
@@ -14,5 +14,5 @@ TARGET:
 1:
   nop
 
-# CHECK: error: {{.*}} malformed consecutive R_RISCV_VENDOR relocations
-# CHECK: error: {{.*}} unknown vendor-specific relocation (255) in vendor namespace "INVALID_VENDOR" against symbol TARGET
+# CHECK: error: {{.*}}:(.text+0x4): malformed consecutive R_RISCV_VENDOR relocations
+# CHECK: error: {{.*}}:(.text+0x4): unknown vendor-specific relocation (255) in vendor namespace "INVALID_VENDOR" against symbol TARGET

>From a1bbc9306d9fba20894f07953b5643ad1ca8cb6b Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Wed, 22 Oct 2025 17:08:31 +0900
Subject: [PATCH 5/5] Update error messages per review feedback.

---
 lld/ELF/Arch/RISCV.cpp              | 4 ++--
 lld/test/riscv-vendor-relocations.s | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 6b226255e345b..5ed89e47c672e 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -1505,8 +1505,8 @@ void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     } else if (!rvVendor.empty()) {
       Err(ctx) << getErrorLoc(ctx, loc)
                << "unknown vendor-specific relocation (" << type.v
-               << ") in vendor namespace \"" << rvVendor << "\" against symbol "
-               << &sym;
+               << ") in namespace '" << rvVendor << "' against symbol '" << &sym
+               << "'";
       rvVendor = "";
       continue;
     }
diff --git a/lld/test/riscv-vendor-relocations.s b/lld/test/riscv-vendor-relocations.s
index f78f0e3b61911..6ccec528f3d40 100644
--- a/lld/test/riscv-vendor-relocations.s
+++ b/lld/test/riscv-vendor-relocations.s
@@ -15,4 +15,4 @@ TARGET:
   nop
 
 # CHECK: error: {{.*}}:(.text+0x4): malformed consecutive R_RISCV_VENDOR relocations
-# CHECK: error: {{.*}}:(.text+0x4): unknown vendor-specific relocation (255) in vendor namespace "INVALID_VENDOR" against symbol TARGET
+# CHECK: error: {{.*}}:(.text+0x4): unknown vendor-specific relocation (255) in namespace 'INVALID_VENDOR' against symbol 'TARGET'



More information about the llvm-commits mailing list