Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: ld.so/sparc64: fix UA64 relocation mask selection and byte order
To:
mark.kettenis@xs4all.nl, tech@openbsd.org
Date:
Thu, 09 Jul 2026 22:29:19 +0200

Download raw body.

Thread
On Thu, 09 Jul 2026 17:24:32 +0200,
Kirill A. Korinsky <kirill@korins.ky> wrote:
> 
> On Thu, 09 Jul 2026 17:01:34 +0200,
> Mark Kettenis <mark.kettenis@xs4all.nl> wrote:
> > 
> > > Date: Thu, 09 Jul 2026 15:30:52 +0200
> > > From: Kirill A. Korinsky <kirill@korins.ky>
> > > 
> > > On Thu, 09 Jul 2026 14:55:45 +0200,
> > > Mark Kettenis <mark.kettenis@xs4all.nl> wrote:
> > > > 
> > > > I think we do.  These "unaligned" relocations might not work in a
> > > > multi-threaded context as threads may observed a partially updated
> > > > state.
> > > 
> > > Make sense. This, actually, not that hard to change.
> > > 
> > > Something like that should do it.
> > 
> > And what about the UA32 and UA16 relocations?
> >
> 
> As far as I see the code of lld it do not allow UA32 and UA16 for dynamic reallocations.
> 

Anyway, here a diff which I will include into my large clang commit to
handle UA16, UA32 and UA64.

This version with tests, because I still hope to upstream it.

diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index 641bc1a67042..90442b774dfe 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -25,6 +25,8 @@ public:
   SPARCV9(Ctx &);
   RelExpr getRelExpr(RelType type, const Symbol &s,
                      const uint8_t *loc) const override;
+  RelType getDynRel(RelType type) const override;
+  void finalizeDynamicReloc(DynamicReloc &rel) const override;
   void writeGotHeader(uint8_t *buf) const override;
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
@@ -59,7 +61,9 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo(ctx) {
 RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
                             const uint8_t *loc) const {
   switch (type) {
+  case R_SPARC_16:
   case R_SPARC_32:
+  case R_SPARC_UA16:
   case R_SPARC_UA32:
   case R_SPARC_64:
   case R_SPARC_UA64:
@@ -75,6 +79,67 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
   }
 }
 
