Skip to content

[ELF] -r: Don't let a weaker offset-0 R_RISCV_ALIGN suppress ALIGN synthesis - #198147

Merged
MaskRay merged 1 commit into
llvm:mainfrom
MaskRay:pr/riscvalign
May 20, 2026
Merged

[ELF] -r: Don't let a weaker offset-0 R_RISCV_ALIGN suppress ALIGN synthesis#198147
MaskRay merged 1 commit into
llvm:mainfrom
MaskRay:pr/riscvalign

Conversation

@MaskRay

@MaskRay MaskRay commented May 17, 2026

Copy link
Copy Markdown
Member

PR #151639 skipped synthesizing the section-start R_RISCV_ALIGN whenever
any R_RISCV_ALIGN existed at offset 0, regardless of its alignment. This
works with newer LLVM integrated assembler (#150816).

However, older MC and GNU Assembler as of today
(https://sourceware.org/bugzilla/show_bug.cgi?id=33236) can carry a weak
offset-0 R_RISCV_ALIGN (addend 2 => align 4) while its real alignment
requirement comes from a .option norelax .balign, which emits no
relocation.

Fix the condition to not suppress synthesis.

Link: https://sourceware.org/bugzilla/show_bug.cgi?id=33236#c4

…nthesis

PR llvm#151639 skipped synthesizing the section-start R_RISCV_ALIGN whenever
any R_RISCV_ALIGN existed at offset 0, regardless of its alignment. This
works with newer LLVM integrated assembler (llvm#150816).

However, older MC and GNU Assembler as of today
(https://sourceware.org/bugzilla/show_bug.cgi?id=33236) can carry a weak
offset-0 R_RISCV_ALIGN (addend 2 => align 4) while its real alignment
requirement comes from a `.option norelax` .balign, which emits no
relocation.

Fix the condition to not suppress synthesis.

Link: https://sourceware.org/bugzilla/show_bug.cgi?id=33236#c4
@llvmorg-github-actions

llvmorg-github-actions Bot commented May 17, 2026

Copy link
Copy Markdown

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

@llvm/pr-subscribers-lld-elf

Author: Fangrui Song (MaskRay)

Changes

PR #151639 skipped synthesizing the section-start R_RISCV_ALIGN whenever
any R_RISCV_ALIGN existed at offset 0, regardless of its alignment. This
works with newer LLVM integrated assembler (#150816).

However, older MC and GNU Assembler as of today
(https://sourceware.org/bugzilla/show_bug.cgi?id=33236) can carry a weak
offset-0 R_RISCV_ALIGN (addend 2 => align 4) while its real alignment
requirement comes from a .option norelax .balign, which emits no
relocation.

Fix the condition to not suppress synthesis.

Link: https://sourceware.org/bugzilla/show_bug.cgi?id=33236#c4


Full diff: https://github.com/llvm/llvm-project/pull/198147.diff

4 Files Affected:

  • (modified) lld/ELF/Arch/LoongArch.cpp (+12-5)
  • (modified) lld/ELF/Arch/RISCV.cpp (+12-5)
  • (modified) lld/test/ELF/loongarch-relocatable-align.s (+25)
  • (modified) lld/test/ELF/riscv-relocatable-align.s (+24)
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 4c9ea3deece2d..7de21a56cf297 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -980,12 +980,19 @@ bool LoongArch::synthesizeAlignForInput(uint64_t &dot, InputSection *sec,
       }
     }
   } else if (sec->addralign > 4) {
-    // If the alignment is > 4 and the section does not start with an ALIGN
-    // relocation, synthesize one.
-    bool hasAlignRel = llvm::any_of(rels, [](const RelTy &rel) {
-      return rel.r_offset == 0 && rel.getType(false) == R_LARCH_ALIGN;
+    // If the alignment is > 4, synthesize an ALIGN unless an ALIGN relocation
+    // at offset 0 already guarantees `addralign`. Note: A weaker ALIGN at
+    // offset 0 from older assemblers do not suppress synthesis (e.g.
+    // `.p2align 2; .option norelax; nop; .p2align 3` does not suppress
+    // synthesized `.p2align 3`).
+    bool covered = llvm::any_of(rels, [&](const RelTy &rel) {
+      if (rel.r_offset != 0 || rel.getType(false) != R_LARCH_ALIGN)
+        return false;
+      if constexpr (RelTy::HasAddend)
+        return uint64_t(rel.r_addend) >= sec->addralign - 4;
+      return false;
     });
-    if (!hasAlignRel) {
+    if (!covered) {
       synthesizedAligns.emplace_back(dot - baseSec->getVA(),
                                      sec->addralign - 4);
       dot += sec->addralign - 4;
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 0ded3bb859a37..94bab7a8e348f 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -1133,12 +1133,19 @@ bool RISCV::synthesizeAlignForInput(uint64_t &dot, InputSection *sec,
       }
     }
   } else if (sec->addralign >= 4) {
-    // If the alignment is >= 4 and the section does not start with an ALIGN
-    // relocation, synthesize one.
-    bool hasAlignRel = llvm::any_of(rels, [](const RelTy &rel) {
-      return rel.r_offset == 0 && rel.getType(false) == R_RISCV_ALIGN;
+    // If the alignment is > 4, synthesize an ALIGN unless an ALIGN relocation
+    // at offset 0 already guarantees `addralign`. Note: A weaker ALIGN at
+    // offset 0 from older assemblers do not suppress synthesis (e.g.
+    // `.p2align 2; .option norelax; nop; .p2align 3` does not suppress
+    // synthesized `.p2align 3`).
+    bool covered = llvm::any_of(rels, [&](const RelTy &rel) {
+      if (rel.r_offset != 0 || rel.getType(false) != R_RISCV_ALIGN)
+        return false;
+      if constexpr (RelTy::HasAddend)
+        return uint64_t(rel.r_addend) >= sec->addralign - 2;
+      return false;
     });
-    if (!hasAlignRel) {
+    if (!covered) {
       synthesizedAligns.emplace_back(dot - baseSec->getVA(),
                                      sec->addralign - 2);
       dot += sec->addralign - 2;
diff --git a/lld/test/ELF/loongarch-relocatable-align.s b/lld/test/ELF/loongarch-relocatable-align.s
index c02007f9f719b..1147ee88d594d 100644
--- a/lld/test/ELF/loongarch-relocatable-align.s
+++ b/lld/test/ELF/loongarch-relocatable-align.s
@@ -92,6 +92,17 @@
 # CHECKC-NEXT: <b0>:
 # CHECKC-NEXT:   c:    addi.d $a0, $a1, 1
 
+## A weaker offset-0 ALIGN must not suppress synthesizing the stronger ALIGN required
+## by the subsequent .option norelax .balign 16 (which emits no relocation).
+## https://sourceware.org/bugzilla/show_bug.cgi?id=33236#c4
+# RUN: llvm-mc -filetype=obj -triple=loongarch64 -mattr=+relax e.s -o e.o
+# RUN: ld.lld -r a.o e.o -o ae.ro
+# RUN: llvm-readelf -r ae.ro | FileCheck %s --check-prefix=CHECK3-REL
+
+# CHECK3-REL:      Relocation section '.rela.text' {{.*}} contains 6 entries:
+# CHECK3-REL:      R_LARCH_ALIGN{{ +}}c
+# CHECK3-REL-NEXT: R_LARCH_ALIGN{{ +}}4
+
 #--- a.s
 .globl _start
 _start:
@@ -145,3 +156,17 @@ c0:
 .balign 8
 d0:
   addi.d $a0, $a1, 5
+
+#--- e.s
+## Mimick `.balign 8` that generates a NOP padding associated with
+## R_LARCH_ALIGN (addend 4 => align 8) when assembled by an assembler
+## without the redundant-ALIGN optimization.
+.reloc ., R_LARCH_ALIGN, 4
+nop
+
+.option push
+.option norelax
+.balign 16
+e0:
+  .word 0x3a393837
+.option pop
diff --git a/lld/test/ELF/riscv-relocatable-align.s b/lld/test/ELF/riscv-relocatable-align.s
index 9cd59e96a7196..24b5b108a4790 100644
--- a/lld/test/ELF/riscv-relocatable-align.s
+++ b/lld/test/ELF/riscv-relocatable-align.s
@@ -93,6 +93,17 @@
 # CHECK2-NEXT: <b0>:
 # CHECK2-NEXT:   e: 00158513             addi    a0, a1, 0x1
 
+## A weaker offset-0 ALIGN must not suppress synthesizing the stronger ALIGN required
+## by the subsequent .option norelax .balign 8 (which emits no relocation).
+## https://sourceware.org/bugzilla/show_bug.cgi?id=33236#c4
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax e.s -o ec.o
+# RUN: ld.lld -r ac.o ec.o -o ae.ro
+# RUN: llvm-readelf -r ae.ro | FileCheck %s --check-prefix=CHECK3-REL
+
+# CHECK3-REL:      Relocation section '.rela.text' {{.*}} contains 4 entries:
+# CHECK3-REL:      R_RISCV_ALIGN{{ +}}6
+# CHECK3-REL-NEXT: R_RISCV_ALIGN{{ +}}2
+
 #--- a.s
 .globl _start
 _start:
@@ -138,3 +149,16 @@ c0:
 .balign 4
 d0:
   addi a0, a1, 2
+
+#--- e.s
+## Mimick `.balign 4` that generates 2 NOP padding bytes associated with
+## R_RISCV_ALIGN (addend 2) when assembled by old MC.
+.reloc ., R_RISCV_ALIGN, 2
+c.nop
+
+.option push
+.option norelax
+.balign 8
+e0:
+  .word 0x3a393837
+.option pop

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-lld

Author: Fangrui Song (MaskRay)

Changes

PR #151639 skipped synthesizing the section-start R_RISCV_ALIGN whenever
any R_RISCV_ALIGN existed at offset 0, regardless of its alignment. This
works with newer LLVM integrated assembler (#150816).

However, older MC and GNU Assembler as of today
(https://sourceware.org/bugzilla/show_bug.cgi?id=33236) can carry a weak
offset-0 R_RISCV_ALIGN (addend 2 => align 4) while its real alignment
requirement comes from a .option norelax .balign, which emits no
relocation.

Fix the condition to not suppress synthesis.

Link: https://sourceware.org/bugzilla/show_bug.cgi?id=33236#c4


Full diff: https://github.com/llvm/llvm-project/pull/198147.diff

4 Files Affected:

  • (modified) lld/ELF/Arch/LoongArch.cpp (+12-5)
  • (modified) lld/ELF/Arch/RISCV.cpp (+12-5)
  • (modified) lld/test/ELF/loongarch-relocatable-align.s (+25)
  • (modified) lld/test/ELF/riscv-relocatable-align.s (+24)
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 4c9ea3deece2d..7de21a56cf297 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -980,12 +980,19 @@ bool LoongArch::synthesizeAlignForInput(uint64_t &dot, InputSection *sec,
       }
     }
   } else if (sec->addralign > 4) {
-    // If the alignment is > 4 and the section does not start with an ALIGN
-    // relocation, synthesize one.
-    bool hasAlignRel = llvm::any_of(rels, [](const RelTy &rel) {
-      return rel.r_offset == 0 && rel.getType(false) == R_LARCH_ALIGN;
+    // If the alignment is > 4, synthesize an ALIGN unless an ALIGN relocation
+    // at offset 0 already guarantees `addralign`. Note: A weaker ALIGN at
+    // offset 0 from older assemblers do not suppress synthesis (e.g.
+    // `.p2align 2; .option norelax; nop; .p2align 3` does not suppress
+    // synthesized `.p2align 3`).
+    bool covered = llvm::any_of(rels, [&](const RelTy &rel) {
+      if (rel.r_offset != 0 || rel.getType(false) != R_LARCH_ALIGN)
+        return false;
+      if constexpr (RelTy::HasAddend)
+        return uint64_t(rel.r_addend) >= sec->addralign - 4;
+      return false;
     });
-    if (!hasAlignRel) {
+    if (!covered) {
       synthesizedAligns.emplace_back(dot - baseSec->getVA(),
                                      sec->addralign - 4);
       dot += sec->addralign - 4;
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 0ded3bb859a37..94bab7a8e348f 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -1133,12 +1133,19 @@ bool RISCV::synthesizeAlignForInput(uint64_t &dot, InputSection *sec,
       }
     }
   } else if (sec->addralign >= 4) {
-    // If the alignment is >= 4 and the section does not start with an ALIGN
-    // relocation, synthesize one.
-    bool hasAlignRel = llvm::any_of(rels, [](const RelTy &rel) {
-      return rel.r_offset == 0 && rel.getType(false) == R_RISCV_ALIGN;
+    // If the alignment is > 4, synthesize an ALIGN unless an ALIGN relocation
+    // at offset 0 already guarantees `addralign`. Note: A weaker ALIGN at
+    // offset 0 from older assemblers do not suppress synthesis (e.g.
+    // `.p2align 2; .option norelax; nop; .p2align 3` does not suppress
+    // synthesized `.p2align 3`).
+    bool covered = llvm::any_of(rels, [&](const RelTy &rel) {
+      if (rel.r_offset != 0 || rel.getType(false) != R_RISCV_ALIGN)
+        return false;
+      if constexpr (RelTy::HasAddend)
+        return uint64_t(rel.r_addend) >= sec->addralign - 2;
+      return false;
     });
-    if (!hasAlignRel) {
+    if (!covered) {
       synthesizedAligns.emplace_back(dot - baseSec->getVA(),
                                      sec->addralign - 2);
       dot += sec->addralign - 2;
diff --git a/lld/test/ELF/loongarch-relocatable-align.s b/lld/test/ELF/loongarch-relocatable-align.s
index c02007f9f719b..1147ee88d594d 100644
--- a/lld/test/ELF/loongarch-relocatable-align.s
+++ b/lld/test/ELF/loongarch-relocatable-align.s
@@ -92,6 +92,17 @@
 # CHECKC-NEXT: <b0>:
 # CHECKC-NEXT:   c:    addi.d $a0, $a1, 1
 
+## A weaker offset-0 ALIGN must not suppress synthesizing the stronger ALIGN required
+## by the subsequent .option norelax .balign 16 (which emits no relocation).
+## https://sourceware.org/bugzilla/show_bug.cgi?id=33236#c4
+# RUN: llvm-mc -filetype=obj -triple=loongarch64 -mattr=+relax e.s -o e.o
+# RUN: ld.lld -r a.o e.o -o ae.ro
+# RUN: llvm-readelf -r ae.ro | FileCheck %s --check-prefix=CHECK3-REL
+
+# CHECK3-REL:      Relocation section '.rela.text' {{.*}} contains 6 entries:
+# CHECK3-REL:      R_LARCH_ALIGN{{ +}}c
+# CHECK3-REL-NEXT: R_LARCH_ALIGN{{ +}}4
+
 #--- a.s
 .globl _start
 _start:
@@ -145,3 +156,17 @@ c0:
 .balign 8
 d0:
   addi.d $a0, $a1, 5
+
+#--- e.s
+## Mimick `.balign 8` that generates a NOP padding associated with
+## R_LARCH_ALIGN (addend 4 => align 8) when assembled by an assembler
+## without the redundant-ALIGN optimization.
+.reloc ., R_LARCH_ALIGN, 4
+nop
+
+.option push
+.option norelax
+.balign 16
+e0:
+  .word 0x3a393837
+.option pop
diff --git a/lld/test/ELF/riscv-relocatable-align.s b/lld/test/ELF/riscv-relocatable-align.s
index 9cd59e96a7196..24b5b108a4790 100644
--- a/lld/test/ELF/riscv-relocatable-align.s
+++ b/lld/test/ELF/riscv-relocatable-align.s
@@ -93,6 +93,17 @@
 # CHECK2-NEXT: <b0>:
 # CHECK2-NEXT:   e: 00158513             addi    a0, a1, 0x1
 
+## A weaker offset-0 ALIGN must not suppress synthesizing the stronger ALIGN required
+## by the subsequent .option norelax .balign 8 (which emits no relocation).
+## https://sourceware.org/bugzilla/show_bug.cgi?id=33236#c4
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax e.s -o ec.o
+# RUN: ld.lld -r ac.o ec.o -o ae.ro
+# RUN: llvm-readelf -r ae.ro | FileCheck %s --check-prefix=CHECK3-REL
+
+# CHECK3-REL:      Relocation section '.rela.text' {{.*}} contains 4 entries:
+# CHECK3-REL:      R_RISCV_ALIGN{{ +}}6
+# CHECK3-REL-NEXT: R_RISCV_ALIGN{{ +}}2
+
 #--- a.s
 .globl _start
 _start:
@@ -138,3 +149,16 @@ c0:
 .balign 4
 d0:
   addi a0, a1, 2
+
+#--- e.s
+## Mimick `.balign 4` that generates 2 NOP padding bytes associated with
+## R_RISCV_ALIGN (addend 2) when assembled by old MC.
+.reloc ., R_RISCV_ALIGN, 2
+c.nop
+
+.option push
+.option norelax
+.balign 8
+e0:
+  .word 0x3a393837
+.option pop

@MaskRay

MaskRay commented May 17, 2026

Copy link
Copy Markdown
Member Author

@MQ-mengqing

@MaskRay
MaskRay requested a review from ylzsx May 17, 2026 05:36

@wangleiat wangleiat left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM from the LoongArch side, thanks.

@MaskRay
MaskRay merged commit a88714b into llvm:main May 20, 2026
14 checks passed
@MaskRay
MaskRay deleted the pr/riscvalign branch May 20, 2026 04:19
saagarjha pushed a commit to ahjragaas/binutils-gdb that referenced this pull request Jul 6, 2026
LoongArch has the same issue as RISC-V for PR 33236 [1].

Section alignment can't be adjusted for objects generated by ld -r.
If previous sections are relaxed, the subsequent section maybe misaligned.

To fix this, add an align section and an align relocation before each
section when ld -r. And change the section alignment to 4 to disable
the default section start address calculation.

ld.lld has fixed this issue in the following two patches [2] [3].

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=33236
[2] llvm/llvm-project#151639
[3] llvm/llvm-project#198147
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.

2 participants