[lld] ELF: Synthesize R_RISCV_ALIGN at input section start (PR #151463)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 31 00:08:21 PDT 2025


https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/151463

None

>From f1e85e2ed9553e6c52ecbc078305982b8c9a9ace Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 29 Jul 2025 21:20:43 -0700
Subject: [PATCH] ELF: Synthesize R_RISCV_ALIGN at input section start

---
 lld/ELF/Arch/RISCV.cpp                 | 100 ++++++++++++++++++++++
 lld/ELF/LinkerScript.cpp               |  12 ++-
 lld/ELF/OutputSections.cpp             |  11 ++-
 lld/ELF/Target.h                       |   3 +
 lld/ELF/Writer.cpp                     |   2 +
 lld/test/ELF/riscv-relocatable-align.s | 111 +++++++++++++++++++++++++
 6 files changed, 235 insertions(+), 4 deletions(-)
 create mode 100644 lld/test/ELF/riscv-relocatable-align.s

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 72d83159ad8ac..69853a616a5a2 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -45,7 +45,15 @@ class RISCV final : public TargetInfo {
                 uint64_t val) const override;
   void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
   bool relaxOnce(int pass) const override;
+  template <class ELFT, class RelTy>
+  bool synthesizeAlign(uint64_t &dot, InputSection *sec, Relocs<RelTy> rels);
+  template <class ELFT, class RelTy>
+  bool synthesizeAlignEnd(uint64_t &dot, InputSection *sec, Relocs<RelTy> rels);
+  bool maybeSynthesizeAlign(uint64_t &dot, InputSection *sec) override;
   void finalizeRelax(int passes) const override;
+
+  InputSection *baseSec = nullptr;
+  SmallVector<std::pair<uint64_t, uint64_t>, 0> synthesizedAligns;
 };
 
 } // end anonymous namespace
@@ -959,10 +967,102 @@ bool RISCV::relaxOnce(int pass) const {
   return changed;
 }
 
