[clang] [llvm] [Clang][AArch64] Add support for SHF_AARCH64_PURECODE ELF section flag (2/3) (PR #125688)

Csanád Hajdú via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 4 06:13:52 PST 2025


https://github.com/Il-Capitano created https://github.com/llvm/llvm-project/pull/125688

Add support for the new SHF_AARCH64_PURECODE ELF section flag: https://github.com/ARM-software/abi-aa/pull/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.

Depends on https://github.com/llvm/llvm-project/pull/125687. The changes in that PR are included here as well for now, but will be removed once that gets merged.

>From 6ff1cc4abb39b0526cae22497b5147cae35a7d6f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Csan=C3=A1d=20Hajd=C3=BA?= <csanad.hajdu at arm.com>
Date: Tue, 4 Feb 2025 14:32:58 +0100
Subject: [PATCH 1/2] [AArch64] Add support for SHF_AARCH64_PURECODE ELF
 section flag (1/3)

Add support for the new SHF_AARCH64_PURECODE ELF section flag: https://github.com/ARM-software/abi-aa/pull/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.
---
 llvm/include/llvm/BinaryFormat/ELF.h          |  5 ++-
 .../CodeGen/TargetLoweringObjectFileImpl.cpp  | 13 +++---
 llvm/lib/MC/MCParser/ELFAsmParser.cpp         |  7 ++-
 llvm/lib/MC/MCSectionELF.cpp                  |  3 ++
 llvm/lib/ObjectYAML/ELFYAML.cpp               |  3 ++
 llvm/lib/Target/AArch64/AArch64Features.td    |  5 +++
 .../AArch64/AArch64TargetObjectFile.cpp       | 26 +++++++++++
 .../Target/AArch64/AArch64TargetObjectFile.h  |  6 +++
 .../AArch64/AArch64TargetTransformInfo.cpp    | 17 +++++++-
 .../AArch64/AArch64TargetTransformInfo.h      |  2 +
 .../MCTargetDesc/AArch64ELFStreamer.cpp       | 18 ++++++++
 .../CodeGen/AArch64/execute-only-section.ll   | 21 +++++++++
 .../execute-only-populated-text-section.s     | 27 ++++++++++++
 .../MC/ELF/AArch64/execute-only-section.s     | 43 +++++++++++++++++++
 .../AArch64/execute-only-text-section-data.s  | 27 ++++++++++++
 llvm/test/MC/ELF/section-flags-unknown.s      |  3 ++
 .../Inline/AArch64/inline-target-attr.ll      | 25 +++++++++++
 llvm/tools/llvm-readobj/ELFDumper.cpp         |  8 ++++
 18 files changed, 250 insertions(+), 9 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/execute-only-section.ll
 create mode 100644 llvm/test/MC/ELF/AArch64/execute-only-populated-text-section.s
 create mode 100644 llvm/test/MC/ELF/AArch64/execute-only-section.s
 create mode 100644 llvm/test/MC/ELF/AArch64/execute-only-text-section-data.s

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", at progbits,unique,0
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK:     .globl test_SectionForGlobal
+; CHECK:     .type test_SectionForGlobal, at function
+define void @test_SectionForGlobal() {
+entry:
+  ret void
+}
+
+; CHECK:     .section .test,"axy", at progbits
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK:     .globl test_ExplicitSectionForGlobal
+; CHECK:     .type test_ExplicitSectionForGlobal, at 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", at progbits,unique,0
+        .globl  foo
+        .p2align  2
+        .type   foo, at function
+foo:
+        .cfi_startproc
+        ret
+.Lfunc_end0:
+        .size   foo, .Lfunc_end0-foo
+        .cfi_endproc
+
+        .section        ".note.GNU-stack","", at 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", at progbits
 
+# CHECK: {{.*}}.s:[[# @LINE+1]]:34: error: unknown flag
+.section SHF_AARCH64_PURECODE,"y", at progbits
+
 # CHECK: {{.*}}.s:[[# @LINE+1]]:30: error: unknown flag
 .section SHF_X86_64_LARGE,"l", at 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));

>From fbb376b768fa617085629266f2fb685a51173670 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Csan=C3=A1d=20Hajd=C3=BA?= <csanad.hajdu at arm.com>
Date: Tue, 4 Feb 2025 14:51:55 +0100
Subject: [PATCH 2/2] [Clang][AArch64] Add support for SHF_AARCH64_PURECODE ELF
 section flag (2/3)

