Skip to content

Conversation

ssijaric-nv
Copy link
Contributor

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 if required.

@ssijaric-nv ssijaric-nv marked this pull request as ready for review August 14, 2025 17:20
@ssijaric-nv ssijaric-nv requested a review from davemgreen August 14, 2025 17:20
@llvmbot
Copy link
Member

llvmbot commented Aug 14, 2025

@llvm/pr-subscribers-mc

@llvm/pr-subscribers-backend-aarch64

Author: None (ssijaric-nv)

Changes

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 if required.


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

6 Files Affected:

  • (modified) llvm/include/llvm/MC/MCAsmInfo.h (+6)
  • (modified) llvm/include/llvm/MC/MCAsmInfoELF.h (+1)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (+6-2)
  • (modified) llvm/lib/MC/MCAsmInfoELF.cpp (+8)
  • (modified) llvm/test/CodeGen/AArch64/trampoline.ll (+1)
  • (modified) llvm/test/CodeGen/RISCV/rv64-trampoline.ll (+1)
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 1641c3eb535a9..cb697cd64d199 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -2840,9 +2840,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..495343a2c89d2 100644
--- a/llvm/lib/MC/MCAsmInfoELF.cpp
+++ b/llvm/lib/MC/MCAsmInfoELF.cpp
@@ -35,6 +35,14 @@ 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));
+  if (section)
+    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",@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",@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)

Copy link
Collaborator

Choose a reason for hiding this comment

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

To quote the top of this file:

; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5

You need ; UTC_ARGS: --disable before this to mark a region that UTC should not be managing. Maybe also best to put an enable back after it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, @jrtc27. I'll update the test cases.

Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. Same as AArch64, this is a UTC file
  2. This belongs outside the function
  3. Does non-Linux not need this too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, @jrtc27. I updated the test cases.

Copy link
Member

Choose a reason for hiding this comment

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

We should add a bool parameter to getNonexecutableStackSection and rename it to getStackSection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, @MaskRay. I renamed the getNonexecutableStackSection.

Copy link
Member

Choose a reason for hiding this comment

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

Canonical way (preferred by clang-tidy) /*Exec=*/false. ditto above

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.
@ssijaric-nv ssijaric-nv force-pushed the trampoline_exec_stack branch from 5f5a54b to 1309d6a Compare October 4, 2025 15:57
@ssijaric-nv ssijaric-nv merged commit 24c1bb6 into llvm:main Oct 4, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Oct 4, 2025

LLVM Buildbot has detected a new failure on builder openmp-s390x-linux running on systemz-1 while building llvm at step 6 "test-openmp".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/88/builds/16788

Here is the relevant piece of the build log for the reference
Step 6 (test-openmp) failure: test (failure)
******************** TEST 'libomp :: tasking/issue-94260-2.c' FAILED ********************
Exit Code: -11

Command Output (stdout):
--
# RUN: at line 1
/home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/./bin/clang -fopenmp   -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test -L /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -fno-omit-frame-pointer -mbackchain -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/ompt /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic && /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp
# executed command: /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/./bin/clang -fopenmp -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test -L /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -fno-omit-frame-pointer -mbackchain -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/ompt /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic
# executed command: /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp
# note: command had no output on stdout or stderr
# error: command failed with exit status: -11

--

********************


@arsenm
Copy link
Contributor

arsenm commented Oct 6, 2025

This is breaking the Mac build:

Undefined symbols for architecture arm64:
  "llvm::MCAsmInfoELF::getNonexecutableStackSection(llvm::MCContext&) const", referenced from:
      vtable for llvm::AVRMCAsmInfo in libLLVMAVRDesc.a[6](AVRMCAsmInfo.cpp.o)
      vtable for llvm::LoongArchMCAsmInfo in libLLVMLoongArchDesc.a[7](LoongArchMCAsmInfo.cpp.o)
      vtable for llvm::SparcELFMCAsmInfo in libLLVMSparcDesc.a[5](SparcMCAsmInfo.cpp.o)
      vtable for llvm::VEELFMCAsmInfo in libLLVMVEDesc.a[5](VEMCAsmInfo.cpp.o)

@ssijaric-nv
Copy link
Contributor Author

Thank you for letting me know, @arsenm. Is there a link to the buildbot log? I thought I removed all the traces of 'getNonexecutableStackSection'. I can revert while I investigate.

aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 6, 2025
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 if required.
@arsenm
Copy link
Contributor

arsenm commented Oct 6, 2025

Thank you for letting me know, @arsenm. Is there a link to the buildbot log? I thought I removed all the traces of 'getNonexecutableStackSection'. I can revert while I investigate.

Not sure there' a bot, we have fairly poor macOS bot coverage. touching those targets MCTargetDesc seems to have worked, so maybe there's a cmake issue somewhere?

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.

6 participants