Skip to content

[AArch64] Add support for SHF_AARCH64_PURECODE ELF section flag (1/3) #125687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 14, 2025

Conversation

Il-Capitano
Copy link
Contributor

@Il-Capitano Il-Capitano commented Feb 4, 2025

Add support for the new SHF_AARCH64_PURECODE ELF section flag: ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets. Generating object files with the SHF_AARCH64_PURECODE flag set is enabled by the +execute-only target feature.

Related PRs:

Add support for the new SHF_AARCH64_PURECODE ELF section flag: ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets.
Generating object files with the `SHF_AARCH64_PURECODE` flag set is
enabled by the `+execute-only` target feature.
@llvmbot
Copy link
Member

llvmbot commented Feb 4, 2025

@llvm/pr-subscribers-objectyaml
@llvm/pr-subscribers-llvm-binary-utilities

@llvm/pr-subscribers-mc

Author: Csanád Hajdú (Il-Capitano)

Changes

Add support for the new SHF_AARCH64_PURECODE ELF section flag: ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets. Generating object files with the SHF_AARCH64_PURECODE flag set is enabled by the +execute-only target feature.


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

18 Files Affected:

  • (modified) llvm/include/llvm/BinaryFormat/ELF.h (+4-1)
  • (modified) llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp (+8-5)
  • (modified) llvm/lib/MC/MCParser/ELFAsmParser.cpp (+5-2)
  • (modified) llvm/lib/MC/MCSectionELF.cpp (+3)
  • (modified) llvm/lib/ObjectYAML/ELFYAML.cpp (+3)
  • (modified) llvm/lib/Target/AArch64/AArch64Features.td (+5)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp (+26)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.h (+6)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp (+16-1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h (+2)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp (+18)
  • (added) llvm/test/CodeGen/AArch64/execute-only-section.ll (+21)
  • (added) llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s (+27)
  • (added) llvm/test/MC/ELF/AArch64/execute-only-section.s (+43)
  • (added) llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s (+27)
  • (modified) llvm/test/MC/ELF/section-flags-unknown.s (+3)
  • (modified) llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll (+25)
  • (modified) llvm/tools/llvm-readobj/ELFDumper.cpp (+8)
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 8853c4a88b0b59..a5979d029a9eaf 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1300,7 +1300,10 @@ enum : unsigned {
   SHF_MIPS_STRING = 0x80000000,
 
   // Make code section unreadable when in execute-only mode
-  SHF_ARM_PURECODE = 0x20000000
+  SHF_ARM_PURECODE = 0x20000000,
+
+  // Section contains only program instructions and no program data.
+  SHF_AARCH64_PURECODE = 0x20000000
 };
 
 // Section Group Flags
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 3c2c7c8c9fed69..6cfebca9a4d576 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -547,7 +547,7 @@ static unsigned getELFSectionType(StringRef Name, SectionKind K) {
   return ELF::SHT_PROGBITS;
 }
 
-static unsigned getELFSectionFlags(SectionKind K) {
+static unsigned getELFSectionFlags(SectionKind K, const Triple &T) {
   unsigned Flags = 0;
 
   if (!K.isMetadata() && !K.isExclude())
@@ -559,9 +559,12 @@ static unsigned getELFSectionFlags(SectionKind K) {
   if (K.isText())
     Flags |= ELF::SHF_EXECINSTR;
 
-  if (K.isExecuteOnly())
+  if ((T.isARM() || T.isThumb()) && K.isExecuteOnly())
     Flags |= ELF::SHF_ARM_PURECODE;
 
+  if (T.isAArch64() && K.isExecuteOnly())
+    Flags |= ELF::SHF_AARCH64_PURECODE;
+
   if (K.isWriteable())
     Flags |= ELF::SHF_WRITE;
 
@@ -840,7 +843,7 @@ static MCSection *selectExplicitSectionGlobal(const GlobalObject *GO,
   // Infer section flags from the section name if we can.
   Kind = getELFKindForNamedSection(SectionName, Kind);
 
-  unsigned Flags = getELFSectionFlags(Kind);
+  unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
   auto [Group, IsComdat, ExtraFlags] = getGlobalObjectInfo(GO, TM);
   Flags |= ExtraFlags;
 
@@ -947,7 +950,7 @@ static MCSection *selectELFSectionForGlobal(
 
 MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
     const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
-  unsigned Flags = getELFSectionFlags(Kind);
+  unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
 
   // If we have -ffunction-section or -fdata-section then we should emit the
   // global value to a uniqued section specifically for it.
@@ -967,7 +970,7 @@ MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
 MCSection *TargetLoweringObjectFileELF::getUniqueSectionForFunction(
     const Function &F, const TargetMachine &TM) const {
   SectionKind Kind = SectionKind::getText();
-  unsigned Flags = getELFSectionFlags(Kind);
+  unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
   // If the function's section names is pre-determined via pragma or a
   // section attribute, call selectExplicitSectionGlobal.
   if (F.hasSection())
diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
index b58210b3c268e9..99b13c68a9966c 100644
--- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
@@ -328,9 +328,12 @@ static unsigned parseSectionFlags(const Triple &TT, StringRef flagsStr,
       flags |= ELF::XCORE_SHF_DP_SECTION;
       break;
     case 'y':
-      if (!(TT.isARM() || TT.isThumb()))
+      if (TT.isARM() || TT.isThumb())
+        flags |= ELF::SHF_ARM_PURECODE;
+      else if (TT.isAArch64())
+        flags |= ELF::SHF_AARCH64_PURECODE;
+      else
         return -1U;
-      flags |= ELF::SHF_ARM_PURECODE;
       break;
     case 's':
       if (TT.getArch() != Triple::hexagon)
diff --git a/llvm/lib/MC/MCSectionELF.cpp b/llvm/lib/MC/MCSectionELF.cpp
index 25e62b70b5e2a0..72a959b1c92082 100644
--- a/llvm/lib/MC/MCSectionELF.cpp
+++ b/llvm/lib/MC/MCSectionELF.cpp
@@ -118,6 +118,9 @@ void MCSectionELF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
   } else if (T.isARM() || T.isThumb()) {
     if (Flags & ELF::SHF_ARM_PURECODE)
       OS << 'y';
+  } else if (T.isAArch64()) {
+    if (Flags & ELF::SHF_AARCH64_PURECODE)
+      OS << 'y';
   } else if (Arch == Triple::hexagon) {
     if (Flags & ELF::SHF_HEX_GPREL)
       OS << 's';
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 539834fc8d4dbf..05e4d85b2ea5d2 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -803,6 +803,9 @@ void ScalarBitSetTraits<ELFYAML::ELF_SHF>::bitset(IO &IO,
     break;
   }
   switch (Object->getMachine()) {
+  case ELF::EM_AARCH64:
+    BCase(SHF_AARCH64_PURECODE);
+    break;
   case ELF::EM_ARM:
     BCase(SHF_ARM_PURECODE);
     break;
diff --git a/llvm/lib/Target/AArch64/AArch64Features.td b/llvm/lib/Target/AArch64/AArch64Features.td
index 20db70ee38572f..7c95ba662938d9 100644
--- a/llvm/lib/Target/AArch64/AArch64Features.td
+++ b/llvm/lib/Target/AArch64/AArch64Features.td
@@ -635,6 +635,11 @@ def FeatureStrictAlign : SubtargetFeature<"strict-align",
                                           "Disallow all unaligned memory "
                                           "access">;
 
+def FeatureExecuteOnly : SubtargetFeature<"execute-only",
+                                          "GenExecuteOnly", "true",
+                                          "Enable the generation of "
+                                          "execute only code.">;
+
 foreach i = {1-7,9-15,18,20-28} in
     def FeatureReserveX#i : SubtargetFeature<"reserve-x"#i, "ReserveXRegister["#i#"]", "true",
                                              "Reserve X"#i#", making it unavailable "
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
index 8729fd4b802c8e..434ae32502d484 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
@@ -148,3 +148,29 @@ MCSymbol *AArch64_MachoTargetObjectFile::getAuthPtrSlotSymbol(
   return getAuthPtrSlotSymbolHelper(getContext(), TM, MMI, MachOMMI, RawSym,
                                     Key, Discriminator);
 }
+
+static bool isExecuteOnlyFunction(const GlobalObject *GO, SectionKind Kind,
+                                  const TargetMachine &TM) {
+  if (const Function *F = dyn_cast<Function>(GO))
+    if (TM.getSubtarget<AArch64Subtarget>(*F).genExecuteOnly() && Kind.isText())
+      return true;
+  return false;
+}
+
+MCSection *AArch64_ELFTargetObjectFile::getExplicitSectionGlobal(
+    const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
+  // Set execute-only access for the explicit section
+  if (isExecuteOnlyFunction(GO, Kind, TM))
+    Kind = SectionKind::getExecuteOnly();
+
+  return TargetLoweringObjectFileELF::getExplicitSectionGlobal(GO, Kind, TM);
+}
+
+MCSection *AArch64_ELFTargetObjectFile::SelectSectionForGlobal(
+    const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
+  // Set execute-only access for the explicit section
+  if (isExecuteOnlyFunction(GO, Kind, TM))
+    Kind = SectionKind::getExecuteOnly();
+
+  return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
+}
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
index 0c822ac84f200c..3e9cd51b742a29 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
@@ -39,6 +39,12 @@ class AArch64_ELFTargetObjectFile : public TargetLoweringObjectFileELF {
   void emitPersonalityValueImpl(MCStreamer &Streamer, const DataLayout &DL,
                                 const MCSymbol *Sym,
                                 const MachineModuleInfo *MMI) const override;
+
+  MCSection *getExplicitSectionGlobal(const GlobalObject *GO, SectionKind Kind,
+                                      const TargetMachine &TM) const override;
+
+  MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
+                                    const TargetMachine &TM) const override;
 };
 
 /// AArch64_MachoTargetObjectFile - This TLOF implementation is used for Darwin.
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index a6edcf125782b2..284278e34f8b4d 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -262,6 +262,10 @@ bool AArch64TTIImpl::isMultiversionedFunction(const Function &F) const {
   return F.hasFnAttribute("fmv-features");
 }
 
+const FeatureBitset AArch64TTIImpl::InlineInverseFeatures = {
+    AArch64::FeatureExecuteOnly,
+};
+
 bool AArch64TTIImpl::areInlineCompatible(const Function *Caller,
                                          const Function *Callee) const {
   SMEAttrs CallerAttrs(*Caller), CalleeAttrs(*Callee);
@@ -284,7 +288,18 @@ bool AArch64TTIImpl::areInlineCompatible(const Function *Caller,
       return false;
   }
 
-  return BaseT::areInlineCompatible(Caller, Callee);
+  const TargetMachine &TM = getTLI()->getTargetMachine();
+  const FeatureBitset &CallerBits =
+      TM.getSubtargetImpl(*Caller)->getFeatureBits();
+  const FeatureBitset &CalleeBits =
+      TM.getSubtargetImpl(*Callee)->getFeatureBits();
+  // Adjust the feature bitsets by inverting some of the bits. This is needed
+  // for target features that represent restrictions rather than capabilities,
+  // for example "+execute-only".
+  FeatureBitset EffectiveCallerBits = CallerBits ^ InlineInverseFeatures;
+  FeatureBitset EffectiveCalleeBits = CalleeBits ^ InlineInverseFeatures;
+
+  return (EffectiveCallerBits & EffectiveCalleeBits) == EffectiveCalleeBits;
 }
 
 bool AArch64TTIImpl::areTypesABICompatible(
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
index b65e3c7a1ab20e..481cb5511a331b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
@@ -48,6 +48,8 @@ class AArch64TTIImpl : public BasicTTIImplBase<AArch64TTIImpl> {
   const AArch64Subtarget *ST;
   const AArch64TargetLowering *TLI;
 
+  static const FeatureBitset InlineInverseFeatures;
+
   const AArch64Subtarget *getST() const { return ST; }
   const AArch64TargetLowering *getTLI() const { return TLI; }
 
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
index 6b5c5f36cbd4b2..d29d383bc23127 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
@@ -27,6 +27,7 @@
 #include "llvm/MC/MCELFStreamer.h"
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCObjectFileInfo.h"
 #include "llvm/MC/MCSectionELF.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/MC/MCSubtargetInfo.h"
@@ -504,6 +505,23 @@ void AArch64TargetELFStreamer::finish() {
     }
   }
 
+  // The mix of execute-only and non-execute-only at link time is
+  // non-execute-only. To avoid the empty implicitly created .text
+  // section from making the whole .text section non-execute-only, we
+  // mark it execute-only if it is empty and there is at least one
+  // execute-only section in the object.
+  if (any_of(Asm, [](const MCSection &Sec) {
+        return cast<MCSectionELF>(Sec).getFlags() & ELF::SHF_AARCH64_PURECODE;
+      })) {
+    auto *Text =
+        static_cast<MCSectionELF *>(Ctx.getObjectFileInfo()->getTextSection());
+    for (auto &F : *Text)
+      if (auto *DF = dyn_cast<MCDataFragment>(&F))
+        if (!DF->getContents().empty())
+          return;
+    Text->setFlags(Text->getFlags() | ELF::SHF_AARCH64_PURECODE);
+  }
+
   MCSectionELF *MemtagSec = nullptr;
   for (const MCSymbol &Symbol : Asm.symbols()) {
     const auto &Sym = cast<MCSymbolELF>(Symbol);
diff --git a/llvm/test/CodeGen/AArch64/execute-only-section.ll b/llvm/test/CodeGen/AArch64/execute-only-section.ll
new file mode 100644
index 00000000000000..15a270b07975e6
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/execute-only-section.ll
@@ -0,0 +1,21 @@
+; RUN: llc -mtriple=aarch64 -mattr=+execute-only %s -o - | FileCheck %s
+
+; CHECK:     .section .text,"axy",@progbits,unique,0
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK:     .globl test_SectionForGlobal
+; CHECK:     .type test_SectionForGlobal,@function
+define void @test_SectionForGlobal() {
+entry:
+  ret void
+}
+
+; CHECK:     .section .test,"axy",@progbits
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK:     .globl test_ExplicitSectionForGlobal
+; CHECK:     .type test_ExplicitSectionForGlobal,@function
+define void @test_ExplicitSectionForGlobal() section ".test" {
+entry:
+  ret void
+}
diff --git a/llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s b/llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s
new file mode 100644
index 00000000000000..b0ba8a18d52b38
--- /dev/null
+++ b/llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s
@@ -0,0 +1,27 @@
+// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
+// RUN: | llvm-readobj -S --symbols - | FileCheck %s
+
+        .text
+        ret
+
+        .section        .text.foo,"axy"
+        ret
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x6)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
+
+// CHECK:      Section {
+// CHECK:        Name: .text.foo
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
diff --git a/llvm/test/MC/ELF/AArch64/execute-only-section.s b/llvm/test/MC/ELF/AArch64/execute-only-section.s
new file mode 100644
index 00000000000000..6fd596376711d6
--- /dev/null
+++ b/llvm/test/MC/ELF/AArch64/execute-only-section.s
@@ -0,0 +1,43 @@
+// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
+// RUN: | llvm-readobj -S --symbols - | FileCheck %s
+
+        .section        .text,"axy",@progbits,unique,0
+        .globl  foo
+        .p2align  2
+        .type   foo,@function
+foo:
+        .cfi_startproc
+        ret
+.Lfunc_end0:
+        .size   foo, .Lfunc_end0-foo
+        .cfi_endproc
+
+        .section        ".note.GNU-stack","",@progbits
+
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:        Size: 0
+// CHECK:      }
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:        Size: 2
+// CHECK:      }
+
+// CHECK: Symbol {
+// CHECK:   Name: foo
+// CHECK:   Section: .text (0x3)
+// CHECK: }
diff --git a/llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s b/llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s
new file mode 100644
index 00000000000000..22bcffae67d0eb
--- /dev/null
+++ b/llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s
@@ -0,0 +1,27 @@
+// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
+// RUN: | llvm-readobj -S --symbols - | FileCheck %s
+
+        .text
+        .ascii "test"
+
+        .section        .text.foo,"axy"
+        ret
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x6)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
+
+// CHECK:      Section {
+// CHECK:        Name: .text.foo
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
diff --git a/llvm/test/MC/ELF/section-flags-unknown.s b/llvm/test/MC/ELF/section-flags-unknown.s
index 90c9185e8bfb10..b172403e34556c 100644
--- a/llvm/test/MC/ELF/section-flags-unknown.s
+++ b/llvm/test/MC/ELF/section-flags-unknown.s
@@ -14,5 +14,8 @@
 # CHECK: {{.*}}.s:[[# @LINE+1]]:30: error: unknown flag
 .section SHF_ARM_PURECODE,"y",@progbits
 
+# CHECK: {{.*}}.s:[[# @LINE+1]]:34: error: unknown flag
+.section SHF_AARCH64_PURECODE,"y",@progbits
+
 # CHECK: {{.*}}.s:[[# @LINE+1]]:30: error: unknown flag
 .section SHF_X86_64_LARGE,"l",@progbits
diff --git a/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll b/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll
index 636f3c6600f8d4..433a9c7bdd23b0 100644
--- a/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll
+++ b/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll
@@ -27,6 +27,14 @@ entry:
 ; CHECK: call i32 @bar()
 }
 
+define i32 @quux() #3 {
+entry:
+  %call = call i32 (...) @baz()
+  ret i32 %call
+; CHECK-LABEL: quux
+; CHECK: call i32 (...) @baz()
+}
+
 define i32 @strict_align() #2 {
 entry:
   %call = call i32 @foo()
@@ -35,6 +43,23 @@ entry:
 ; CHECK: call i32 (...) @baz()
 }
 
+define i32 @execute_only1() #3 {
+entry:
+  %call = call i32 @foo()
+  ret i32 %call
+; CHECK-LABEL: execute_only1
+; CHECK: call i32 @foo()
+}
+
+define i32 @execute_only2() #0 {
+entry:
+  %call = call i32 @quux()
+  ret i32 %call
+; CHECK-LABEL: execute_only2
+; CHECK: call i32 (...) @baz()
+}
+
 attributes #0 = { "target-cpu"="generic" "target-features"="+crc,+neon" }
 attributes #1 = { "target-cpu"="generic" "target-features"="+crc,+neon,+crypto" }
 attributes #2 = { "target-cpu"="generic" "target-features"="+crc,+neon,+strict-align" }
+attributes #3 = { "target-cpu"="generic" "target-features"="+crc,+neon,+execute-only" }
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index bfca65aad52b44..78aaf79a61c468 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -1337,6 +1337,10 @@ const EnumEntry<unsigned> ElfXCoreSectionFlags[] = {
   ENUM_ENT(XCORE_SHF_DP_SECTION, "")
 };
 
+const EnumEntry<unsigned> ElfAArch64SectionFlags[] = {
+    ENUM_ENT(SHF_AARCH64_PURECODE, "y"),
+};
+
 const EnumEntry<unsigned> ElfARMSectionFlags[] = {
   ENUM_ENT(SHF_ARM_PURECODE, "y")
 };
@@ -1375,6 +1379,10 @@ getSectionFlagsForTarget(unsigned EOSAbi, unsigned EMachine) {
     break;
   }
   switch (EMachine) {
+  case EM_AARCH64:
+    Ret.insert(Ret.end(), std::begin(ElfAArch64SectionFlags),
+               std::end(ElfAArch64SectionFlags));
+    break;
   case EM_ARM:
     Ret.insert(Ret.end(), std::begin(ElfARMSectionFlags),
                std::end(ElfARMSectionFlags));

@llvmbot
Copy link
Member

llvmbot commented Feb 4, 2025

@llvm/pr-subscribers-backend-aarch64

Author: Csanád Hajdú (Il-Capitano)

Changes

Add support for the new SHF_AARCH64_PURECODE ELF section flag: ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets. Generating object files with the SHF_AARCH64_PURECODE flag set is enabled by the +execute-only target feature.


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

18 Files Affected:

  • (modified) llvm/include/llvm/BinaryFormat/ELF.h (+4-1)
  • (modified) llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp (+8-5)
  • (modified) llvm/lib/MC/MCParser/ELFAsmParser.cpp (+5-2)
  • (modified) llvm/lib/MC/MCSectionELF.cpp (+3)
  • (modified) llvm/lib/ObjectYAML/ELFYAML.cpp (+3)
  • (modified) llvm/lib/Target/AArch64/AArch64Features.td (+5)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp (+26)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.h (+6)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp (+16-1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h (+2)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp (+18)
  • (added) llvm/test/CodeGen/AArch64/execute-only-section.ll (+21)
  • (added) llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s (+27)
  • (added) llvm/test/MC/ELF/AArch64/execute-only-section.s (+43)
  • (added) llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s (+27)
  • (modified) llvm/test/MC/ELF/section-flags-unknown.s (+3)
  • (modified) llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll (+25)
  • (modified) llvm/tools/llvm-readobj/ELFDumper.cpp (+8)
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 8853c4a88b0b593..a5979d029a9eafd 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1300,7 +1300,10 @@ enum : unsigned {
   SHF_MIPS_STRING = 0x80000000,
 
   // Make code section unreadable when in execute-only mode
-  SHF_ARM_PURECODE = 0x20000000
+  SHF_ARM_PURECODE = 0x20000000,
+
+  // Section contains only program instructions and no program data.
+  SHF_AARCH64_PURECODE = 0x20000000
 };
 
 // Section Group Flags
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 3c2c7c8c9fed69a..6cfebca9a4d576e 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -547,7 +547,7 @@ static unsigned getELFSectionType(StringRef Name, SectionKind K) {
   return ELF::SHT_PROGBITS;
 }
 
-static unsigned getELFSectionFlags(SectionKind K) {
+static unsigned getELFSectionFlags(SectionKind K, const Triple &T) {
   unsigned Flags = 0;
 
   if (!K.isMetadata() && !K.isExclude())
@@ -559,9 +559,12 @@ static unsigned getELFSectionFlags(SectionKind K) {
   if (K.isText())
     Flags |= ELF::SHF_EXECINSTR;
 
-  if (K.isExecuteOnly())
+  if ((T.isARM() || T.isThumb()) && K.isExecuteOnly())
     Flags |= ELF::SHF_ARM_PURECODE;
 
+  if (T.isAArch64() && K.isExecuteOnly())
+    Flags |= ELF::SHF_AARCH64_PURECODE;
+
   if (K.isWriteable())
     Flags |= ELF::SHF_WRITE;
 
@@ -840,7 +843,7 @@ static MCSection *selectExplicitSectionGlobal(const GlobalObject *GO,
   // Infer section flags from the section name if we can.
   Kind = getELFKindForNamedSection(SectionName, Kind);
 
-  unsigned Flags = getELFSectionFlags(Kind);
+  unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
   auto [Group, IsComdat, ExtraFlags] = getGlobalObjectInfo(GO, TM);
   Flags |= ExtraFlags;
 
@@ -947,7 +950,7 @@ static MCSection *selectELFSectionForGlobal(
 
 MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
     const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
-  unsigned Flags = getELFSectionFlags(Kind);
+  unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
 
   // If we have -ffunction-section or -fdata-section then we should emit the
   // global value to a uniqued section specifically for it.
@@ -967,7 +970,7 @@ MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
 MCSection *TargetLoweringObjectFileELF::getUniqueSectionForFunction(
     const Function &F, const TargetMachine &TM) const {
   SectionKind Kind = SectionKind::getText();
-  unsigned Flags = getELFSectionFlags(Kind);
+  unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
   // If the function's section names is pre-determined via pragma or a
   // section attribute, call selectExplicitSectionGlobal.
   if (F.hasSection())
diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
index b58210b3c268e99..99b13c68a9966c7 100644
--- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
@@ -328,9 +328,12 @@ static unsigned parseSectionFlags(const Triple &TT, StringRef flagsStr,
       flags |= ELF::XCORE_SHF_DP_SECTION;
       break;
     case 'y':
-      if (!(TT.isARM() || TT.isThumb()))
+      if (TT.isARM() || TT.isThumb())
+        flags |= ELF::SHF_ARM_PURECODE;
+      else if (TT.isAArch64())
+        flags |= ELF::SHF_AARCH64_PURECODE;
+      else
         return -1U;
-      flags |= ELF::SHF_ARM_PURECODE;
       break;
     case 's':
       if (TT.getArch() != Triple::hexagon)
diff --git a/llvm/lib/MC/MCSectionELF.cpp b/llvm/lib/MC/MCSectionELF.cpp
index 25e62b70b5e2a0c..72a959b1c920825 100644
--- a/llvm/lib/MC/MCSectionELF.cpp
+++ b/llvm/lib/MC/MCSectionELF.cpp
@@ -118,6 +118,9 @@ void MCSectionELF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
   } else if (T.isARM() || T.isThumb()) {
     if (Flags & ELF::SHF_ARM_PURECODE)
       OS << 'y';
+  } else if (T.isAArch64()) {
+    if (Flags & ELF::SHF_AARCH64_PURECODE)
+      OS << 'y';
   } else if (Arch == Triple::hexagon) {
     if (Flags & ELF::SHF_HEX_GPREL)
       OS << 's';
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 539834fc8d4dbfe..05e4d85b2ea5d24 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -803,6 +803,9 @@ void ScalarBitSetTraits<ELFYAML::ELF_SHF>::bitset(IO &IO,
     break;
   }
   switch (Object->getMachine()) {
+  case ELF::EM_AARCH64:
+    BCase(SHF_AARCH64_PURECODE);
+    break;
   case ELF::EM_ARM:
     BCase(SHF_ARM_PURECODE);
     break;
diff --git a/llvm/lib/Target/AArch64/AArch64Features.td b/llvm/lib/Target/AArch64/AArch64Features.td
index 20db70ee38572f2..7c95ba662938d9f 100644
--- a/llvm/lib/Target/AArch64/AArch64Features.td
+++ b/llvm/lib/Target/AArch64/AArch64Features.td
@@ -635,6 +635,11 @@ def FeatureStrictAlign : SubtargetFeature<"strict-align",
                                           "Disallow all unaligned memory "
                                           "access">;
 
+def FeatureExecuteOnly : SubtargetFeature<"execute-only",
+                                          "GenExecuteOnly", "true",
+                                          "Enable the generation of "
+                                          "execute only code.">;
+
 foreach i = {1-7,9-15,18,20-28} in
     def FeatureReserveX#i : SubtargetFeature<"reserve-x"#i, "ReserveXRegister["#i#"]", "true",
                                              "Reserve X"#i#", making it unavailable "
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
index 8729fd4b802c8eb..434ae32502d484b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
@@ -148,3 +148,29 @@ MCSymbol *AArch64_MachoTargetObjectFile::getAuthPtrSlotSymbol(
   return getAuthPtrSlotSymbolHelper(getContext(), TM, MMI, MachOMMI, RawSym,
                                     Key, Discriminator);
 }
+
+static bool isExecuteOnlyFunction(const GlobalObject *GO, SectionKind Kind,
+                                  const TargetMachine &TM) {
+  if (const Function *F = dyn_cast<Function>(GO))
+    if (TM.getSubtarget<AArch64Subtarget>(*F).genExecuteOnly() && Kind.isText())
+      return true;
+  return false;
+}
+
+MCSection *AArch64_ELFTargetObjectFile::getExplicitSectionGlobal(
+    const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
+  // Set execute-only access for the explicit section
+  if (isExecuteOnlyFunction(GO, Kind, TM))
+    Kind = SectionKind::getExecuteOnly();
+
+  return TargetLoweringObjectFileELF::getExplicitSectionGlobal(GO, Kind, TM);
+}
+
+MCSection *AArch64_ELFTargetObjectFile::SelectSectionForGlobal(
+    const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
+  // Set execute-only access for the explicit section
+  if (isExecuteOnlyFunction(GO, Kind, TM))
+    Kind = SectionKind::getExecuteOnly();
+
+  return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
+}
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
index 0c822ac84f200c7..3e9cd51b742a29c 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
@@ -39,6 +39,12 @@ class AArch64_ELFTargetObjectFile : public TargetLoweringObjectFileELF {
   void emitPersonalityValueImpl(MCStreamer &Streamer, const DataLayout &DL,
                                 const MCSymbol *Sym,
                                 const MachineModuleInfo *MMI) const override;
+
+  MCSection *getExplicitSectionGlobal(const GlobalObject *GO, SectionKind Kind,
+                                      const TargetMachine &TM) const override;
+
+  MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
+                                    const TargetMachine &TM) const override;
 };
 
 /// AArch64_MachoTargetObjectFile - This TLOF implementation is used for Darwin.
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index a6edcf125782b2c..284278e34f8b4d0 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -262,6 +262,10 @@ bool AArch64TTIImpl::isMultiversionedFunction(const Function &F) const {
   return F.hasFnAttribute("fmv-features");
 }
 
+const FeatureBitset AArch64TTIImpl::InlineInverseFeatures = {
+    AArch64::FeatureExecuteOnly,
+};
+
 bool AArch64TTIImpl::areInlineCompatible(const Function *Caller,
                                          const Function *Callee) const {
   SMEAttrs CallerAttrs(*Caller), CalleeAttrs(*Callee);
@@ -284,7 +288,18 @@ bool AArch64TTIImpl::areInlineCompatible(const Function *Caller,
       return false;
   }
 
-  return BaseT::areInlineCompatible(Caller, Callee);
+  const TargetMachine &TM = getTLI()->getTargetMachine();
+  const FeatureBitset &CallerBits =
+      TM.getSubtargetImpl(*Caller)->getFeatureBits();
+  const FeatureBitset &CalleeBits =
+      TM.getSubtargetImpl(*Callee)->getFeatureBits();
+  // Adjust the feature bitsets by inverting some of the bits. This is needed
+  // for target features that represent restrictions rather than capabilities,
+  // for example "+execute-only".
+  FeatureBitset EffectiveCallerBits = CallerBits ^ InlineInverseFeatures;
+  FeatureBitset EffectiveCalleeBits = CalleeBits ^ InlineInverseFeatures;
+
+  return (EffectiveCallerBits & EffectiveCalleeBits) == EffectiveCalleeBits;
 }
 
 bool AArch64TTIImpl::areTypesABICompatible(
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
index b65e3c7a1ab20e5..481cb5511a331b7 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
@@ -48,6 +48,8 @@ class AArch64TTIImpl : public BasicTTIImplBase<AArch64TTIImpl> {
   const AArch64Subtarget *ST;
   const AArch64TargetLowering *TLI;
 
+  static const FeatureBitset InlineInverseFeatures;
+
   const AArch64Subtarget *getST() const { return ST; }
   const AArch64TargetLowering *getTLI() const { return TLI; }
 
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
index 6b5c5f36cbd4b27..d29d383bc231277 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
@@ -27,6 +27,7 @@
 #include "llvm/MC/MCELFStreamer.h"
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCObjectFileInfo.h"
 #include "llvm/MC/MCSectionELF.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/MC/MCSubtargetInfo.h"
@@ -504,6 +505,23 @@ void AArch64TargetELFStreamer::finish() {
     }
   }
 
+  // The mix of execute-only and non-execute-only at link time is
+  // non-execute-only. To avoid the empty implicitly created .text
+  // section from making the whole .text section non-execute-only, we
+  // mark it execute-only if it is empty and there is at least one
+  // execute-only section in the object.
+  if (any_of(Asm, [](const MCSection &Sec) {
+        return cast<MCSectionELF>(Sec).getFlags() & ELF::SHF_AARCH64_PURECODE;
+      })) {
+    auto *Text =
+        static_cast<MCSectionELF *>(Ctx.getObjectFileInfo()->getTextSection());
+    for (auto &F : *Text)
+      if (auto *DF = dyn_cast<MCDataFragment>(&F))
+        if (!DF->getContents().empty())
+          return;
+    Text->setFlags(Text->getFlags() | ELF::SHF_AARCH64_PURECODE);
+  }
+
   MCSectionELF *MemtagSec = nullptr;
   for (const MCSymbol &Symbol : Asm.symbols()) {
     const auto &Sym = cast<MCSymbolELF>(Symbol);
diff --git a/llvm/test/CodeGen/AArch64/execute-only-section.ll b/llvm/test/CodeGen/AArch64/execute-only-section.ll
new file mode 100644
index 000000000000000..15a270b07975e69
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/execute-only-section.ll
@@ -0,0 +1,21 @@
+; RUN: llc -mtriple=aarch64 -mattr=+execute-only %s -o - | FileCheck %s
+
+; CHECK:     .section .text,"axy",@progbits,unique,0
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK:     .globl test_SectionForGlobal
+; CHECK:     .type test_SectionForGlobal,@function
+define void @test_SectionForGlobal() {
+entry:
+  ret void
+}
+
+; CHECK:     .section .test,"axy",@progbits
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK:     .globl test_ExplicitSectionForGlobal
+; CHECK:     .type test_ExplicitSectionForGlobal,@function
+define void @test_ExplicitSectionForGlobal() section ".test" {
+entry:
+  ret void
+}
diff --git a/llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s b/llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s
new file mode 100644
index 000000000000000..b0ba8a18d52b386
--- /dev/null
+++ b/llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s
@@ -0,0 +1,27 @@
+// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
+// RUN: | llvm-readobj -S --symbols - | FileCheck %s
+
+        .text
+        ret
+
+        .section        .text.foo,"axy"
+        ret
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x6)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
+
+// CHECK:      Section {
+// CHECK:        Name: .text.foo
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
diff --git a/llvm/test/MC/ELF/AArch64/execute-only-section.s b/llvm/test/MC/ELF/AArch64/execute-only-section.s
new file mode 100644
index 000000000000000..6fd596376711d6a
--- /dev/null
+++ b/llvm/test/MC/ELF/AArch64/execute-only-section.s
@@ -0,0 +1,43 @@
+// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
+// RUN: | llvm-readobj -S --symbols - | FileCheck %s
+
+        .section        .text,"axy",@progbits,unique,0
+        .globl  foo
+        .p2align  2
+        .type   foo,@function
+foo:
+        .cfi_startproc
+        ret
+.Lfunc_end0:
+        .size   foo, .Lfunc_end0-foo
+        .cfi_endproc
+
+        .section        ".note.GNU-stack","",@progbits
+
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:        Size: 0
+// CHECK:      }
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:        Size: 2
+// CHECK:      }
+
+// CHECK: Symbol {
+// CHECK:   Name: foo
+// CHECK:   Section: .text (0x3)
+// CHECK: }
diff --git a/llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s b/llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s
new file mode 100644
index 000000000000000..22bcffae67d0eb2
--- /dev/null
+++ b/llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s
@@ -0,0 +1,27 @@
+// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
+// RUN: | llvm-readobj -S --symbols - | FileCheck %s
+
+        .text
+        .ascii "test"
+
+        .section        .text.foo,"axy"
+        ret
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x6)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
+
+// CHECK:      Section {
+// CHECK:        Name: .text.foo
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
diff --git a/llvm/test/MC/ELF/section-flags-unknown.s b/llvm/test/MC/ELF/section-flags-unknown.s
index 90c9185e8bfb108..b172403e34556ca 100644
--- a/llvm/test/MC/ELF/section-flags-unknown.s
+++ b/llvm/test/MC/ELF/section-flags-unknown.s
@@ -14,5 +14,8 @@
 # CHECK: {{.*}}.s:[[# @LINE+1]]:30: error: unknown flag
 .section SHF_ARM_PURECODE,"y",@progbits
 
+# CHECK: {{.*}}.s:[[# @LINE+1]]:34: error: unknown flag
+.section SHF_AARCH64_PURECODE,"y",@progbits
+
 # CHECK: {{.*}}.s:[[# @LINE+1]]:30: error: unknown flag
 .section SHF_X86_64_LARGE,"l",@progbits
diff --git a/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll b/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll
index 636f3c6600f8d4c..433a9c7bdd23b01 100644
--- a/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll
+++ b/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll
@@ -27,6 +27,14 @@ entry:
 ; CHECK: call i32 @bar()
 }
 
+define i32 @quux() #3 {
+entry:
+  %call = call i32 (...) @baz()
+  ret i32 %call
+; CHECK-LABEL: quux
+; CHECK: call i32 (...) @baz()
+}
+
 define i32 @strict_align() #2 {
 entry:
   %call = call i32 @foo()
@@ -35,6 +43,23 @@ entry:
 ; CHECK: call i32 (...) @baz()
 }
 
+define i32 @execute_only1() #3 {
+entry:
+  %call = call i32 @foo()
+  ret i32 %call
+; CHECK-LABEL: execute_only1
+; CHECK: call i32 @foo()
+}
+
+define i32 @execute_only2() #0 {
+entry:
+  %call = call i32 @quux()
+  ret i32 %call
+; CHECK-LABEL: execute_only2
+; CHECK: call i32 (...) @baz()
+}
+
 attributes #0 = { "target-cpu"="generic" "target-features"="+crc,+neon" }
 attributes #1 = { "target-cpu"="generic" "target-features"="+crc,+neon,+crypto" }
 attributes #2 = { "target-cpu"="generic" "target-features"="+crc,+neon,+strict-align" }
+attributes #3 = { "target-cpu"="generic" "target-features"="+crc,+neon,+execute-only" }
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index bfca65aad52b443..78aaf79a61c468e 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -1337,6 +1337,10 @@ const EnumEntry<unsigned> ElfXCoreSectionFlags[] = {
   ENUM_ENT(XCORE_SHF_DP_SECTION, "")
 };
 
+const EnumEntry<unsigned> ElfAArch64SectionFlags[] = {
+    ENUM_ENT(SHF_AARCH64_PURECODE, "y"),
+};
+
 const EnumEntry<unsigned> ElfARMSectionFlags[] = {
   ENUM_ENT(SHF_ARM_PURECODE, "y")
 };
@@ -1375,6 +1379,10 @@ getSectionFlagsForTarget(unsigned EOSAbi, unsigned EMachine) {
     break;
   }
   switch (EMachine) {
+  case EM_AARCH64:
+    Ret.insert(Ret.end(), std::begin(ElfAArch64SectionFlags),
+               std::end(ElfAArch64SectionFlags));
+    break;
   case EM_ARM:
     Ret.insert(Ret.end(), std::begin(ElfARMSectionFlags),
                std::end(ElfARMSectionFlags));

@llvmbot
Copy link
Member

llvmbot commented Feb 4, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Csanád Hajdú (Il-Capitano)

Changes

Add support for the new SHF_AARCH64_PURECODE ELF section flag: ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets. Generating object files with the SHF_AARCH64_PURECODE flag set is enabled by the +execute-only target feature.


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

18 Files Affected:

  • (modified) llvm/include/llvm/BinaryFormat/ELF.h (+4-1)
  • (modified) llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp (+8-5)
  • (modified) llvm/lib/MC/MCParser/ELFAsmParser.cpp (+5-2)
  • (modified) llvm/lib/MC/MCSectionELF.cpp (+3)
  • (modified) llvm/lib/ObjectYAML/ELFYAML.cpp (+3)
  • (modified) llvm/lib/Target/AArch64/AArch64Features.td (+5)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp (+26)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.h (+6)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp (+16-1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h (+2)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp (+18)
  • (added) llvm/test/CodeGen/AArch64/execute-only-section.ll (+21)
  • (added) llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s (+27)
  • (added) llvm/test/MC/ELF/AArch64/execute-only-section.s (+43)
  • (added) llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s (+27)
  • (modified) llvm/test/MC/ELF/section-flags-unknown.s (+3)
  • (modified) llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll (+25)
  • (modified) llvm/tools/llvm-readobj/ELFDumper.cpp (+8)
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 8853c4a88b0b59..a5979d029a9eaf 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1300,7 +1300,10 @@ enum : unsigned {
   SHF_MIPS_STRING = 0x80000000,
 
   // Make code section unreadable when in execute-only mode
-  SHF_ARM_PURECODE = 0x20000000
+  SHF_ARM_PURECODE = 0x20000000,
+
+  // Section contains only program instructions and no program data.
+  SHF_AARCH64_PURECODE = 0x20000000
 };
 
 // Section Group Flags
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 3c2c7c8c9fed69..6cfebca9a4d576 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -547,7 +547,7 @@ static unsigned getELFSectionType(StringRef Name, SectionKind K) {
   return ELF::SHT_PROGBITS;
 }
 
-static unsigned getELFSectionFlags(SectionKind K) {
+static unsigned getELFSectionFlags(SectionKind K, const Triple &T) {
   unsigned Flags = 0;
 
   if (!K.isMetadata() && !K.isExclude())
@@ -559,9 +559,12 @@ static unsigned getELFSectionFlags(SectionKind K) {
   if (K.isText())
     Flags |= ELF::SHF_EXECINSTR;
 
-  if (K.isExecuteOnly())
+  if ((T.isARM() || T.isThumb()) && K.isExecuteOnly())
     Flags |= ELF::SHF_ARM_PURECODE;
 
+  if (T.isAArch64() && K.isExecuteOnly())
+    Flags |= ELF::SHF_AARCH64_PURECODE;
+
   if (K.isWriteable())
     Flags |= ELF::SHF_WRITE;
 
@@ -840,7 +843,7 @@ static MCSection *selectExplicitSectionGlobal(const GlobalObject *GO,
   // Infer section flags from the section name if we can.
   Kind = getELFKindForNamedSection(SectionName, Kind);
 
-  unsigned Flags = getELFSectionFlags(Kind);
+  unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
   auto [Group, IsComdat, ExtraFlags] = getGlobalObjectInfo(GO, TM);
   Flags |= ExtraFlags;
 
@@ -947,7 +950,7 @@ static MCSection *selectELFSectionForGlobal(
 
 MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
     const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
-  unsigned Flags = getELFSectionFlags(Kind);
+  unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
 
   // If we have -ffunction-section or -fdata-section then we should emit the
   // global value to a uniqued section specifically for it.
@@ -967,7 +970,7 @@ MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
 MCSection *TargetLoweringObjectFileELF::getUniqueSectionForFunction(
     const Function &F, const TargetMachine &TM) const {
   SectionKind Kind = SectionKind::getText();
-  unsigned Flags = getELFSectionFlags(Kind);
+  unsigned Flags = getELFSectionFlags(Kind, TM.getTargetTriple());
   // If the function's section names is pre-determined via pragma or a
   // section attribute, call selectExplicitSectionGlobal.
   if (F.hasSection())
diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
index b58210b3c268e9..99b13c68a9966c 100644
--- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp
@@ -328,9 +328,12 @@ static unsigned parseSectionFlags(const Triple &TT, StringRef flagsStr,
       flags |= ELF::XCORE_SHF_DP_SECTION;
       break;
     case 'y':
-      if (!(TT.isARM() || TT.isThumb()))
+      if (TT.isARM() || TT.isThumb())
+        flags |= ELF::SHF_ARM_PURECODE;
+      else if (TT.isAArch64())
+        flags |= ELF::SHF_AARCH64_PURECODE;
+      else
         return -1U;
-      flags |= ELF::SHF_ARM_PURECODE;
       break;
     case 's':
       if (TT.getArch() != Triple::hexagon)
diff --git a/llvm/lib/MC/MCSectionELF.cpp b/llvm/lib/MC/MCSectionELF.cpp
index 25e62b70b5e2a0..72a959b1c92082 100644
--- a/llvm/lib/MC/MCSectionELF.cpp
+++ b/llvm/lib/MC/MCSectionELF.cpp
@@ -118,6 +118,9 @@ void MCSectionELF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
   } else if (T.isARM() || T.isThumb()) {
     if (Flags & ELF::SHF_ARM_PURECODE)
       OS << 'y';
+  } else if (T.isAArch64()) {
+    if (Flags & ELF::SHF_AARCH64_PURECODE)
+      OS << 'y';
   } else if (Arch == Triple::hexagon) {
     if (Flags & ELF::SHF_HEX_GPREL)
       OS << 's';
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 539834fc8d4dbf..05e4d85b2ea5d2 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -803,6 +803,9 @@ void ScalarBitSetTraits<ELFYAML::ELF_SHF>::bitset(IO &IO,
     break;
   }
   switch (Object->getMachine()) {
+  case ELF::EM_AARCH64:
+    BCase(SHF_AARCH64_PURECODE);
+    break;
   case ELF::EM_ARM:
     BCase(SHF_ARM_PURECODE);
     break;
diff --git a/llvm/lib/Target/AArch64/AArch64Features.td b/llvm/lib/Target/AArch64/AArch64Features.td
index 20db70ee38572f..7c95ba662938d9 100644
--- a/llvm/lib/Target/AArch64/AArch64Features.td
+++ b/llvm/lib/Target/AArch64/AArch64Features.td
@@ -635,6 +635,11 @@ def FeatureStrictAlign : SubtargetFeature<"strict-align",
                                           "Disallow all unaligned memory "
                                           "access">;
 
+def FeatureExecuteOnly : SubtargetFeature<"execute-only",
+                                          "GenExecuteOnly", "true",
+                                          "Enable the generation of "
+                                          "execute only code.">;
+
 foreach i = {1-7,9-15,18,20-28} in
     def FeatureReserveX#i : SubtargetFeature<"reserve-x"#i, "ReserveXRegister["#i#"]", "true",
                                              "Reserve X"#i#", making it unavailable "
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
index 8729fd4b802c8e..434ae32502d484 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
@@ -148,3 +148,29 @@ MCSymbol *AArch64_MachoTargetObjectFile::getAuthPtrSlotSymbol(
   return getAuthPtrSlotSymbolHelper(getContext(), TM, MMI, MachOMMI, RawSym,
                                     Key, Discriminator);
 }
+
+static bool isExecuteOnlyFunction(const GlobalObject *GO, SectionKind Kind,
+                                  const TargetMachine &TM) {
+  if (const Function *F = dyn_cast<Function>(GO))
+    if (TM.getSubtarget<AArch64Subtarget>(*F).genExecuteOnly() && Kind.isText())
+      return true;
+  return false;
+}
+
+MCSection *AArch64_ELFTargetObjectFile::getExplicitSectionGlobal(
+    const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
+  // Set execute-only access for the explicit section
+  if (isExecuteOnlyFunction(GO, Kind, TM))
+    Kind = SectionKind::getExecuteOnly();
+
+  return TargetLoweringObjectFileELF::getExplicitSectionGlobal(GO, Kind, TM);
+}
+
+MCSection *AArch64_ELFTargetObjectFile::SelectSectionForGlobal(
+    const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
+  // Set execute-only access for the explicit section
+  if (isExecuteOnlyFunction(GO, Kind, TM))
+    Kind = SectionKind::getExecuteOnly();
+
+  return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
+}
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
index 0c822ac84f200c..3e9cd51b742a29 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
@@ -39,6 +39,12 @@ class AArch64_ELFTargetObjectFile : public TargetLoweringObjectFileELF {
   void emitPersonalityValueImpl(MCStreamer &Streamer, const DataLayout &DL,
                                 const MCSymbol *Sym,
                                 const MachineModuleInfo *MMI) const override;
+
+  MCSection *getExplicitSectionGlobal(const GlobalObject *GO, SectionKind Kind,
+                                      const TargetMachine &TM) const override;
+
+  MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
+                                    const TargetMachine &TM) const override;
 };
 
 /// AArch64_MachoTargetObjectFile - This TLOF implementation is used for Darwin.
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index a6edcf125782b2..284278e34f8b4d 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -262,6 +262,10 @@ bool AArch64TTIImpl::isMultiversionedFunction(const Function &F) const {
   return F.hasFnAttribute("fmv-features");
 }
 
+const FeatureBitset AArch64TTIImpl::InlineInverseFeatures = {
+    AArch64::FeatureExecuteOnly,
+};
+
 bool AArch64TTIImpl::areInlineCompatible(const Function *Caller,
                                          const Function *Callee) const {
   SMEAttrs CallerAttrs(*Caller), CalleeAttrs(*Callee);
@@ -284,7 +288,18 @@ bool AArch64TTIImpl::areInlineCompatible(const Function *Caller,
       return false;
   }
 
-  return BaseT::areInlineCompatible(Caller, Callee);
+  const TargetMachine &TM = getTLI()->getTargetMachine();
+  const FeatureBitset &CallerBits =
+      TM.getSubtargetImpl(*Caller)->getFeatureBits();
+  const FeatureBitset &CalleeBits =
+      TM.getSubtargetImpl(*Callee)->getFeatureBits();
+  // Adjust the feature bitsets by inverting some of the bits. This is needed
+  // for target features that represent restrictions rather than capabilities,
+  // for example "+execute-only".
+  FeatureBitset EffectiveCallerBits = CallerBits ^ InlineInverseFeatures;
+  FeatureBitset EffectiveCalleeBits = CalleeBits ^ InlineInverseFeatures;
+
+  return (EffectiveCallerBits & EffectiveCalleeBits) == EffectiveCalleeBits;
 }
 
 bool AArch64TTIImpl::areTypesABICompatible(
diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
index b65e3c7a1ab20e..481cb5511a331b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
@@ -48,6 +48,8 @@ class AArch64TTIImpl : public BasicTTIImplBase<AArch64TTIImpl> {
   const AArch64Subtarget *ST;
   const AArch64TargetLowering *TLI;
 
+  static const FeatureBitset InlineInverseFeatures;
+
   const AArch64Subtarget *getST() const { return ST; }
   const AArch64TargetLowering *getTLI() const { return TLI; }
 
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
index 6b5c5f36cbd4b2..d29d383bc23127 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
@@ -27,6 +27,7 @@
 #include "llvm/MC/MCELFStreamer.h"
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCObjectFileInfo.h"
 #include "llvm/MC/MCSectionELF.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/MC/MCSubtargetInfo.h"
@@ -504,6 +505,23 @@ void AArch64TargetELFStreamer::finish() {
     }
   }
 
+  // The mix of execute-only and non-execute-only at link time is
+  // non-execute-only. To avoid the empty implicitly created .text
+  // section from making the whole .text section non-execute-only, we
+  // mark it execute-only if it is empty and there is at least one
+  // execute-only section in the object.
+  if (any_of(Asm, [](const MCSection &Sec) {
+        return cast<MCSectionELF>(Sec).getFlags() & ELF::SHF_AARCH64_PURECODE;
+      })) {
+    auto *Text =
+        static_cast<MCSectionELF *>(Ctx.getObjectFileInfo()->getTextSection());
+    for (auto &F : *Text)
+      if (auto *DF = dyn_cast<MCDataFragment>(&F))
+        if (!DF->getContents().empty())
+          return;
+    Text->setFlags(Text->getFlags() | ELF::SHF_AARCH64_PURECODE);
+  }
+
   MCSectionELF *MemtagSec = nullptr;
   for (const MCSymbol &Symbol : Asm.symbols()) {
     const auto &Sym = cast<MCSymbolELF>(Symbol);
diff --git a/llvm/test/CodeGen/AArch64/execute-only-section.ll b/llvm/test/CodeGen/AArch64/execute-only-section.ll
new file mode 100644
index 00000000000000..15a270b07975e6
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/execute-only-section.ll
@@ -0,0 +1,21 @@
+; RUN: llc -mtriple=aarch64 -mattr=+execute-only %s -o - | FileCheck %s
+
+; CHECK:     .section .text,"axy",@progbits,unique,0
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK:     .globl test_SectionForGlobal
+; CHECK:     .type test_SectionForGlobal,@function
+define void @test_SectionForGlobal() {
+entry:
+  ret void
+}
+
+; CHECK:     .section .test,"axy",@progbits
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK:     .globl test_ExplicitSectionForGlobal
+; CHECK:     .type test_ExplicitSectionForGlobal,@function
+define void @test_ExplicitSectionForGlobal() section ".test" {
+entry:
+  ret void
+}
diff --git a/llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s b/llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s
new file mode 100644
index 00000000000000..b0ba8a18d52b38
--- /dev/null
+++ b/llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s
@@ -0,0 +1,27 @@
+// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
+// RUN: | llvm-readobj -S --symbols - | FileCheck %s
+
+        .text
+        ret
+
+        .section        .text.foo,"axy"
+        ret
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x6)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
+
+// CHECK:      Section {
+// CHECK:        Name: .text.foo
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
diff --git a/llvm/test/MC/ELF/AArch64/execute-only-section.s b/llvm/test/MC/ELF/AArch64/execute-only-section.s
new file mode 100644
index 00000000000000..6fd596376711d6
--- /dev/null
+++ b/llvm/test/MC/ELF/AArch64/execute-only-section.s
@@ -0,0 +1,43 @@
+// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
+// RUN: | llvm-readobj -S --symbols - | FileCheck %s
+
+        .section        .text,"axy",@progbits,unique,0
+        .globl  foo
+        .p2align  2
+        .type   foo,@function
+foo:
+        .cfi_startproc
+        ret
+.Lfunc_end0:
+        .size   foo, .Lfunc_end0-foo
+        .cfi_endproc
+
+        .section        ".note.GNU-stack","",@progbits
+
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:        Size: 0
+// CHECK:      }
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:        Size: 2
+// CHECK:      }
+
+// CHECK: Symbol {
+// CHECK:   Name: foo
+// CHECK:   Section: .text (0x3)
+// CHECK: }
diff --git a/llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s b/llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s
new file mode 100644
index 00000000000000..22bcffae67d0eb
--- /dev/null
+++ b/llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s
@@ -0,0 +1,27 @@
+// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux-gnu %s -o - \
+// RUN: | llvm-readobj -S --symbols - | FileCheck %s
+
+        .text
+        .ascii "test"
+
+        .section        .text.foo,"axy"
+        ret
+
+// CHECK:      Section {
+// CHECK:        Name: .text
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x6)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
+
+// CHECK:      Section {
+// CHECK:        Name: .text.foo
+// CHECK-NEXT:   Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT:   Flags [ (0x20000006)
+// CHECK-NEXT:     SHF_AARCH64_PURECODE (0x20000000)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_EXECINSTR (0x4)
+// CHECK-NEXT:   ]
+// CHECK:      }
diff --git a/llvm/test/MC/ELF/section-flags-unknown.s b/llvm/test/MC/ELF/section-flags-unknown.s
index 90c9185e8bfb10..b172403e34556c 100644
--- a/llvm/test/MC/ELF/section-flags-unknown.s
+++ b/llvm/test/MC/ELF/section-flags-unknown.s
@@ -14,5 +14,8 @@
 # CHECK: {{.*}}.s:[[# @LINE+1]]:30: error: unknown flag
 .section SHF_ARM_PURECODE,"y",@progbits
 
+# CHECK: {{.*}}.s:[[# @LINE+1]]:34: error: unknown flag
+.section SHF_AARCH64_PURECODE,"y",@progbits
+
 # CHECK: {{.*}}.s:[[# @LINE+1]]:30: error: unknown flag
 .section SHF_X86_64_LARGE,"l",@progbits
diff --git a/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll b/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll
index 636f3c6600f8d4..433a9c7bdd23b0 100644
--- a/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll
+++ b/llvm/test/Transforms/Inline/AArch64/inline-target-attr.ll
@@ -27,6 +27,14 @@ entry:
 ; CHECK: call i32 @bar()
 }
 
+define i32 @quux() #3 {
+entry:
+  %call = call i32 (...) @baz()
+  ret i32 %call
+; CHECK-LABEL: quux
+; CHECK: call i32 (...) @baz()
+}
+
 define i32 @strict_align() #2 {
 entry:
   %call = call i32 @foo()
@@ -35,6 +43,23 @@ entry:
 ; CHECK: call i32 (...) @baz()
 }
 
+define i32 @execute_only1() #3 {
+entry:
+  %call = call i32 @foo()
+  ret i32 %call
+; CHECK-LABEL: execute_only1
+; CHECK: call i32 @foo()
+}
+
+define i32 @execute_only2() #0 {
+entry:
+  %call = call i32 @quux()
+  ret i32 %call
+; CHECK-LABEL: execute_only2
+; CHECK: call i32 (...) @baz()
+}
+
 attributes #0 = { "target-cpu"="generic" "target-features"="+crc,+neon" }
 attributes #1 = { "target-cpu"="generic" "target-features"="+crc,+neon,+crypto" }
 attributes #2 = { "target-cpu"="generic" "target-features"="+crc,+neon,+strict-align" }
+attributes #3 = { "target-cpu"="generic" "target-features"="+crc,+neon,+execute-only" }
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index bfca65aad52b44..78aaf79a61c468 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -1337,6 +1337,10 @@ const EnumEntry<unsigned> ElfXCoreSectionFlags[] = {
   ENUM_ENT(XCORE_SHF_DP_SECTION, "")
 };
 
+const EnumEntry<unsigned> ElfAArch64SectionFlags[] = {
+    ENUM_ENT(SHF_AARCH64_PURECODE, "y"),
+};
+
 const EnumEntry<unsigned> ElfARMSectionFlags[] = {
   ENUM_ENT(SHF_ARM_PURECODE, "y")
 };
@@ -1375,6 +1379,10 @@ getSectionFlagsForTarget(unsigned EOSAbi, unsigned EMachine) {
     break;
   }
   switch (EMachine) {
+  case EM_AARCH64:
+    Ret.insert(Ret.end(), std::begin(ElfAArch64SectionFlags),
+               std::end(ElfAArch64SectionFlags));
+    break;
   case EM_ARM:
     Ret.insert(Ret.end(), std::begin(ElfARMSectionFlags),
                std::end(ElfARMSectionFlags));

@Il-Capitano
Copy link
Contributor Author

CC @DanielKristofKiss

Copy link
Collaborator

@paulwalker-arm paulwalker-arm left a comment

Choose a reason for hiding this comment

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

Other than a minor formatting request the PR looks good to me, being consistent with the ARM/THUMB implementation.

That said, this is not my area so please given other reviewers (I'm tagging in @smithp35 who has more experience in this area) a day or so to chip in before merging the PR.

Copy link

github-actions bot commented Feb 12, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 83ff9d4a34b1e579dd809759d13b70b8837f0cde bcb5275221591da01303f6f2e368be4d3679ce0e --extensions h,cpp -- llvm/include/llvm/BinaryFormat/ELF.h llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp llvm/lib/MC/MCParser/ELFAsmParser.cpp llvm/lib/MC/MCSectionELF.cpp llvm/lib/ObjectYAML/ELFYAML.cpp llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp llvm/lib/Target/AArch64/AArch64TargetObjectFile.h llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp llvm/tools/llvm-readobj/ELFDumper.cpp
View the diff from clang-format here.
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index a484a897b8..f569dcf2c6 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -1338,8 +1338,7 @@ const EnumEntry<unsigned> ElfXCoreSectionFlags[] = {
 };
 
 const EnumEntry<unsigned> ElfAArch64SectionFlags[] = {
-  ENUM_ENT(SHF_AARCH64_PURECODE, "y")
-};
+    ENUM_ENT(SHF_AARCH64_PURECODE, "y")};
 
 const EnumEntry<unsigned> ElfARMSectionFlags[] = {
   ENUM_ENT(SHF_ARM_PURECODE, "y")

@smithp35
Copy link
Collaborator

No objections from me. I've left a few small comments, but nothing significant. I've checked the implementation against the existing ARM backend.

@Il-Capitano
Copy link
Contributor Author

@smithp35 I can't see any comments made by you. Are you sure you saved/published them?

@smithp35
Copy link
Collaborator

@smithp35 I can't see any comments made by you. Are you sure you saved/published them?

Thanks for pointing it out. Looks like I've got a few pending.

Copy link
Collaborator

@smithp35 smithp35 left a comment

Choose a reason for hiding this comment

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

Publish pending comments!

Copy link
Collaborator

@jh7370 jh7370 left a comment

Choose a reason for hiding this comment

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

Tool code and tests LGTM. I haven't reviewed other aspects.

I think this is ready to be merged. Do you have commit access?

@Il-Capitano
Copy link
Contributor Author

Thanks for the review!

I think this is ready to be merged. Do you have commit access?

No, I don't.

@jh7370
Copy link
Collaborator

jh7370 commented Feb 13, 2025

Thanks for the review!

I think this is ready to be merged. Do you have commit access?

No, I don't.

Okay, I'll give this a day before merging, to give other reviewers a final chance to look it over.

@jh7370 jh7370 merged commit a190f15 into llvm:main Feb 14, 2025
7 of 8 checks passed
joaosaffran pushed a commit to joaosaffran/llvm-project that referenced this pull request Feb 14, 2025
…llvm#125687)

Add support for the new SHF_AARCH64_PURECODE ELF section flag:
ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets.
Generating object files with the `SHF_AARCH64_PURECODE` flag set is
enabled by the `+execute-only` target feature.

Related PRs:
* Clang: llvm#125688
* LLD: llvm#125689
@Il-Capitano Il-Capitano deleted the aarch64-execute-only-llvm branch February 18, 2025 15:19
MaskRay pushed a commit that referenced this pull request Feb 21, 2025
…flag (3/3) (#125689)

Add support for the new SHF_AARCH64_PURECODE ELF section flag:
ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets. The
output section only has the `SHF_AARCH64_PURECODE` flag set if all input
sections have it set.

Related PRs:
* LLVM: #125687
* Clang: #125688
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Feb 21, 2025
…LF section flag (3/3) (#125689)

Add support for the new SHF_AARCH64_PURECODE ELF section flag:
ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets. The
output section only has the `SHF_AARCH64_PURECODE` flag set if all input
sections have it set.

Related PRs:
* LLVM: llvm/llvm-project#125687
* Clang: llvm/llvm-project#125688
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Feb 24, 2025
…llvm#125687)

Add support for the new SHF_AARCH64_PURECODE ELF section flag:
ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets.
Generating object files with the `SHF_AARCH64_PURECODE` flag set is
enabled by the `+execute-only` target feature.

Related PRs:
* Clang: llvm#125688
* LLD: llvm#125689
davemgreen pushed a commit that referenced this pull request Mar 10, 2025
…g (2/3) (#125688)

Add support for the new SHF_AARCH64_PURECODE ELF section flag:
ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets.
Simlarly to ARM targets, generating object files with the
`SHF_AARCH64_PURECODE` flag set is enabled by the
`-mexecute-only`/`-mpure-code` driver flag.

Related PRs:
* LLVM: #125687
* LLD: #125689
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Mar 10, 2025
…section flag (2/3) (#125688)

Add support for the new SHF_AARCH64_PURECODE ELF section flag:
ARM-software/abi-aa#304

The general implementation follows the existing one for ARM targets.
Simlarly to ARM targets, generating object files with the
`SHF_AARCH64_PURECODE` flag set is enabled by the
`-mexecute-only`/`-mpure-code` driver flag.

Related PRs:
* LLVM: llvm/llvm-project#125687
* LLD: llvm/llvm-project#125689
Il-Capitano added a commit to Il-Capitano/llvm-project that referenced this pull request Mar 26, 2025
Support for execute-only code generation (llvm#125687) introduced a bug in
the case where the memtag sanitizer is used in a module containing a mix
of execute-only and non-execute-only functions.

The bug is caused by using `return` instead of `break` to short-circuit
a loop, which meant that the rest of the function dealing with memtag
sanitizer logic wasn't run.
Il-Capitano added a commit that referenced this pull request Apr 1, 2025
#133084)

Support for execute-only code generation (#125687) introduced a bug in
the case where the memtag sanitizer is used in a module containing a mix
of execute-only and non-execute-only functions.

The bug is caused by using `return` instead of `break` to short-circuit
a loop, which meant that the rest of the function dealing with memtag
sanitizer logic wasn't run.
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request Apr 2, 2025
llvm#133084)

Support for execute-only code generation (llvm#125687) introduced a bug in
the case where the memtag sanitizer is used in a module containing a mix
of execute-only and non-execute-only functions.

The bug is caused by using `return` instead of `break` to short-circuit
a loop, which meant that the rest of the function dealing with memtag
sanitizer logic wasn't run.
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.

5 participants