+template <class ELFT, class RelTy>
+bool RISCV::synthesizeAlign(uint64_t &dot, InputSection *sec,
+                            Relocs<RelTy> rels) {
+  if (!baseSec) {
+    // Record the first section with RELAX relocations.
+    for (auto rel : rels) {
+      if (rel.getType(false) == R_RISCV_RELAX) {
+        baseSec = sec;
+        break;
+      }
+    }
+  } else if (sec->addralign >= 4) {
+    // If the alignment is >= 4 and the section does not start with an ALIGN
+    // relocation, synthesize one.
+    bool alignRel = false;
+    for (auto rel : rels) {
+      if (rel.r_offset == 0 && rel.getType(false) == R_RISCV_ALIGN)
+        alignRel = true;
+      break;
+    }
+    if (!alignRel) {
+      synthesizedAligns.emplace_back(dot - baseSec->getVA(),
+                                     sec->addralign - 2);
+      dot += sec->addralign - 2;
+      return true;
+    }
+  }
+  return false;
+}
+
+template <class ELFT, class RelTy>
+bool RISCV::synthesizeAlignEnd(uint64_t &dot, InputSection *sec,
+                               Relocs<RelTy> rels) {
+  auto *f = cast<ObjFile<ELFT>>(baseSec->file);
+  auto shdr = f->template getELFShdrs<ELFT>()[baseSec->relSecIdx];
+  sec = make<InputSection>(*f, shdr, baseSec->name);
+  auto *baseRelSec = cast<InputSection>(f->getSections()[baseSec->relSecIdx]);
+  baseSec = nullptr;
+  *sec = *baseRelSec;
+
+  auto newSize = rels.size() + synthesizedAligns.size();
+  auto *relas = makeThreadLocalN<typename ELFT::Rela>(newSize);
+  sec->size = newSize * sizeof(typename ELFT::Rela);
+  sec->content_ = reinterpret_cast<uint8_t *>(relas);
+  // Copy original relocations.
+  for (auto [i, r] : llvm::enumerate(rels)) {
+    relas[i].r_offset = r.r_offset;
+    relas[i].setRInfo(r.getRInfo(false), false);
+    relas[i].r_addend = r.r_addend;
+  }
+  // Append synthesized ALIGN relocations.
+  for (auto [i, r] : llvm::enumerate(synthesizedAligns)) {
+    auto &rela = relas[rels.size() + i];
+    rela.r_offset = r.first;
+    rela.setSymbolAndType(0, R_RISCV_ALIGN, false);
+    rela.r_addend = r.second;
+  }
+  // In the output relocation section, replace the old relocation section with
+  // the new one.
+  for (SectionCommand *cmd : sec->getParent()->commands) {
+    auto *isd = dyn_cast<InputSectionDescription>(cmd);
+    if (!isd)
+      continue;
+    for (auto *&isec : isd->sections)
+      if (isec == baseRelSec)
+        isec = sec;
+  }
+  return false;
+}
+
+bool RISCV::maybeSynthesizeAlign(uint64_t &dot, InputSection *sec) {
+  if (sec) {
+    if (ctx.arg.is64) {
+      const RelsOrRelas<ELF64LE> rs = sec->template relsOrRelas<ELF64LE>();
+      return synthesizeAlign<ELF64LE>(dot, sec, rs.relas);
+    } else {
+      const RelsOrRelas<ELF32LE> rs = sec->template relsOrRelas<ELF32LE>();
+      return synthesizeAlign<ELF32LE>(dot, sec, rs.relas);
+    }
+  }
+  if (!baseSec)
+    return false;
+  if (ctx.arg.is64) {
+    const RelsOrRelas<ELF64LE> rs = baseSec->template relsOrRelas<ELF64LE>();
+    return synthesizeAlignEnd<ELF64LE>(dot, sec, rs.relas);
+  } else {
+    const RelsOrRelas<ELF32LE> rs = baseSec->template relsOrRelas<ELF32LE>();
+    return synthesizeAlignEnd<ELF32LE>(dot, sec, rs.relas);
+  }
+}
+
 void RISCV::finalizeRelax(int passes) const {
   llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation");
   Log(ctx) << "relaxation passes: " << passes;
   SmallVector<InputSection *, 0> storage;
+
   for (OutputSection *osec : ctx.outputSections) {
     if (!(osec->flags & SHF_EXECINSTR))
       continue;
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index a5d08f4979dab..95830aacf45ce 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -1230,6 +1230,9 @@ bool LinkerScript::assignOffsets(OutputSection *sec) {
   if (sec->firstInOverlay)
     state->overlaySize = 0;
 
+  bool synthesizeAlign =
+      (sec->flags & SHF_EXECINSTR) && ctx.arg.relocatable && ctx.arg.relax &&
+      is_contained({EM_RISCV, EM_LOONGARCH}, ctx.arg.emachine);
   // We visited SectionsCommands from processSectionCommands to
   // layout sections. Now, we visit SectionsCommands again to fix
   // section offsets.
@@ -1260,7 +1263,8 @@ bool LinkerScript::assignOffsets(OutputSection *sec) {
       if (isa<PotentialSpillSection>(isec))
         continue;
       const uint64_t pos = dot;
-      dot = alignToPowerOf2(dot, isec->addralign);
+      if (!(synthesizeAlign && ctx.target->maybeSynthesizeAlign(dot, isec)))
+        dot = alignToPowerOf2(dot, isec->addralign);
       isec->outSecOff = dot - sec->addr;
       dot += isec->getSize();
 
@@ -1276,6 +1280,12 @@ bool LinkerScript::assignOffsets(OutputSection *sec) {
   if (ctx.in.relroPadding && sec == ctx.in.relroPadding->getParent())
     expandOutputSection(alignToPowerOf2(dot, ctx.arg.commonPageSize) - dot);
 
+  if (synthesizeAlign) {
+    const uint64_t pos = dot;
+    ctx.target->maybeSynthesizeAlign(dot, nullptr);
+    expandOutputSection(dot - pos);
+  }
+
   // Non-SHF_ALLOC sections do not affect the addresses of other OutputSections
   // as they are not part of the process image.
   if (!(sec->flags & SHF_ALLOC)) {
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index 1020dd9f2569e..1ce4f2fd3b3f6 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -889,9 +889,14 @@ void OutputSection::sortInitFini() {
 std::array<uint8_t, 4> OutputSection::getFiller(Ctx &ctx) {
   if (filler)
     return *filler;
-  if (flags & SHF_EXECINSTR)
-    return ctx.target->trapInstr;
-  return {0, 0, 0, 0};
+  if (!(flags & SHF_EXECINSTR))
+    return {0, 0, 0, 0};
+  if (ctx.arg.relocatable && ctx.arg.emachine == EM_RISCV) {
+    if (ctx.arg.eflags & EF_RISCV_RVC)
+      return {1, 0, 1, 0};
+    return {0x13, 0, 0, 0};
+  }
+  return ctx.target->trapInstr;
 }
 
 void OutputSection::checkDynRelAddends(Ctx &ctx) {
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index f4a6d83dafad2..7d6d3cee338e5 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -96,6 +96,9 @@ class TargetInfo {
 
   // Do a linker relaxation pass and return true if we changed something.
   virtual bool relaxOnce(int pass) const { return false; }
+  virtual bool maybeSynthesizeAlign(uint64_t &dot, InputSection *sec) {
+    return false;
+  }
   // Do finalize relaxation after collecting relaxation infos.
   virtual void finalizeRelax(int passes) const {}
 
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index c3c057812a685..5990a2f6e5bb8 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1543,6 +1543,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
 
   uint32_t pass = 0, assignPasses = 0;
   for (;;) {
+    if (ctx.arg.relocatable)
+      break;
     bool changed = ctx.target->needsThunks
                        ? tc.createThunks(pass, ctx.outputSections)
                        : ctx.target->relaxOnce(pass);
diff --git a/lld/test/ELF/riscv-relocatable-align.s b/lld/test/ELF/riscv-relocatable-align.s
new file mode 100644
index 0000000000000..006dd45138b37
--- /dev/null
+++ b/lld/test/ELF/riscv-relocatable-align.s
@@ -0,0 +1,111 @@
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax a.s -o ac.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax b.s -o bc.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax b1.s -o b1c.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax c.s -o cc.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax d.s -o dc.o
+# RUN: ld.lld -r bc.o bc.o ac.o bc.o b1c.o cc.o dc.o -o out.ro
+# RUN: llvm-objdump -dr -M no-aliases out.ro | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax a.s -o a.o
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax b.s -o b.o
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax d.s -o d.o
+# RUN: ld.lld -r a.o b.o d.o -o out0.ro
+# RUN: ld.lld -Ttext=0x10000 out0.ro -o out0
+# RUN: llvm-objdump -dr -M no-aliases out0 | FileCheck %s --check-prefix=CHECK1
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax a.s -o acrel.o
+# RUN: ld.lld -r bc.o bc.o ac.o bc.o b1c.o cc.o dc.o -o out.crel.ro
+
+# CHECK:      <b0>:
+# CHECK-NEXT:   0: 00158513             addi    a0, a1, 0x1
+# CHECK-NEXT:   4: 0001                 c.nop
+# CHECK-NEXT:   6: 0001                 c.nop
+# CHECK-EMPTY:
+# CHECK-NEXT: <b0>:
+# CHECK-NEXT:   8: 00158513             addi    a0, a1, 0x1
+# CHECK-EMPTY:
+# CHECK-NEXT: <_start>:
+# CHECK-NEXT:   c: 00000097             auipc   ra, 0x0
+# CHECK-NEXT:           000000000000000c:  R_RISCV_CALL_PLT     foo
+# CHECK-NEXT:           000000000000000c:  R_RISCV_RELAX        *ABS*
+# CHECK-NEXT:  10: 000080e7             jalr    ra, 0x0(ra) <_start>
+# CHECK-NEXT:  14: 0001                 c.nop
+# CHECK-NEXT:           0000000000000014:  R_RISCV_ALIGN        *ABS*+0x6
+# CHECK-NEXT:  16: 0001                 c.nop
+# CHECK-NEXT:  18: 0001                 c.nop
+# CHECK-EMPTY:
+# CHECK-NEXT: <b0>:
+# CHECK-NEXT:  1a: 00158513             addi    a0, a1, 0x1
+# CHECK-NEXT:  1e: 0001                 c.nop
+# CHECK-NEXT:  20: 0001                 c.nop
+# CHECK-NEXT:           0000000000000020:  R_RISCV_ALIGN        *ABS*+0x6
+# CHECK-NEXT:  22: 0001                 c.nop
+# CHECK-NEXT:  24: 00000013             addi    zero, zero, 0x0
+# CHECK-EMPTY:
+# CHECK-NEXT: <b0>:
+# CHECK-NEXT:  28: 00158513             addi    a0, a1, 0x1
+# CHECK-EMPTY:
+# CHECK-NEXT: <c0>:
+# CHECK-NEXT:  2c: 00000097             auipc   ra, 0x0
+# CHECK-NEXT:           000000000000002c:  R_RISCV_CALL_PLT     foo
+# CHECK-NEXT:           000000000000002c:  R_RISCV_RELAX        *ABS*
+# CHECK-NEXT:  30: 000080e7             jalr    ra, 0x0(ra) <c0>
+# CHECK-NEXT:  34: 0001                 c.nop
+# CHECK-NEXT:           0000000000000034:  R_RISCV_ALIGN        *ABS*+0x2
+# CHECK-EMPTY:
+# CHECK-NEXT: <d0>:
+# CHECK-NEXT:  36: 00258513             addi    a0, a1, 0x2
+
+# CHECK1:      <_start>:
+# CHECK1-NEXT:    010000ef      jal     ra, 0x10010 <foo>
+# CHECK1-NEXT:    00000013      addi zero, zero, 0x0
+# CHECK1-EMPTY: 
+# CHECK1-NEXT: <b0>:
+# CHECK1-NEXT:    00158513      addi    a0, a1, 0x1
+# CHECK1-EMPTY: 
+# CHECK1-NEXT: <d0>:
+# CHECK1-NEXT:    00258513      addi    a0, a1, 0x2
+
+#--- a.s
+.globl _start
+_start:
+  call foo
+
+.section .text1,"ax"
+.globl foo
+foo:
+
+#--- b.s
+## Needs synthesized ALIGN
+.option push
+.option norelax
+.balign 8
+b0:
+  addi a0, a1, 1
+.option pop
+
+#--- b1.s
+.option push
+.option norelax
+  .reloc ., R_RISCV_ALIGN, 6
+  addi x0, x0, 0
+  c.nop
+.balign 8
+b0:
+  addi a0, a1, 1
+.option pop
+
+#--- c.s
+.balign 2
+c0:
+  call foo
+
+#--- d.s
+## Needs synthesized ALIGN
+.option push
+.option norelax
+.balign 4
+d0:
+  addi a0, a1, 2
+.option pop



More information about the llvm-commits mailing list