Skip to content

Conversation

@resistor
Copy link
Collaborator

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: CHERIoT-Platform/llvm-project@3d6d6f7

…ations.

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: CHERIoT-Platform/llvm-project@3d6d6f7
@llvmbot
Copy link
Member

llvmbot commented Nov 24, 2025

@llvm/pr-subscribers-lld

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

Author: Owen Anderson (resistor)

Changes

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: CHERIoT-Platform/llvm-project@3d6d6f7


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 --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

@llvmbot
Copy link
Member

llvmbot commented Nov 24, 2025

@llvm/pr-subscribers-lld-elf

Author: Owen Anderson (resistor)

Changes

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: CHERIoT-Platform/llvm-project@3d6d6f7


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 --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

Copy link
Member

@lenary lenary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good, low-impact approach, and beyond the minor comments below, I am happy with how this works.

Comment on lines +31 to +41
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can reuse RISCV_nonstandard.def for defining these, but that's a nit we can revisit in the future.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it's possible somehow, but it seems like it might require some metaprogramming heavy-lifting.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be simplest to end up with identifiers such as INTERNAL_R_RISCV_QC_* etc, which would be doable with just token pasting, I think. Anyway, a reasonable follow-up if we want.

Copy link
Member

@lenary lenary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@resistor resistor merged commit 0c6d7a4 into llvm:main Nov 28, 2025
10 checks passed
aahrun pushed a commit to aahrun/llvm-project that referenced this pull request Dec 1, 2025
…ations. (llvm#169273)

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: CHERIoT-Platform/llvm-project@3d6d6f7
augusto2112 pushed a commit to augusto2112/llvm-project that referenced this pull request Dec 3, 2025
…ations. (llvm#169273)

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: CHERIoT-Platform/llvm-project@3d6d6f7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants