[lld] [LLD] Add support for statically resolved vendor-specific RISCV relocations. (PR #169273)

via llvm-commits llvm-commits at lists.llvm.org
Sun Nov 23 19:52:02 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-risc-v

Author: Owen Anderson (resistor)

<details>
<summary>Changes</summary>

This is achieved by using some of the bits of RelType to tag vendor namespaces.
This change also adds a relocation iterator for RISCV that folds vendor namespaces
into the RelType of the following relocation.

This patch is extracted from the implementation of RISCV vendor-specific relocations
in the CHERIoT LLVM downstream: https://github.com/CHERIoT-Platform/llvm-project/commit/3d6d6f7d9480b590731cbcf4b4817e1fa3049854


---
Full diff: https://github.com/llvm/llvm-project/pull/169273.diff


4 Files Affected:

- (modified) lld/ELF/Arch/RISCV.cpp (+40-8) 
- (added) lld/ELF/Arch/RISCVInternalRelocations.h (+113) 
- (modified) lld/ELF/Target.cpp (+9) 
- (modified) lld/test/ELF/riscv-vendor-relocations.s (+11-4) 


``````````diff
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 5ed89e47c672e..b3a0f7e804020 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -8,6 +8,7 @@
 
 #include "InputFiles.h"
 #include "OutputSections.h"
+#include "RISCVInternalRelocations.h"
 #include "RelocScan.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
@@ -345,8 +346,15 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
   case R_RISCV_SUB_ULEB128:
     return RE_RISCV_LEB128;
   default:
-    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v
-             << ") against symbol " << &s;
+    if (type.v & INTERNAL_RISCV_VENDOR_MASK) {
+      Err(ctx) << getErrorLoc(ctx, loc)
+               << "unsupported vendor-specific relocation " << type
+               << " against symbol " << &s;
+      return R_NONE;
+    }
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation ("
+             << (type.v & ~INTERNAL_RISCV_VENDOR_MASK) << ") against symbol "
+             << &s;
     return R_NONE;
   }
 }
@@ -859,7 +867,7 @@ static bool relax(Ctx &ctx, int pass, InputSection &sec) {
 
   std::fill_n(aux.relocTypes.get(), relocs.size(), R_RISCV_NONE);
   aux.writes.clear();
-  for (auto [i, r] : llvm::enumerate(relocs)) {
+  for (auto [i, r] : llvm::enumerate(riscv_vendor_relocs(relocs))) {
     const uint64_t loc = secAddr + r.offset - delta;
     uint32_t &cur = aux.relocDeltas[i], remove = 0;
     switch (r.type) {
@@ -1503,12 +1511,18 @@ void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
       rvVendor = sym.getName();
       continue;
     } else if (!rvVendor.empty()) {
-      Err(ctx) << getErrorLoc(ctx, loc)
-               << "unknown vendor-specific relocation (" << type.v
-               << ") in namespace '" << rvVendor << "' against symbol '" << &sym
-               << "'";
+      uint32_t VendorFlag = getRISCVVendorRelMarker(rvVendor);
+      if (!VendorFlag) {
+        Err(ctx) << getErrorLoc(ctx, loc)
+                 << "unknown vendor-specific relocation (" << type.v
+                 << ") in namespace '" << rvVendor << "' against symbol '"
+                 << &sym << "'";
+        rvVendor = "";
+        continue;
+      }
+
       rvVendor = "";
-      continue;
+      type.v |= VendorFlag;
     }
 
     rs.scan<ELFT, RelTy>(it, type, rs.getAddend<ELFT>(*it, type));
@@ -1533,3 +1547,21 @@ template <class ELFT> void RISCV::scanSection1(InputSectionBase &sec) {
 void RISCV::scanSection(InputSectionBase &sec) {
   invokeELFT(scanSection1, sec);
 }
+
+namespace lld::elf {
+uint32_t getRISCVVendorRelMarker(StringRef rvVendor) {
+  return StringSwitch<uint32_t>(rvVendor)
+      .Case("QUALCOMM", INTERNAL_RISCV_VENDOR_QUALCOMM)
+      .Case("ANDES", INTERNAL_RISCV_VENDOR_ANDES)
+      .Default(0);
+}
+
+std::optional<StringRef> getRISCVVendorString(RelType ty) {
+  if ((ty.v & INTERNAL_RISCV_VENDOR_MASK) == INTERNAL_RISCV_VENDOR_QUALCOMM)
+    return "QUALCOMM";
+  if ((ty.v & INTERNAL_RISCV_VENDOR_MASK) == INTERNAL_RISCV_VENDOR_ANDES)
+    return "ANDES";
+  return std::nullopt;
+}
+
+} // namespace lld::elf
\ No newline at end of file
diff --git a/lld/ELF/Arch/RISCVInternalRelocations.h b/lld/ELF/Arch/RISCVInternalRelocations.h
new file mode 100644
index 0000000000000..3153dd0a3f9e7
--- /dev/null
+++ b/lld/ELF/Arch/RISCVInternalRelocations.h
@@ -0,0 +1,113 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_ARCH_RISCVINTERNALRELOCATIONS_H
+#define LLD_ELF_ARCH_RISCVINTERNALRELOCATIONS_H
+
+#include "Relocations.h"
+#include "Symbols.h"
+
+namespace lld::elf {
+
+// Bit 8 of RelType is used to indicate linker-internal relocations that are
+// not vendor-specific.
+// These are internal relocation numbers for GP/X0 relaxation. They aren't part
+// of the psABI spec.
+constexpr uint32_t INTERNAL_R_RISCV_GPREL_I = 256;
+constexpr uint32_t INTERNAL_R_RISCV_GPREL_S = 257;
+constexpr uint32_t INTERNAL_R_RISCV_X0REL_I = 258;
+constexpr uint32_t INTERNAL_R_RISCV_X0REL_S = 259;
+
+// Bits 9 -> 31 of RelType are used to indicate vendor-specific relocations.
+constexpr uint32_t INTERNAL_RISCV_VENDOR_MASK = 0xFFFFFFFF << 9;
+constexpr uint32_t INTERNAL_RISCV_VENDOR_QUALCOMM = 1 << 9;
+constexpr uint32_t INTERNAL_RISCV_VENDOR_ANDES = 2 << 9;
+
+constexpr uint32_t INTERNAL_RISCV_QC_ABS20_U =
+    INTERNAL_RISCV_VENDOR_QUALCOMM | llvm::ELF::R_RISCV_QC_ABS20_U;
+constexpr uint32_t INTERNAL_RISCV_QC_E_BRANCH =
+    INTERNAL_RISCV_VENDOR_QUALCOMM | llvm::ELF::R_RISCV_QC_E_BRANCH;
+constexpr uint32_t INTERNAL_RISCV_QC_E_32 =
+    INTERNAL_RISCV_VENDOR_QUALCOMM | llvm::ELF::R_RISCV_QC_E_32;
+constexpr uint32_t INTERNAL_RISCV_QC_E_CALL_PLT =
+    INTERNAL_RISCV_VENDOR_QUALCOMM | llvm::ELF::R_RISCV_QC_E_CALL_PLT;
+
+constexpr uint32_t INTERNAL_RISCV_NDS_BRANCH_10 =
+    INTERNAL_RISCV_VENDOR_ANDES | llvm::ELF::R_RISCV_NDS_BRANCH_10;
+
+uint32_t getRISCVVendorRelMarker(llvm::StringRef rvVendor);
+std::optional<llvm::StringRef> getRISCVVendorString(RelType ty);
+
+class vendor_reloc_iterator {
+public:
+  using iterator_category = std::forward_iterator_tag;
+  using value_type = Relocation;
+  using difference_type = std::ptrdiff_t;
+  using pointer = Relocation *;
+  using reference = Relocation; // returned by value
+
+  vendor_reloc_iterator(MutableArrayRef<Relocation>::iterator i,
+                        MutableArrayRef<Relocation>::iterator e)
+      : it(i), end(e) {}
+
+  // Dereference
+  Relocation operator*() const {
+    Relocation r = *it;
+    r.type.v |= rvVendorFlag;
+    return r;
+  }
+
+  struct vendor_reloc_proxy {
+    Relocation r;
+    const Relocation *operator->() const { return &r; }
+  };
+
+  vendor_reloc_proxy operator->() const {
+    return vendor_reloc_proxy{this->operator*()};
+  }
+
+  vendor_reloc_iterator &operator++() {
+    ++it;
+    if (it != end && it->type == llvm::ELF::R_RISCV_VENDOR) {
+      rvVendorFlag = getRISCVVendorRelMarker(it->sym->getName());
+      ++it;
+    } else {
+      rvVendorFlag = 0;
+    }
+    return *this;
+  }
+
+  vendor_reloc_iterator operator++(int) {
+    vendor_reloc_iterator tmp(*this);
+    ++(*this);
+    return tmp;
+  }
+
+  bool operator==(const vendor_reloc_iterator &other) const {
+    return it == other.it;
+  }
+  bool operator!=(const vendor_reloc_iterator &other) const {
+    return it != other.it;
+  }
+
+  Relocation *getUnderlyingRelocation() const { return &*it; }
+
+private:
+  MutableArrayRef<Relocation>::iterator it;
+  MutableArrayRef<Relocation>::iterator end;
+  uint32_t rvVendorFlag = 0;
+};
+
+inline auto riscv_vendor_relocs(MutableArrayRef<Relocation> arr) {
+  return llvm::make_range(vendor_reloc_iterator(arr.begin(), arr.end()),
+                          vendor_reloc_iterator(arr.end(), arr.end()));
+}
+
+} // namespace lld::elf
+
+#endif
\ No newline at end of file
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 89e4dbeed3109..3fc3e3f16e9e0 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -24,6 +24,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Target.h"
+#include "Arch/RISCVInternalRelocations.h"
 #include "InputFiles.h"
 #include "OutputSections.h"
 #include "RelocScan.h"
@@ -40,6 +41,14 @@ using namespace lld::elf;
 
 std::string elf::toStr(Ctx &ctx, RelType type) {
   StringRef s = getELFRelocationTypeName(ctx.arg.emachine, type);
+  if (ctx.arg.emachine == EM_RISCV && s == "Unknown") {
+    auto VendorString = getRISCVVendorString(type);
+    if (VendorString)
+      s = getRISCVVendorRelocationTypeName(type & ~INTERNAL_RISCV_VENDOR_MASK,
+                                           *VendorString);
+    if (s == "Unknown")
+      return ("Unknown vendor-specific (" + Twine(type) + ")").str();
+  }
   if (s == "Unknown")
     return ("Unknown (" + Twine(type) + ")").str();
   return std::string(s);
diff --git a/lld/test/ELF/riscv-vendor-relocations.s b/lld/test/ELF/riscv-vendor-relocations.s
index b0f3c4a30d060..f121adec95cd0 100644
--- a/lld/test/ELF/riscv-vendor-relocations.s
+++ b/lld/test/ELF/riscv-vendor-relocations.s
@@ -8,12 +8,19 @@
 TARGET:
   nop
 
-.global INVALID_VENDOR
+.local INVALID_VENDOR
+.local QUALCOMM
+.local ANDES
 .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: {{.*}}:(.text+0x4): malformed consecutive R_RISCV_VENDOR relocations
 # CHECK: error: {{.*}}:(.text+0x4): unknown vendor-specific relocation (255) in namespace 'INVALID_VENDOR' against symbol 'TARGET'
+.reloc 1f, R_RISCV_VENDOR, QUALCOMM+0
+.reloc 1f, R_RISCV_CUSTOM192, TARGET
+# CHECK: error: {{.*}}:(.text+0x4): unsupported vendor-specific relocation R_RISCV_QC_ABS20_U against symbol TARGET
+.reloc 1f, R_RISCV_VENDOR, ANDES+0
+.reloc 1f, R_RISCV_CUSTOM241, TARGET
+# CHECK: error: {{.*}}:(.text+0x4): unsupported vendor-specific relocation R_RISCV_NDS_BRANCH_10 against symbol TARGET
+1:
+  nop

``````````

</details>


https://github.com/llvm/llvm-project/pull/169273


More information about the llvm-commits mailing list