+RelType SPARCV9::getDynRel(RelType type) const {
+  switch (type) {
+  case R_SPARC_16:
+  case R_SPARC_32:
+  case R_SPARC_64:
+  case R_SPARC_UA16:
+  case R_SPARC_UA32:
+  case R_SPARC_UA64:
+    return type;
+  default:
+    break;
+  }
+  return R_SPARC_NONE;
+}
+
+static bool getSparcAbsRelocPair(RelType type, RelType &aligned,
+                                 RelType &unaligned, uint64_t &alignment) {
+  switch (type) {
+  case R_SPARC_16:
+  case R_SPARC_UA16:
+    aligned = R_SPARC_16;
+    unaligned = R_SPARC_UA16;
+    alignment = 2;
+    return true;
+  case R_SPARC_32:
+  case R_SPARC_UA32:
+    aligned = R_SPARC_32;
+    unaligned = R_SPARC_UA32;
+    alignment = 4;
+    return true;
+  case R_SPARC_64:
+  case R_SPARC_UA64:
+    aligned = R_SPARC_64;
+    unaligned = R_SPARC_UA64;
+    alignment = 8;
+    return true;
+  default:
+    return false;
+  }
+}
+
+void SPARCV9::finalizeDynamicReloc(DynamicReloc &rel) const {
+  RelType aligned = R_SPARC_NONE, unaligned = R_SPARC_NONE;
+  uint64_t alignment = 1;
+  if (!getSparcAbsRelocPair(rel.type, aligned, unaligned, alignment))
+    return;
+
+  rel.type = rel.r_offset % alignment == 0 ? aligned : unaligned;
+  if (!rel.needsDynSymIndex() || rel.sym->isPreemptible)
+    return;
+
+  if (rel.type == R_SPARC_64) {
+    rel.convertToRelative(relativeRel);
+    return;
+  }
+
+  Err(ctx) << "relocation " << rel.type << " at offset " << rel.r_offset
+           << " against non-preemptible symbol " << rel.sym
+           << " cannot be converted to " << relativeRel;
+}
+
 template <class ELFT, class RelTy>
 void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
   RelocScan rs(ctx, &sec);
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 0d45236e6d11..c76176db7842 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -991,10 +991,16 @@ void RelocScan::processAux(RelExpr expr, RelType type, uint64_t offset,
                     (isa<EhInputSection>(sec) && ctx.arg.emachine != EM_MIPS));
   if (canWrite) {
     RelType rel = ctx.target->getDynRel(type);
+    bool useRelative =
+        (rel == ctx.target->symbolicRel ||
+         (ctx.arg.emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64)) &&
+        !sym.isPreemptible;
+    if (ctx.arg.emachine == EM_SPARCV9 &&
+        oneof<R_SPARC_16, R_SPARC_32, R_SPARC_64, R_SPARC_UA16, R_SPARC_UA32,
+              R_SPARC_UA64>(type))
+      useRelative = false;
     if (oneof<R_GOT, RE_LOONGARCH_GOT>(expr) ||
-        ((rel == ctx.target->symbolicRel ||
-          (ctx.arg.emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64)) &&
-         !sym.isPreemptible)) {
+        useRelative) {
       addRelativeReloc<true>(ctx, *sec, offset, sym, addend, expr, type);
       return;
     }
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 65d3cbbe63a7..b5432825a162 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1527,6 +1527,7 @@ void RelocationBaseSection::finalizeContents() {
 
 void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSection *symt) {
   r_offset = getOffset();
+  ctx.target->finalizeDynamicReloc(*this);
   r_sym = getSymIndex(symt);
   addend = computeAddend(ctx);
   isFinal = true; // Catch errors
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 2b937558ea01..c95b54a347e9 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -440,6 +440,11 @@ public:
   uint64_t getOffset() const;
   uint32_t getSymIndex(SymbolTableBaseSection *symTab) const;
   bool needsDynSymIndex() const { return isAgainstSymbol; }
+  void convertToRelative(RelType relativeRel) {
+    type = relativeRel;
+    isAgainstSymbol = false;
+    expr = R_ABS;
+  }
 
   /// Computes the addend of the dynamic relocation. Note that this is not the
   /// same as the #addend member variable as it may also include the symbol
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 199f76e3fe85..31bd38b1f156 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -22,6 +22,7 @@
 namespace lld {
 namespace elf {
 class Defined;
+class DynamicReloc;
 class InputFile;
 class Symbol;
 template <class RelTy> struct Relocs;
@@ -37,6 +38,7 @@ public:
   virtual RelExpr getRelExpr(RelType type, const Symbol &s,
                              const uint8_t *loc) const = 0;
   virtual RelType getDynRel(RelType type) const { return 0; }
+  virtual void finalizeDynamicReloc(DynamicReloc &rel) const {}
   virtual void writeGotPltHeader(uint8_t *buf) const {}
   virtual void writeGotHeader(uint8_t *buf) const {}
   virtual void writeGotPlt(uint8_t *buf, const Symbol &s) const {}
diff --git a/lld/test/ELF/sparcv9-ua-dynrel.s b/lld/test/ELF/sparcv9-ua-dynrel.s
new file mode 100644
index 000000000000..4f58f9585231
--- /dev/null
+++ b/lld/test/ELF/sparcv9-ua-dynrel.s
@@ -0,0 +1,80 @@
+# REQUIRES: sparc
+# RUN: llvm-mc -filetype=obj -triple=sparcv9 %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so
+# RUN: llvm-readelf -r %t.so | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=sparcv9 --defsym ERR=1 %s -o %t.err.o
+# RUN: not ld.lld -shared %t.err.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR %s
+
+# CHECK:      R_SPARC_RELATIVE
+# CHECK:      R_SPARC_16 {{.*}} external
+# CHECK:      R_SPARC_32 {{.*}} external
+# CHECK:      R_SPARC_64 {{.*}} external
+# CHECK:      R_SPARC_UA16 {{.*}} external
+# CHECK:      R_SPARC_UA32 {{.*}} external
+# CHECK:      R_SPARC_UA64 {{.*}} external
+
+# ERR-DAG: error: relocation R_SPARC_16 at offset {{[0-9]+}} against non-preemptible symbol local cannot be converted to R_SPARC_RELATIVE
+# ERR-DAG: error: relocation R_SPARC_32 at offset {{[0-9]+}} against non-preemptible symbol local cannot be converted to R_SPARC_RELATIVE
+# ERR-DAG: error: relocation R_SPARC_UA64 at offset {{[0-9]+}} against non-preemptible symbol local cannot be converted to R_SPARC_RELATIVE
+
+.data
+.p2align 3
+aligned_local64:
+  .xword 0
+  .reloc aligned_local64, R_SPARC_UA64, local
+
+.p2align 1
+aligned_external16:
+  .half 0
+  .reloc aligned_external16, R_SPARC_UA16, external
+
+.p2align 2
+aligned_external32:
+  .word 0
+  .reloc aligned_external32, R_SPARC_UA32, external
+
+.p2align 3
+aligned_external64:
+  .xword 0
+  .reloc aligned_external64, R_SPARC_UA64, external
+
+.p2align 1
+  .byte 0
+unaligned_external16:
+  .half 0
+  .reloc unaligned_external16, R_SPARC_16, external
+
+.p2align 2
+  .byte 0
+unaligned_external32:
+  .word 0
+  .reloc unaligned_external32, R_SPARC_32, external
+
+.p2align 3
+  .byte 0
+unaligned_external64:
+  .xword 0
+  .reloc unaligned_external64, R_SPARC_64, external
+
+.ifdef ERR
+.p2align 1
+aligned_local16:
+  .half 0
+  .reloc aligned_local16, R_SPARC_UA16, local
+
+.p2align 2
+aligned_local32:
+  .word 0
+  .reloc aligned_local32, R_SPARC_UA32, local
+
+.p2align 3
+  .byte 0
+unaligned_local64:
+  .xword 0
+  .reloc unaligned_local64, R_SPARC_64, local
+.endif
+
+.hidden local
+local:
+  .xword 0


-- 
wbr, Kirill