[lld] [wip][LLD] Support RISCV vendor-specific relocations. (PR #168497)

Owen Anderson via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 18 18:54:00 PST 2025


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

>From 4e2e28e05a25d18ad26ae661326bd14e85001c9c Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Tue, 18 Nov 2025 01:19:17 -0600
Subject: [PATCH 1/2] [LLD] Support RISCV vendor-specific relocations.

---
 lld/ELF/Arch/RISCV.cpp                  |  37 ++++++--
 lld/ELF/Arch/RISCVInternalRelocations.h | 113 ++++++++++++++++++++++++
 lld/ELF/Relocations.cpp                 |   7 +-
 lld/ELF/SyntheticSections.cpp           |  54 +++++++++++
 lld/ELF/SyntheticSections.h             |   7 +-
 lld/ELF/Target.cpp                      |   7 ++
 6 files changed, 216 insertions(+), 9 deletions(-)
 create mode 100644 lld/ELF/Arch/RISCVInternalRelocations.h

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 5ed89e47c672e..5aa71db508446 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"
@@ -859,7 +860,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 +1504,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 = getRISCVVendorRelType(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 +1540,21 @@ template <class ELFT> void RISCV::scanSection1(InputSectionBase &sec) {
 void RISCV::scanSection(InputSectionBase &sec) {
   invokeELFT(scanSection1, sec);
 }
+
+namespace lld::elf {
+uint32_t getRISCVVendorRelType(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_QUALCOMM)
+    return "QUALCOMM";
+  if (ty.v & 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..da9e8b9feef0d
--- /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 = 1 << 10;
+
+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 getRISCVVendorRelType(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 = getRISCVVendorRelType(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/Relocations.cpp b/lld/ELF/Relocations.cpp
index d21376fd3ee47..75d50de95394f 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -1644,8 +1644,11 @@ void elf::postScanRelocations(Ctx &ctx) {
   }
 
   assert(ctx.symAux.size() == 1);
-  for (Symbol *sym : ctx.symtab->getSymbols())
-    fn(*sym);
+  // When RISCV vendor-specific relocations are used in the GOT, a new marker
+  // symbol may be introduced during this iteration, so we have to use an
+  // invalidation-safe loop.
+  for (size_t i = 0; i < ctx.symtab->getSymbols().size(); ++i)
+    fn(*ctx.symtab->getSymbols()[i]);
 
   // Local symbols may need the aforementioned non-preemptible ifunc and GOT
   // handling. They don't need regular PLT.
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 19b08152ae081..2e2f0d16c16e6 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -14,6 +14,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "SyntheticSections.h"
+#include "Arch/RISCVInternalRelocations.h"
 #include "Config.h"
 #include "DWARF.h"
 #include "EhFrame.h"
@@ -1703,6 +1704,18 @@ void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSection *symt) {
   isFinal = true; // Catch errors
 }
 
+size_t RelocationBaseSection::getSize() const {
+  size_t size = relocs.size() * entsize;
+  if (ctx.arg.emachine == EM_RISCV) {
+    for (const auto &reloc : relocs) {
+      if (reloc.type.v & INTERNAL_RISCV_VENDOR_MASK) {
+        size += entsize;
+      }
+    }
+  }
+  return size;
+}
+
 void RelocationBaseSection::computeRels() {
   SymbolTableBaseSection *symTab = getPartition(ctx).dynSymTab.get();
   parallelForEach(relocs, [&ctx = ctx, symTab](DynamicReloc &rel) {
@@ -1725,6 +1738,47 @@ void RelocationBaseSection::computeRels() {
       return std::tie(a.r_sym, a.r_offset) < std::tie(b.r_sym, b.r_offset);
     });
   }
+  // Insert R_RISCV_VENDOR relocations very late, so that it doesn't interfere
+  // with relocation sorting above.
+  if (ctx.arg.emachine == EM_RISCV) {
+    SmallVector<DynamicReloc, 0> processedRelocs;
+    processedRelocs.reserve(relocs.size());
+    for (auto reloc : relocs) {
+      auto vendorString = getRISCVVendorString(reloc.type);
+      if (vendorString) {
+        // Symbol *vendorSym = ctx.symtab->find(*vendorString);
+        auto *vendorSym = ctx.symtab->find(*vendorString);
+        if (!vendorSym || !vendorSym->isDefined()) {
+          vendorSym = ctx.symtab->addSymbol(Defined{ctx, nullptr, *vendorString,
+                                                    STB_GLOBAL, STV_HIDDEN,
+                                                    STT_NOTYPE, 0, 0, nullptr});
+          symTab->addSymbol(vendorSym);
+        }
+        vendorSym->isUsedInRegularObj = true;
+        vendorSym->isExported = true;
+        processedRelocs.push_back({llvm::ELF::R_RISCV_VENDOR, reloc.inputSec,
+                                   reloc.offsetInSec, true, *vendorSym, 0,
+                                   R_ABS});
+        processedRelocs.back().finalize(ctx, symTab);
+      }
+
+      reloc.type.v &= ~INTERNAL_RISCV_VENDOR_MASK;
+      processedRelocs.push_back(reloc);
+    }
+
+    relocs = std::move(processedRelocs);
+  }
+}
+
+void RelocationBaseSection::maybeAddRISCVendorRelocation(
+    const DynamicReloc &reloc, SmallVector<DynamicReloc, 0> &outRelocs) {
+  auto riscvVendorString = getRISCVVendorString(reloc.type);
+  if (ctx.arg.emachine == llvm::ELF::EM_RISCV && riscvVendorString) {
+    Symbol &vendorSym = *ctx.symtab->addSymbol(Defined{
+        ctx, ctx.internalFile, *riscvVendorString, llvm::ELF::STB_GLOBAL,
+        llvm::ELF::STV_HIDDEN, llvm::ELF::STT_NOTYPE, 0, 0, nullptr});
+    vendorSym.isUsedInRegularObj = true;
+  }
 }
 
 template <class ELFT>
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 66c866d7e8cde..d5365242b85ce 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -478,6 +478,7 @@ class RelocationBaseSection : public SyntheticSection {
   /// This overload can be used if the addends are written directly instead of
   /// using relocations on the input section (e.g. MipsGotSection::writeTo()).
   template <bool shard = false> void addReloc(const DynamicReloc &reloc) {
+    maybeAddRISCVendorRelocation(reloc, relocs);
     relocs.push_back(reloc);
   }
   /// Add a dynamic relocation against \p sym with an optional addend.
@@ -518,7 +519,7 @@ class RelocationBaseSection : public SyntheticSection {
     return !relocs.empty() ||
            llvm::any_of(relocsVec, [](auto &v) { return !v.empty(); });
   }
-  size_t getSize() const override { return relocs.size() * this->entsize; }
+  size_t getSize() const override;
   size_t getRelativeRelocCount() const { return numRelativeRelocs; }
   void mergeRels();
   void partitionRels();
@@ -529,6 +530,8 @@ class RelocationBaseSection : public SyntheticSection {
 
 protected:
   void computeRels();
+  void maybeAddRISCVendorRelocation(const DynamicReloc &reloc,
+                                    SmallVector<DynamicReloc, 0> &outRelocs);
   // Used when parallel relocation scanning adds relocations. The elements
   // will be moved into relocs by mergeRel().
   SmallVector<SmallVector<DynamicReloc, 0>, 0> relocsVec;
@@ -538,6 +541,8 @@ class RelocationBaseSection : public SyntheticSection {
 
 template <>
 inline void RelocationBaseSection::addReloc<true>(const DynamicReloc &reloc) {
+  maybeAddRISCVendorRelocation(reloc,
+                               relocsVec[llvm::parallel::getThreadIndex()]);
   relocsVec[llvm::parallel::getThreadIndex()].push_back(reloc);
 }
 
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 89e4dbeed3109..9f87cc7d5f49c 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,12 @@ 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 (" + Twine(type) + ")").str();
   return std::string(s);

>From ec1a0a5db39af1948a39b1e56fb94cca64a808b5 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Tue, 18 Nov 2025 20:53:12 -0600
Subject: [PATCH 2/2] Treat the vendor field of RelType as a single contiguous
 field rather than a mask.

---
 lld/ELF/Arch/RISCV.cpp                  | 4 ++--
 lld/ELF/Arch/RISCVInternalRelocations.h | 2 +-
 lld/ELF/Target.cpp                      | 2 ++
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 5aa71db508446..d21aac7a38303 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -1550,9 +1550,9 @@ uint32_t getRISCVVendorRelType(StringRef rvVendor) {
 }
 
 std::optional<StringRef> getRISCVVendorString(RelType ty) {
-  if (ty.v & INTERNAL_RISCV_VENDOR_QUALCOMM)
+  if ((ty.v & INTERNAL_RISCV_VENDOR_MASK) == INTERNAL_RISCV_VENDOR_QUALCOMM)
     return "QUALCOMM";
-  if (ty.v & INTERNAL_RISCV_VENDOR_ANDES)
+  if ((ty.v & INTERNAL_RISCV_VENDOR_MASK) == INTERNAL_RISCV_VENDOR_ANDES)
     return "ANDES";
   return std::nullopt;
 }
diff --git a/lld/ELF/Arch/RISCVInternalRelocations.h b/lld/ELF/Arch/RISCVInternalRelocations.h
index da9e8b9feef0d..0e9d80bba0a04 100644
--- a/lld/ELF/Arch/RISCVInternalRelocations.h
+++ b/lld/ELF/Arch/RISCVInternalRelocations.h
@@ -26,7 +26,7 @@ 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 = 1 << 10;
+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;
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 9f87cc7d5f49c..3fc3e3f16e9e0 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -46,6 +46,8 @@ std::string elf::toStr(Ctx &ctx, RelType 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();



More information about the llvm-commits mailing list