Add support for the new SHF_AARCH64_PURECODE ELF section flag: https://github.com/ARM-software/abi-aa/pull/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.
---
 clang/include/clang/Driver/Options.td        |  4 ++--
 clang/lib/Driver/ToolChains/Arch/AArch64.cpp | 11 +++++++++
 clang/test/CodeGen/aarch64-execute-only.c    | 24 ++++++++++++++++++++
 clang/test/Driver/aarch64-execute-only.c     |  6 +++++
 clang/test/Driver/fsanitize.c                |  8 ++++++-
 5 files changed, 50 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGen/aarch64-execute-only.c
 create mode 100644 clang/test/Driver/aarch64-execute-only.c

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 0ab923fcdd5838..028d74c9711d1f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4677,9 +4677,9 @@ def mno_long_calls : Flag<["-"], "mno-long-calls">, Group<m_Group>,
   HelpText<"Restore the default behaviour of not generating long calls">;
 } // let Flags = [TargetSpecific]
 def mexecute_only : Flag<["-"], "mexecute-only">, Group<m_arm_Features_Group>,
-  HelpText<"Disallow generation of data access to code sections (ARM only)">;
+  HelpText<"Disallow generation of data access to code sections (AArch64/ARM only)">;
 def mno_execute_only : Flag<["-"], "mno-execute-only">, Group<m_arm_Features_Group>,
