[llvm] [MC] Make .note.GNU-stack explicit for the trampoline case (PR #151754)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 3 15:00:48 PDT 2025


https://github.com/ssijaric-nv updated https://github.com/llvm/llvm-project/pull/151754

>From ae2a91da86d32f16625dc9ecb52ec496b517a851 Mon Sep 17 00:00:00 2001
From: Sanjin Sijaric <ssijaric at nvidia.com>
Date: Mon, 30 Jun 2025 12:02:49 -0700
Subject: [PATCH 1/4] [MC] Make .note.GNU-stack explicit for the trampoline
 case

In the presence of trampolines, the .note.GNU-stack section is not emitted.  The
absence of .note.GNU-stack results in the stack marked executable by some
linkers. But others require an explict .note.GNU-stack section.

The GNU ld 2.43 on x86 machines, for example, issues the following:

missing .note.GNU-stack section implies executable stack
NOTE: This behaviour is deprecated and will be removed in a future version of the linker

On one of the ARM machines, the absence of .note.GNU-stack results in the stack
marked as non-executable:

STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
      filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-

This change just emits the explicit .note.GNU-stack and marks it executable.
---
 llvm/include/llvm/MC/MCAsmInfo.h           | 6 ++++++
 llvm/include/llvm/MC/MCAsmInfoELF.h        | 1 +
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 8 ++++++--
 llvm/lib/MC/MCAsmInfoELF.cpp               | 7 +++++++
 llvm/test/CodeGen/AArch64/trampoline.ll    | 1 +
 llvm/test/CodeGen/RISCV/rv64-trampoline.ll | 1 +
 6 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h
index 6c12cd347901a..1e87ce23b158b 100644
--- a/llvm/include/llvm/MC/MCAsmInfo.h
+++ b/llvm/include/llvm/MC/MCAsmInfo.h
@@ -471,6 +471,12 @@ class LLVM_ABI MCAsmInfo {
     return nullptr;
   }
 
+  /// Targets can implement this method to specify a section to switch to if the
+  /// translation has trampolines that require an executable stack.
+  virtual MCSection *getExecutableStackSection(MCContext &Ctx) const {
+    return nullptr;
+  }
+
   virtual const MCExpr *getExprForPersonalitySymbol(const MCSymbol *Sym,
                                                     unsigned Encoding,
                                                     MCStreamer &Streamer) const;
diff --git a/llvm/include/llvm/MC/MCAsmInfoELF.h b/llvm/include/llvm/MC/MCAsmInfoELF.h
index c05e4ad78ecd1..c5dff900227fd 100644
--- a/llvm/include/llvm/MC/MCAsmInfoELF.h
+++ b/llvm/include/llvm/MC/MCAsmInfoELF.h
@@ -16,6 +16,7 @@ namespace llvm {
 class MCAsmInfoELF : public MCAsmInfo {
   virtual void anchor();
   MCSection *getNonexecutableStackSection(MCContext &Ctx) const final;
+  MCSection *getExecutableStackSection(MCContext &Ctx) const final;
   void printSwitchToSection(const MCSection &, uint32_t, const Triple &,
                             raw_ostream &) const final;
   bool useCodeAlign(const MCSection &Sec) const final;
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index cd14a4f57f760..39a32cc781b66 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -2841,9 +2841,13 @@ bool AsmPrinter::doFinalization(Module &M) {
   // If we don't have any trampolines, then we don't require stack memory
   // to be executable. Some targets have a directive to declare this.
   Function *InitTrampolineIntrinsic = M.getFunction("llvm.init.trampoline");
+  MCSection *S = nullptr;
   if (!InitTrampolineIntrinsic || InitTrampolineIntrinsic->use_empty())
-    if (MCSection *S = MAI->getNonexecutableStackSection(OutContext))
-      OutStreamer->switchSection(S);
+    S = MAI->getNonexecutableStackSection(OutContext);
+  else
+    S = MAI->getExecutableStackSection(OutContext);
+  if (S)
+    OutStreamer->switchSection(S);
 
   if (TM.Options.EmitAddrsig) {
     // Emit address-significance attributes for all globals.
diff --git a/llvm/lib/MC/MCAsmInfoELF.cpp b/llvm/lib/MC/MCAsmInfoELF.cpp
index cdae9d7860f33..3d0271699d8de 100644
--- a/llvm/lib/MC/MCAsmInfoELF.cpp
+++ b/llvm/lib/MC/MCAsmInfoELF.cpp
@@ -35,6 +35,13 @@ MCSection *MCAsmInfoELF::getNonexecutableStackSection(MCContext &Ctx) const {
   return Ctx.getELFSection(".note.GNU-stack", ELF::SHT_PROGBITS, 0);
 }
 
+MCSection *MCAsmInfoELF::getExecutableStackSection(MCContext &Ctx) const {
+  MCSectionELF *section =
+      static_cast<MCSectionELF *>(getNonexecutableStackSection(Ctx));
+  section->setFlags(section->getFlags() | ELF::SHF_EXECINSTR);
+  return section;
+}
+
 bool MCAsmInfoELF::useCodeAlign(const MCSection &Sec) const {
   return static_cast<const MCSectionELF &>(Sec).getFlags() & ELF::SHF_EXECINSTR;
 }
diff --git a/llvm/test/CodeGen/AArch64/trampoline.ll b/llvm/test/CodeGen/AArch64/trampoline.ll
index 0e682704afbf8..6689425bf5832 100644
--- a/llvm/test/CodeGen/AArch64/trampoline.ll
+++ b/llvm/test/CodeGen/AArch64/trampoline.ll
@@ -263,3 +263,4 @@ define i64 @func2() {
   %fp = call ptr @llvm.adjust.trampoline(ptr @trampg)
   ret i64 0
 }
+; CHECK-LINUX:         .section        ".note.GNU-stack","x", at progbits
diff --git a/llvm/test/CodeGen/RISCV/rv64-trampoline.ll b/llvm/test/CodeGen/RISCV/rv64-trampoline.ll
index 34d46579518ea..69c905002032d 100644
--- a/llvm/test/CodeGen/RISCV/rv64-trampoline.ll
+++ b/llvm/test/CodeGen/RISCV/rv64-trampoline.ll
@@ -71,6 +71,7 @@ define i64 @test0(i64 %n, ptr %p) nounwind {
 ; RV64-LINUX-NEXT:    ld ra, 56(sp) # 8-byte Folded Reload
 ; RV64-LINUX-NEXT:    addi sp, sp, 64
 ; RV64-LINUX-NEXT:    ret
+; RV64-LINUX:         .section        ".note.GNU-stack","x", at progbits
   %alloca = alloca [32 x i8], align 8
   call void @llvm.init.trampoline(ptr %alloca, ptr @f, ptr %p)
   %tramp = call ptr @llvm.adjust.trampoline(ptr %alloca)

>From ad7063af22d1243ef3dc6682ee3f6f2ec7933119 Mon Sep 17 00:00:00 2001
From: ssijaric-nv <ssijaric at nvidia.com>
Date: Mon, 11 Aug 2025 15:11:27 -0700
Subject: [PATCH 2/4] Check for a potential null section

---
 llvm/lib/MC/MCAsmInfoELF.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/MC/MCAsmInfoELF.cpp b/llvm/lib/MC/MCAsmInfoELF.cpp
index 3d0271699d8de..495343a2c89d2 100644
--- a/llvm/lib/MC/MCAsmInfoELF.cpp
+++ b/llvm/lib/MC/MCAsmInfoELF.cpp
@@ -38,7 +38,8 @@ MCSection *MCAsmInfoELF::getNonexecutableStackSection(MCContext &Ctx) const {
 MCSection *MCAsmInfoELF::getExecutableStackSection(MCContext &Ctx) const {
   MCSectionELF *section =
       static_cast<MCSectionELF *>(getNonexecutableStackSection(Ctx));
-  section->setFlags(section->getFlags() | ELF::SHF_EXECINSTR);
+  if (section)
+    section->setFlags(section->getFlags() | ELF::SHF_EXECINSTR);
   return section;
 }
 

>From c23db27af9fb8c01f1439fd2d8a85fead8229dcc Mon Sep 17 00:00:00 2001
From: Sanjin Sijaric <ssijaric at nvidia.com>
Date: Thu, 21 Aug 2025 13:06:28 -0700
Subject: [PATCH 3/4] Fix the test cases; use getStackSection

---
 llvm/include/llvm/MC/MCAsmInfo.h           | 14 ++++----------
 llvm/include/llvm/MC/MCAsmInfoELF.h        |  3 +--
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp |  8 +++-----
 llvm/lib/MC/MCAsmInfoELF.cpp               | 13 +++----------
 llvm/lib/MC/MCELFStreamer.cpp              |  2 +-
 llvm/test/CodeGen/AArch64/trampoline.ll    |  5 +++++
 llvm/test/CodeGen/RISCV/rv64-trampoline.ll |  8 +++++++-
 llvm/tools/llvm-mc/llvm-mc.cpp             |  3 ++-
 8 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h
index 1e87ce23b158b..7a2e9ad154f01 100644
--- a/llvm/include/llvm/MC/MCAsmInfo.h
+++ b/llvm/include/llvm/MC/MCAsmInfo.h
@@ -464,16 +464,10 @@ class LLVM_ABI MCAsmInfo {
   const char *getData64bitsDirective() const { return Data64bitsDirective; }
   bool supportsSignedData() const { return SupportsSignedData; }
 
-  /// Targets can implement this method to specify a section to switch to if the
-  /// translation unit doesn't have any trampolines that require an executable
-  /// stack.
-  virtual MCSection *getNonexecutableStackSection(MCContext &Ctx) const {
-    return nullptr;
-  }
-
-  /// Targets can implement this method to specify a section to switch to if the
-  /// translation has trampolines that require an executable stack.
-  virtual MCSection *getExecutableStackSection(MCContext &Ctx) const {
+  /// Targets can implement this method to specify a section to switch to
+  /// depending on whether the translation unit has any trampolines that require
+  /// an executable stack.
+  virtual MCSection *getStackSection(MCContext &Ctx, bool Exec) const {
     return nullptr;
   }
 
diff --git a/llvm/include/llvm/MC/MCAsmInfoELF.h b/llvm/include/llvm/MC/MCAsmInfoELF.h
index c5dff900227fd..7b1ee93f798e6 100644
--- a/llvm/include/llvm/MC/MCAsmInfoELF.h
+++ b/llvm/include/llvm/MC/MCAsmInfoELF.h
@@ -15,8 +15,7 @@ namespace llvm {
 
 class MCAsmInfoELF : public MCAsmInfo {
   virtual void anchor();
-  MCSection *getNonexecutableStackSection(MCContext &Ctx) const final;
-  MCSection *getExecutableStackSection(MCContext &Ctx) const final;
+  MCSection *getStackSection(MCContext &Ctx, bool Exec) const final;
   void printSwitchToSection(const MCSection &, uint32_t, const Triple &,
                             raw_ostream &) const final;
   bool useCodeAlign(const MCSection &Sec) const final;
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 39a32cc781b66..55e5dd55b25d8 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -2841,11 +2841,9 @@ bool AsmPrinter::doFinalization(Module &M) {
   // If we don't have any trampolines, then we don't require stack memory
   // to be executable. Some targets have a directive to declare this.
   Function *InitTrampolineIntrinsic = M.getFunction("llvm.init.trampoline");
-  MCSection *S = nullptr;
-  if (!InitTrampolineIntrinsic || InitTrampolineIntrinsic->use_empty())
-    S = MAI->getNonexecutableStackSection(OutContext);
-  else
-    S = MAI->getExecutableStackSection(OutContext);
+  bool HasTrampolineUses =
+      InitTrampolineIntrinsic && !InitTrampolineIntrinsic->use_empty();
+  MCSection *S = MAI->getStackSection(OutContext, /* Exec */ HasTrampolineUses);
   if (S)
     OutStreamer->switchSection(S);
 
diff --git a/llvm/lib/MC/MCAsmInfoELF.cpp b/llvm/lib/MC/MCAsmInfoELF.cpp
index 495343a2c89d2..98090d34bcbdc 100644
--- a/llvm/lib/MC/MCAsmInfoELF.cpp
+++ b/llvm/lib/MC/MCAsmInfoELF.cpp
@@ -27,20 +27,13 @@ using namespace llvm;
 
 void MCAsmInfoELF::anchor() {}
 
-MCSection *MCAsmInfoELF::getNonexecutableStackSection(MCContext &Ctx) const {
+MCSection *MCAsmInfoELF::getStackSection(MCContext &Ctx, bool Exec) const {
   // Solaris doesn't know/doesn't care about .note.GNU-stack sections, so
   // don't emit them.
   if (Ctx.getTargetTriple().isOSSolaris())
     return nullptr;
-  return Ctx.getELFSection(".note.GNU-stack", ELF::SHT_PROGBITS, 0);
-}
-
-MCSection *MCAsmInfoELF::getExecutableStackSection(MCContext &Ctx) const {
-  MCSectionELF *section =
-      static_cast<MCSectionELF *>(getNonexecutableStackSection(Ctx));
-  if (section)
-    section->setFlags(section->getFlags() | ELF::SHF_EXECINSTR);
-  return section;
+  return Ctx.getELFSection(".note.GNU-stack", ELF::SHT_PROGBITS,
+                           Exec ? ELF::SHF_EXECINSTR : 0U);
 }
 
 bool MCAsmInfoELF::useCodeAlign(const MCSection &Sec) const {
diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp
index 2881d7cfab4ba..fd65a3c80232b 100644
--- a/llvm/lib/MC/MCELFStreamer.cpp
+++ b/llvm/lib/MC/MCELFStreamer.cpp
@@ -54,7 +54,7 @@ void MCELFStreamer::initSections(bool NoExecStack, const MCSubtargetInfo &STI) {
                     &STI);
 
   if (NoExecStack)
-    switchSection(Ctx.getAsmInfo()->getNonexecutableStackSection(Ctx));
+    switchSection(Ctx.getAsmInfo()->getStackSection(Ctx, /* Exec */ false));
 }
 
 void MCELFStreamer::emitLabel(MCSymbol *S, SMLoc Loc) {
diff --git a/llvm/test/CodeGen/AArch64/trampoline.ll b/llvm/test/CodeGen/AArch64/trampoline.ll
index 6689425bf5832..3e933fadc4fa2 100644
--- a/llvm/test/CodeGen/AArch64/trampoline.ll
+++ b/llvm/test/CodeGen/AArch64/trampoline.ll
@@ -263,4 +263,9 @@ define i64 @func2() {
   %fp = call ptr @llvm.adjust.trampoline(ptr @trampg)
   ret i64 0
 }
+
+; Check for the explicitly emitted .note.GNU-stack section (ELF only) in the
+; presence of trampolines.
+; UTC_ARGS: --disable
 ; CHECK-LINUX:         .section        ".note.GNU-stack","x", at progbits
+; UTC_ARGS: --enable
diff --git a/llvm/test/CodeGen/RISCV/rv64-trampoline.ll b/llvm/test/CodeGen/RISCV/rv64-trampoline.ll
index 69c905002032d..c68fa59cd5780 100644
--- a/llvm/test/CodeGen/RISCV/rv64-trampoline.ll
+++ b/llvm/test/CodeGen/RISCV/rv64-trampoline.ll
@@ -71,7 +71,6 @@ define i64 @test0(i64 %n, ptr %p) nounwind {
 ; RV64-LINUX-NEXT:    ld ra, 56(sp) # 8-byte Folded Reload
 ; RV64-LINUX-NEXT:    addi sp, sp, 64
 ; RV64-LINUX-NEXT:    ret
-; RV64-LINUX:         .section        ".note.GNU-stack","x", at progbits
   %alloca = alloca [32 x i8], align 8
   call void @llvm.init.trampoline(ptr %alloca, ptr @f, ptr %p)
   %tramp = call ptr @llvm.adjust.trampoline(ptr %alloca)
@@ -79,3 +78,10 @@ define i64 @test0(i64 %n, ptr %p) nounwind {
   ret i64 %ret
 
 }
+
+; Check for the explicitly emitted .note.GNU-stack section (ELF only) in the
+; presence of trampolines.
+; UTC_ARGS: --disable
+; RV64-LINUX:         .section        ".note.GNU-stack","x", at progbits
+; RV64:               .section        ".note.GNU-stack","x", at progbits
+; UTC_ARGS: --enable
diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp
index f69f7c725c6b4..57a26166601f8 100644
--- a/llvm/tools/llvm-mc/llvm-mc.cpp
+++ b/llvm/tools/llvm-mc/llvm-mc.cpp
@@ -630,7 +630,8 @@ int main(int argc, char **argv) {
                : MAB->createObjectWriter(*OS),
         std::unique_ptr<MCCodeEmitter>(CE), *STI));
     if (NoExecStack)
-      Str->switchSection(Ctx.getAsmInfo()->getNonexecutableStackSection(Ctx));
+      Str->switchSection(
+          Ctx.getAsmInfo()->getStackSection(Ctx, /* Exec */ false));
     Str->emitVersionForTarget(TheTriple, VersionTuple(), nullptr,
                               VersionTuple());
   }

>From 6050654c040e44f666bf085350cb5d0feac6de67 Mon Sep 17 00:00:00 2001
From: Sanjin Sijaric <ssijaric at nvidia.com>
Date: Wed, 3 Sep 2025 09:27:27 -0700
Subject: [PATCH 4/4] Change the comments to canonical form for clang-tidy

---
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 2 +-
 llvm/lib/MC/MCELFStreamer.cpp              | 2 +-
 llvm/tools/llvm-mc/llvm-mc.cpp             | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 55e5dd55b25d8..ebe619569b23b 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -2843,7 +2843,7 @@ bool AsmPrinter::doFinalization(Module &M) {
   Function *InitTrampolineIntrinsic = M.getFunction("llvm.init.trampoline");
   bool HasTrampolineUses =
       InitTrampolineIntrinsic && !InitTrampolineIntrinsic->use_empty();
-  MCSection *S = MAI->getStackSection(OutContext, /* Exec */ HasTrampolineUses);
+  MCSection *S = MAI->getStackSection(OutContext, /*Exec=*/HasTrampolineUses);
   if (S)
     OutStreamer->switchSection(S);
 
diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp
index fd65a3c80232b..1bc1b92610871 100644
--- a/llvm/lib/MC/MCELFStreamer.cpp
+++ b/llvm/lib/MC/MCELFStreamer.cpp
@@ -54,7 +54,7 @@ void MCELFStreamer::initSections(bool NoExecStack, const MCSubtargetInfo &STI) {
                     &STI);
 
   if (NoExecStack)
-    switchSection(Ctx.getAsmInfo()->getStackSection(Ctx, /* Exec */ false));
+    switchSection(Ctx.getAsmInfo()->getStackSection(Ctx, /*Exec=*/false));
 }
 
 void MCELFStreamer::emitLabel(MCSymbol *S, SMLoc Loc) {
diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp
index 57a26166601f8..873bea04c5339 100644
--- a/llvm/tools/llvm-mc/llvm-mc.cpp
+++ b/llvm/tools/llvm-mc/llvm-mc.cpp
@@ -631,7 +631,7 @@ int main(int argc, char **argv) {
         std::unique_ptr<MCCodeEmitter>(CE), *STI));
     if (NoExecStack)
       Str->switchSection(
-          Ctx.getAsmInfo()->getStackSection(Ctx, /* Exec */ false));
+          Ctx.getAsmInfo()->getStackSection(Ctx, /*Exec=*/false));
     Str->emitVersionForTarget(TheTriple, VersionTuple(), nullptr,
                               VersionTuple());
   }



More information about the llvm-commits mailing list