Re: ld.so/sparc64: fix UA64 relocation mask selection and byte order

Kirill A. Korinsky <[email protected]>
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>
On Thu, 09 Jul 2026 17:24:32 +0200,
Kirill A. Korinsky <[email protected]> wrote:
> 
> On Thu, 09 Jul 2026 17:01:34 +0200,
> Mark Kettenis <[email protected]> wrote:
> > 
> > > Date: Thu, 09 Jul 2026 15:30:52 +0200
> > > From: Kirill A. Korinsky <[email protected]>
> > > 
> > > On Thu, 09 Jul 2026 14:55:45 +0200,
> > > Mark Kettenis <[email protected]> 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.