-  HelpText<"Allow generation of data access to code sections (ARM only)">;
+  HelpText<"Allow generation of data access to code sections (AArch64/ARM only)">;
 let Flags = [TargetSpecific] in {
 def mtp_mode_EQ : Joined<["-"], "mtp=">, Group<m_arm_Features_Group>, Values<"soft,cp15,tpidrurw,tpidruro,tpidrprw,el0,el1,el2,el3,tpidr_el0,tpidr_el1,tpidr_el2,tpidr_el3,tpidrro_el0">,
   HelpText<"Thread pointer access method. "
diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
index 1e2ac4e501bafd..1248fea50f9de2 100644
--- a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
@@ -332,6 +332,17 @@ void aarch64::getAArch64TargetFeatures(const Driver &D,
   } else if (Triple.isOSOpenBSD())
     Features.push_back("+strict-align");
 
+  // Generate execute-only output (no data access to code sections).
+  // This only makes sense for the compiler, not for the assembler.
+  if (!ForAS) {
+    if (Arg *A = Args.getLastArg(options::OPT_mexecute_only,
+                                 options::OPT_mno_execute_only)) {
+      if (A->getOption().matches(options::OPT_mexecute_only)) {
+        Features.push_back("+execute-only");
+      }
+    }
+  }
+
   if (Args.hasArg(options::OPT_ffixed_x1))
     Features.push_back("+reserve-x1");
 
diff --git a/clang/test/CodeGen/aarch64-execute-only.c b/clang/test/CodeGen/aarch64-execute-only.c
new file mode 100644
index 00000000000000..d885e954166f67
--- /dev/null
+++ b/clang/test/CodeGen/aarch64-execute-only.c
@@ -0,0 +1,24 @@
+// RUN: %clang -target aarch64 -### %s 2>&1 \
+// RUN:    | FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY
+
+// RUN: %clang -target aarch64 -### -mexecute-only %s 2>&1 \
+// RUN:    | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY
+
+// RUN: %clang -target aarch64 -### -mexecute-only -mno-execute-only %s 2>&1 \
+// RUN:    | FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY
+
+
+// -mpure-code flag for GCC compatibility
+// RUN: %clang -target aarch64 -### %s 2>&1 \
+// RUN:    | FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY
+
+// RUN: %clang -target aarch64 -### -mpure-code %s 2>&1 \
+// RUN:    | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY
+
+// RUN: %clang -target aarch64 -### -mpure-code -mno-pure-code %s 2>&1 \
+// RUN:    | FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY
+
+// CHECK-NO-EXECUTE-ONLY-NOT: "+execute-only"
+// CHECK-EXECUTE-ONLY: "+execute-only"
+
+void a() {}
diff --git a/clang/test/Driver/aarch64-execute-only.c b/clang/test/Driver/aarch64-execute-only.c
new file mode 100644
index 00000000000000..67c6d4a5eb0669
--- /dev/null
+++ b/clang/test/Driver/aarch64-execute-only.c
@@ -0,0 +1,6 @@
+// RUN: %clang --sysroot=%S/Inputs -c -fdriver-only -Werror --target=aarch64-unknown-linux-gnu \
+// RUN:   -mexecute-only %s 2>&1 | count 0
+
+// RUN: %clang -### --target=aarch64-unknown-linux-gnu -x assembler -mexecute-only %s -c -### 2>&1 \
+// RUN:    | FileCheck %s --check-prefix=CHECK-NO-EXECUTE-ONLY-ASM
+// CHECK-NO-EXECUTE-ONLY-ASM: warning: argument unused during compilation: '-mexecute-only'
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index 429dc51b3356d6..cb3b75cbd27a7b 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -1052,7 +1052,6 @@
 // RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-TARGET
 // RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=function -fsanitize=kcfi %s -### 2>&1 | FileCheck %s  --check-prefix=CHECK-UBSAN-KCFI-TARGET --check-prefix=CHECK-UBSAN-FUNCTION-TARGET
 // RUN: %clang --target=x86_64-sie-ps5 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED
-// CHECK-UBSAN-UNDEFINED: "-fsanitize={{((alignment|array-bounds|bool|builtin|enum|float-cast-overflow|integer-divide-by-zero|nonnull-attribute|null|pointer-overflow|return|returns-nonnull-attribute|shift-base|shift-exponent|signed-integer-overflow|unreachable|vla-bound),?){17}"}}
 
 // RUN: not %clang --target=armv6t2-eabi -mexecute-only -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MEXECUTE-ONLY
 // RUN: not %clang --target=armv6t2-eabi -mpure-code -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MPURE-CODE
@@ -1060,12 +1059,19 @@
 // RUN: not %clang --target=armv6t2-eabi -mpure-code -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-MPURE-CODE
 // RUN: %clang --target=armv6t2-eabi -mexecute-only -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED-VPTR
 
+// RUN: not %clang --target=aarch64 -mexecute-only -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MEXECUTE-ONLY
+// RUN: not %clang --target=aarch64 -mpure-code -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MPURE-CODE
+// RUN: not %clang --target=aarch64 -mexecute-only -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-MEXECUTE-ONLY
+// RUN: not %clang --target=aarch64 -mpure-code -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI-MPURE-CODE
+// RUN: %clang --target=aarch64 -mexecute-only -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED
+
 // CHECK-UBSAN-KCFI-TARGET-DAG: error: unsupported option '-fsanitize=kcfi' for target 'x86_64-sie-ps5'
 // CHECK-UBSAN-KCFI-MEXECUTE-ONLY-DAG: error: invalid argument '-fsanitize=kcfi' not allowed with '-mexecute-only'
 // CHECK-UBSAN-KCFI-MPURE-CODE-DAG: error: invalid argument '-fsanitize=kcfi' not allowed with '-mpure-code'
 // CHECK-UBSAN-FUNCTION-TARGET-DAG: error: unsupported option '-fsanitize=function' for target 'x86_64-sie-ps5'
 // CHECK-UBSAN-FUNCTION-MEXECUTE-ONLY-DAG: error: invalid argument '-fsanitize=function' not allowed with '-mexecute-only'
 // CHECK-UBSAN-FUNCTION-MPURE-CODE-DAG: error: invalid argument '-fsanitize=function' not allowed with '-mpure-code'
+// CHECK-UBSAN-UNDEFINED: "-fsanitize={{((alignment|array-bounds|bool|builtin|enum|float-cast-overflow|integer-divide-by-zero|nonnull-attribute|null|pointer-overflow|return|returns-nonnull-attribute|shift-base|shift-exponent|signed-integer-overflow|unreachable|vla-bound),?){17}"}}
 // CHECK-UBSAN-UNDEFINED-VPTR: "-fsanitize={{((alignment|array-bounds|bool|builtin|enum|float-cast-overflow|integer-divide-by-zero|nonnull-attribute|null|pointer-overflow|return|returns-nonnull-attribute|shift-base|shift-exponent|signed-integer-overflow|unreachable|vla-bound|vptr),?){18}"}}
 
 // * Test BareMetal toolchain sanitizer support *



More information about the cfe-commits mailing list