[lld] [LLD][ELF][AArch64] Add support for SHF_AARCH64_PURECODE ELF section flag (3/3) (PR #125689)

Csanád Hajdú via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 14 02:31:39 PST 2025


https://github.com/Il-Capitano updated https://github.com/llvm/llvm-project/pull/125689

>From 4328cdb38e6c5adfdecc2e686d68aebfd26d6de4 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:55:22 +0100
Subject: [PATCH 1/6] [LLD][ELF][AArch64] Add support for SHF_AARCH64_PURECODE
 ELF section flag (3/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. The
output section only has the `SHF_AARCH64_PURECODE` flag set if all input
sections have it set.
---
 lld/ELF/Config.h                    |  1 +
 lld/ELF/Driver.cpp                  | 67 +++++++++++++++++++++++++++--
 lld/ELF/OutputSections.cpp          | 10 +++--
 lld/ELF/ScriptParser.cpp            |  1 +
 lld/test/ELF/aarch64-execute-only.s | 33 ++++++++++++++
 5 files changed, 105 insertions(+), 7 deletions(-)
 create mode 100644 lld/test/ELF/aarch64-execute-only.s

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index f132b11b20c63..b5872b85efd3a 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -229,6 +229,7 @@ struct Config {
   StringRef zCetReport = "none";
   StringRef zPauthReport = "none";
   StringRef zGcsReport = "none";
+  StringRef zExecuteOnlyReport = "none";
   bool ltoBBAddrMap;
   llvm::StringRef ltoBasicBlockSections;
   std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 7d14180a49926..90b6dc0c75036 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -461,6 +461,27 @@ static void checkOptions(Ctx &ctx) {
 
   if (ctx.arg.zRetpolineplt && ctx.arg.zForceIbt)
     ErrAlways(ctx) << "-z force-ibt may not be used with -z retpolineplt";
+
+  if (ctx.arg.emachine != EM_AARCH64) {
+    if (ctx.arg.zPacPlt)
+      ErrAlways(ctx) << "-z pac-plt only supported on AArch64";
+    if (ctx.arg.zForceBti)
+      ErrAlways(ctx) << "-z force-bti only supported on AArch64";
+    if (ctx.arg.zBtiReport != "none")
+      ErrAlways(ctx) << "-z bti-report only supported on AArch64";
+    if (ctx.arg.zPauthReport != "none")
+      ErrAlways(ctx) << "-z pauth-report only supported on AArch64";
+    if (ctx.arg.zGcsReport != "none")
+      ErrAlways(ctx) << "-z gcs-report only supported on AArch64";
+    if (ctx.arg.zGcs != GcsPolicy::Implicit)
+      ErrAlways(ctx) << "-z gcs only supported on AArch64";
+    if (ctx.arg.zExecuteOnlyReport != "none")
+      ErrAlways(ctx) << "-z execute-only-report only supported on AArch64";
+  }
+
+  if (ctx.arg.emachine != EM_386 && ctx.arg.emachine != EM_X86_64 &&
+      ctx.arg.zCetReport != "none")
+    ErrAlways(ctx) << "-z cet-report only supported on X86 and X86_64";
 }
 
 static const char *getReproduceOption(opt::InputArgList &args) {
@@ -1620,10 +1641,12 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
       ErrAlways(ctx) << errPrefix << pat.takeError() << ": " << kv.first;
   }
 
-  auto reports = {std::make_pair("bti-report", &ctx.arg.zBtiReport),
-                  std::make_pair("cet-report", &ctx.arg.zCetReport),
-                  std::make_pair("gcs-report", &ctx.arg.zGcsReport),
-                  std::make_pair("pauth-report", &ctx.arg.zPauthReport)};
+  auto reports = {
+      std::make_pair("bti-report", &ctx.arg.zBtiReport),
+      std::make_pair("cet-report", &ctx.arg.zCetReport),
+      std::make_pair("gcs-report", &ctx.arg.zGcsReport),
+      std::make_pair("pauth-report", &ctx.arg.zPauthReport),
+      std::make_pair("execute-only-report", &ctx.arg.zExecuteOnlyReport)};
   for (opt::Arg *arg : args.filtered(OPT_z)) {
     std::pair<StringRef, StringRef> option =
         StringRef(arg->getValue()).split('=');
@@ -2907,6 +2930,40 @@ static void readSecurityNotes(Ctx &ctx) {
     ctx.arg.andFeatures &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
 }
 
+static void checkExecuteOnly(Ctx &ctx) {
+  if (ctx.arg.emachine != EM_AARCH64)
+    return;
+
+  auto report = [&](StringRef config) -> ELFSyncStream {
+    if (config == "error")
+      return {ctx, DiagLevel::Err};
+    if (config == "warning")
+      return {ctx, DiagLevel::Warn};
+    return {ctx, DiagLevel::None};
+  };
+  auto reportUnless = [&](StringRef config, bool cond) -> ELFSyncStream {
+    if (cond)
+      return {ctx, DiagLevel::None};
+    return report(config);
+  };
+
+  for (ELFFileBase *file : ctx.objectFiles) {
+    for (InputSectionBase *section : file->getSections()) {
+      // Only check for executable sections.
+      if (!(section && section->flags & SHF_EXECINSTR))
+        continue;
+
+      OutputSection *outputSection = section->getOutputSection();
+      if (outputSection && outputSection->name == ".text") {
+        reportUnless(ctx.arg.zExecuteOnlyReport,
+                     section->flags & SHF_AARCH64_PURECODE)
+            << file << ": -z execute-only-report: section " << section->name
+            << " does not have SHF_AARCH64_PURECODE flag set";
+      }
+    }
+  }
+}
+
 static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
   switch (file->ekind) {
   case ELF32LEKind:
@@ -3272,6 +3329,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
     ctx.script->addOrphanSections();
   }
 
+  checkExecuteOnly(ctx);
+
   {
     llvm::TimeTraceScope timeScope("Merge/finalize input sections");
 
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index a2da5543d5867..c8dc0e5fe0335 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -42,7 +42,8 @@ using namespace lld::elf;
 
 uint32_t OutputSection::getPhdrFlags() const {
   uint32_t ret = 0;
-  if (ctx.arg.emachine != EM_ARM || !(flags & SHF_ARM_PURECODE))
+  if ((ctx.arg.emachine != EM_ARM || !(flags & SHF_ARM_PURECODE)) &&
+      (ctx.arg.emachine != EM_AARCH64 || !(flags & SHF_AARCH64_PURECODE)))
     ret |= PF_R;
   if (flags & SHF_WRITE)
     ret |= PF_W;
@@ -161,8 +162,11 @@ void OutputSection::commitSection(InputSection *isec) {
   }
 
   isec->parent = this;
-  uint64_t andMask =
-      ctx.arg.emachine == EM_ARM ? (uint64_t)SHF_ARM_PURECODE : 0;
+  uint64_t andMask = 0;
+  if (ctx.arg.emachine == EM_ARM)
+    andMask |= (uint64_t)SHF_ARM_PURECODE;
+  if (ctx.arg.emachine == EM_AARCH64)
+    andMask |= (uint64_t)SHF_AARCH64_PURECODE;
   uint64_t orMask = ~andMask;
   uint64_t andFlags = (flags & isec->flags) & andMask;
   uint64_t orFlags = (flags | isec->flags) & orMask;
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index a10af9565a1d6..671a5ec4deccb 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -1411,6 +1411,7 @@ static std::optional<uint64_t> parseFlag(StringRef tok) {
       .Case(CASE_ENT(SHF_COMPRESSED))
       .Case(CASE_ENT(SHF_EXCLUDE))
       .Case(CASE_ENT(SHF_ARM_PURECODE))
+      .Case(CASE_ENT(SHF_AARCH64_PURECODE))
       .Default(std::nullopt);
 #undef CASE_ENT
 }
diff --git a/lld/test/ELF/aarch64-execute-only.s b/lld/test/ELF/aarch64-execute-only.s
new file mode 100644
index 0000000000000..e67bbf05ff2e2
--- /dev/null
+++ b/lld/test/ELF/aarch64-execute-only.s
@@ -0,0 +1,33 @@
+// REQUIRES: aarch64
+
+// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-none %s -o %t.o
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readelf -l %t.so | FileCheck --implicit-check-not=LOAD %s
+
+// RUN: echo ".section .foo,\"ax\"; \
+// RUN:       ret" > %t.s
+// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-none %t.s -o %t2.o
+// RUN: ld.lld %t.o %t2.o -o %t.so -shared
+// RUN: llvm-readelf -l %t.so | FileCheck --check-prefix=DIFF --implicit-check-not=LOAD %s
+
+// CHECK:      LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R   0x10000
+// CHECK:      LOAD           0x000248 0x0000000000010248 0x0000000000010248 0x{{.*}} 0x{{.*}} R E 0x10000
+// CHECK:      LOAD           0x00024c 0x000000000002024c 0x000000000002024c 0x{{.*}} 0x{{.*}}   E 0x10000
+// CHECK:      LOAD           0x000250 0x0000000000030250 0x0000000000030250 0x000070 0x000db0 RW  0x10000
+
+// CHECK: 01     .dynsym .gnu.hash .hash .dynstr
+// CHECK: 02     .text
+// CHECK: 03     .foo
+// CHECK: 04     .dynamic
+
+// DIFF:      LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x00020d 0x00020d R   0x10000
+// DIFF:      LOAD           0x000210 0x0000000000010210 0x0000000000010210 0x00000c 0x00000c R E 0x10000
+// DIFF:      LOAD           0x000220 0x0000000000020220 0x0000000000020220 0x000070 0x000de0 RW  0x10000
+
+// DIFF: 01     .dynsym .gnu.hash .hash .dynstr
+// DIFF: 02     .text .foo
+// DIFF: 03     .dynamic
+
+        ret
+        .section .foo,"axy"
+        ret

>From 60ed7aac3d9053dd0863fcde8005b13318971df9 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 16:46:48 +0100
Subject: [PATCH 2/6] Address some review feedback

---
 lld/ELF/Driver.cpp | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 90b6dc0c75036..8a8d06992425a 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -2934,22 +2934,18 @@ static void checkExecuteOnly(Ctx &ctx) {
   if (ctx.arg.emachine != EM_AARCH64)
     return;
 
-  auto report = [&](StringRef config) -> ELFSyncStream {
+  auto reportUnless = [&](StringRef config, bool cond) -> ELFSyncStream {
+    if (cond)
+      return {ctx, DiagLevel::None};
     if (config == "error")
       return {ctx, DiagLevel::Err};
     if (config == "warning")
       return {ctx, DiagLevel::Warn};
     return {ctx, DiagLevel::None};
   };
-  auto reportUnless = [&](StringRef config, bool cond) -> ELFSyncStream {
-    if (cond)
-      return {ctx, DiagLevel::None};
-    return report(config);
-  };
 
   for (ELFFileBase *file : ctx.objectFiles) {
     for (InputSectionBase *section : file->getSections()) {
-      // Only check for executable sections.
       if (!(section && section->flags & SHF_EXECINSTR))
         continue;
 

>From 537dd6d81ba92afee33f0160284ef47a57074027 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 17:14:23 +0100
Subject: [PATCH 3/6] Add tests for -z execute-only-report

---
 .../ELF/aarch64-execute-only-report-error.s     | 17 +++++++++++++++++
 lld/test/ELF/aarch64-execute-only-report.s      | 16 ++++++++++++++++
 2 files changed, 33 insertions(+)
 create mode 100644 lld/test/ELF/aarch64-execute-only-report-error.s
 create mode 100644 lld/test/ELF/aarch64-execute-only-report.s

diff --git a/lld/test/ELF/aarch64-execute-only-report-error.s b/lld/test/ELF/aarch64-execute-only-report-error.s
new file mode 100644
index 0000000000000..3414e34a3a2cc
--- /dev/null
+++ b/lld/test/ELF/aarch64-execute-only-report-error.s
@@ -0,0 +1,17 @@
+// REQUIRES: x86
+
+// RUN: llvm-mc --triple=x86_64-pc-linux --filetype=obj -o %t.o %s
+// RUN: not ld.lld -z execute-only-report=warning %t.o -o /dev/null 2>&1 \
+// RUN:     | FileCheck %s
+// RUN: not ld.lld -z execute-only-report=error %t.o -o /dev/null 2>&1 \
+// RUN:     | FileCheck %s
+
+// CHECK: error: -z execute-only-report only supported on AArch64
+//
+// RUN: not ld.lld -z execute-only-report=something %t.o -o /dev/null 2>&1 \
+// RUN:     | FileCheck --check-prefix=REPORT_INVALID %s
+// REPORT_INVALID: error: -z execute-only-report= parameter something is not recognized
+
+        .globl _start
+_start:
+        ret
diff --git a/lld/test/ELF/aarch64-execute-only-report.s b/lld/test/ELF/aarch64-execute-only-report.s
new file mode 100644
index 0000000000000..9c120997270ad
--- /dev/null
+++ b/lld/test/ELF/aarch64-execute-only-report.s
@@ -0,0 +1,16 @@
+// REQUIRES: aarch64
+
+// RUN: llvm-mc --triple=aarch64-linux-none --filetype=obj -o %t.o %s
+// RUN: ld.lld -z execute-only-report=none --fatal-warnings %t.o -o /dev/null 2>&1
+// RUN: ld.lld -z execute-only-report=warning %t.o -o /dev/null 2>&1 \
+// RUN:     | FileCheck --check-prefix=WARNING %s
+// RUN: not ld.lld -z execute-only-report=error %t.o -o /dev/null 2>&1 \
+// RUN:     | FileCheck --check-prefix=ERROR %s
+
+// WARNING: warning: {{.*}}.o: -z execute-only-report: section .text does not have SHF_AARCH64_PURECODE flag set
+// ERROR: error: {{.*}}.o: -z execute-only-report: section .text does not have SHF_AARCH64_PURECODE flag set
+
+        .section .text,"ax"
+        .globl _start
+_start:
+        ret

>From 41472adbbdd435eafb9c9a98658db987fb5a544a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Csan=C3=A1d=20Hajd=C3=BA?= <csanad.hajdu at arm.com>
Date: Wed, 5 Feb 2025 10:04:48 +0100
Subject: [PATCH 4/6] Address review feedback from MaskRay

---
 lld/ELF/Driver.cpp                         | 16 ++++++++--------
 lld/ELF/OutputSections.cpp                 |  6 ++++--
 lld/test/ELF/aarch64-execute-only-report.s |  4 ++--
 3 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 8a8d06992425a..4af9a1dadd546 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1644,9 +1644,9 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   auto reports = {
       std::make_pair("bti-report", &ctx.arg.zBtiReport),
       std::make_pair("cet-report", &ctx.arg.zCetReport),
+      std::make_pair("execute-only-report", &ctx.arg.zExecuteOnlyReport),
       std::make_pair("gcs-report", &ctx.arg.zGcsReport),
-      std::make_pair("pauth-report", &ctx.arg.zPauthReport),
-      std::make_pair("execute-only-report", &ctx.arg.zExecuteOnlyReport)};
+      std::make_pair("pauth-report", &ctx.arg.zPauthReport)};
   for (opt::Arg *arg : args.filtered(OPT_z)) {
     std::pair<StringRef, StringRef> option =
         StringRef(arg->getValue()).split('=');
@@ -2945,15 +2945,15 @@ static void checkExecuteOnly(Ctx &ctx) {
   };
 
   for (ELFFileBase *file : ctx.objectFiles) {
-    for (InputSectionBase *section : file->getSections()) {
-      if (!(section && section->flags & SHF_EXECINSTR))
+    for (InputSectionBase *sec : file->getSections()) {
+      if (!(sec && sec->flags & SHF_EXECINSTR))
         continue;
 
-      OutputSection *outputSection = section->getOutputSection();
-      if (outputSection && outputSection->name == ".text") {
+      OutputSection *osec = sec->getOutputSection();
+      if (osec && osec->name == ".text") {
         reportUnless(ctx.arg.zExecuteOnlyReport,
-                     section->flags & SHF_AARCH64_PURECODE)
-            << file << ": -z execute-only-report: section " << section->name
+                     sec->flags & SHF_AARCH64_PURECODE)
+            << "-z execute-only-report: " << sec
             << " does not have SHF_AARCH64_PURECODE flag set";
       }
     }
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index c8dc0e5fe0335..1020dd9f2569e 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -42,8 +42,10 @@ using namespace lld::elf;
 
 uint32_t OutputSection::getPhdrFlags() const {
   uint32_t ret = 0;
-  if ((ctx.arg.emachine != EM_ARM || !(flags & SHF_ARM_PURECODE)) &&
-      (ctx.arg.emachine != EM_AARCH64 || !(flags & SHF_AARCH64_PURECODE)))
+  bool purecode =
+      (ctx.arg.emachine == EM_ARM && (flags & SHF_ARM_PURECODE)) ||
+      (ctx.arg.emachine == EM_AARCH64 && (flags & SHF_AARCH64_PURECODE));
+  if (!purecode)
     ret |= PF_R;
   if (flags & SHF_WRITE)
     ret |= PF_W;
diff --git a/lld/test/ELF/aarch64-execute-only-report.s b/lld/test/ELF/aarch64-execute-only-report.s
index 9c120997270ad..b385ab4a2935d 100644
--- a/lld/test/ELF/aarch64-execute-only-report.s
+++ b/lld/test/ELF/aarch64-execute-only-report.s
@@ -7,8 +7,8 @@
 // RUN: not ld.lld -z execute-only-report=error %t.o -o /dev/null 2>&1 \
 // RUN:     | FileCheck --check-prefix=ERROR %s
 
-// WARNING: warning: {{.*}}.o: -z execute-only-report: section .text does not have SHF_AARCH64_PURECODE flag set
-// ERROR: error: {{.*}}.o: -z execute-only-report: section .text does not have SHF_AARCH64_PURECODE flag set
+// WARNING: warning: -z execute-only-report: {{.*}}.o:(.text) does not have SHF_AARCH64_PURECODE flag set
+// ERROR: error: -z execute-only-report: {{.*}}.o:(.text) does not have SHF_AARCH64_PURECODE flag set
 
         .section .text,"ax"
         .globl _start

>From d12bc148f72b8887266d9f10754c6f2903169b57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Csan=C3=A1d=20Hajd=C3=BA?= <csanad.hajdu at arm.com>
Date: Wed, 5 Feb 2025 10:42:10 +0100
Subject: [PATCH 5/6] Move -z execute-only-report checking into Writer.cpp

---
 lld/ELF/Driver.cpp | 32 --------------------------------
 lld/ELF/Writer.cpp | 30 ++++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+), 32 deletions(-)

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 4af9a1dadd546..0af4d71b2e095 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -2930,36 +2930,6 @@ static void readSecurityNotes(Ctx &ctx) {
     ctx.arg.andFeatures &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
 }
 
-static void checkExecuteOnly(Ctx &ctx) {
-  if (ctx.arg.emachine != EM_AARCH64)
-    return;
-
-  auto reportUnless = [&](StringRef config, bool cond) -> ELFSyncStream {
-    if (cond)
-      return {ctx, DiagLevel::None};
-    if (config == "error")
-      return {ctx, DiagLevel::Err};
-    if (config == "warning")
-      return {ctx, DiagLevel::Warn};
-    return {ctx, DiagLevel::None};
-  };
-
-  for (ELFFileBase *file : ctx.objectFiles) {
-    for (InputSectionBase *sec : file->getSections()) {
-      if (!(sec && sec->flags & SHF_EXECINSTR))
-        continue;
-
-      OutputSection *osec = sec->getOutputSection();
-      if (osec && osec->name == ".text") {
-        reportUnless(ctx.arg.zExecuteOnlyReport,
-                     sec->flags & SHF_AARCH64_PURECODE)
-            << "-z execute-only-report: " << sec
-            << " does not have SHF_AARCH64_PURECODE flag set";
-      }
-    }
-  }
-}
-
 static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
   switch (file->ekind) {
   case ELF32LEKind:
@@ -3325,8 +3295,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
     ctx.script->addOrphanSections();
   }
 
-  checkExecuteOnly(ctx);
-
   {
     llvm::TimeTraceScope timeScope("Merge/finalize input sections");
 
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 858f92c001158..8e1698af518a4 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -64,6 +64,7 @@ template <class ELFT> class Writer {
   void sortOrphanSections();
   void finalizeSections();
   void checkExecuteOnly();
+  void checkExecuteOnlyReport();
   void setReservedSymbolSections();
 
   SmallVector<std::unique_ptr<PhdrEntry>, 0> createPhdrs(Partition &part);
@@ -325,6 +326,7 @@ template <class ELFT> void Writer<ELFT>::run() {
   // finalizeSections does that.
   finalizeSections();
   checkExecuteOnly();
+  checkExecuteOnlyReport();
 
   // If --compressed-debug-sections is specified, compress .debug_* sections.
   // Do it right now because it changes the size of output sections.
@@ -2179,6 +2181,34 @@ template <class ELFT> void Writer<ELFT>::checkExecuteOnly() {
                             "data and code";
 }
 
+// Check that all input sections of .text have the SHF_AARCH64_PURECODE section
+// flag set.
+template <class ELFT> void Writer<ELFT>::checkExecuteOnlyReport() {
+  if (ctx.arg.emachine != EM_AARCH64 || ctx.arg.zExecuteOnlyReport == "none")
+    return;
+
+  auto reportUnless = [&](StringRef config, bool cond) -> ELFSyncStream {
+    if (cond)
+      return {ctx, DiagLevel::None};
+    if (config == "error")
+      return {ctx, DiagLevel::Err};
+    if (config == "warning")
+      return {ctx, DiagLevel::Warn};
+    return {ctx, DiagLevel::None};
+  };
+
+  SmallVector<InputSection *, 0> storage;
+  for (OutputSection *osec : ctx.outputSections) {
+    if (osec->name != ".text")
+      continue;
+    for (InputSection *sec : getInputSections(*osec, storage))
+      reportUnless(ctx.arg.zExecuteOnlyReport,
+                   sec->flags & SHF_AARCH64_PURECODE)
+          << "-z execute-only-report: " << sec
+          << " does not have SHF_AARCH64_PURECODE flag set";
+  }
+}
+
 // The linker is expected to define SECNAME_start and SECNAME_end
 // symbols for a few sections. This function defines them.
 template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {

>From 5d894c7e2a5687ab03361b926d3207e205dbe578 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Csan=C3=A1d=20Hajd=C3=BA?= <csanad.hajdu at arm.com>
Date: Fri, 14 Feb 2025 11:29:24 +0100
Subject: [PATCH 6/6] Resolve merge conflicts after rebase

---
 lld/ELF/Driver.cpp | 23 ++---------------------
 1 file changed, 2 insertions(+), 21 deletions(-)

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 0af4d71b2e095..ad69121643a20 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -404,6 +404,8 @@ static void checkOptions(Ctx &ctx) {
       ErrAlways(ctx) << "-z gcs-report only supported on AArch64";
     if (ctx.arg.zGcs != GcsPolicy::Implicit)
       ErrAlways(ctx) << "-z gcs only supported on AArch64";
+    if (ctx.arg.zExecuteOnlyReport != "none")
+      ErrAlways(ctx) << "-z execute-only-report only supported on AArch64";
   }
 
   if (ctx.arg.emachine != EM_PPC64) {
@@ -461,27 +463,6 @@ static void checkOptions(Ctx &ctx) {
 
   if (ctx.arg.zRetpolineplt && ctx.arg.zForceIbt)
     ErrAlways(ctx) << "-z force-ibt may not be used with -z retpolineplt";
-
-  if (ctx.arg.emachine != EM_AARCH64) {
-    if (ctx.arg.zPacPlt)
-      ErrAlways(ctx) << "-z pac-plt only supported on AArch64";
-    if (ctx.arg.zForceBti)
-      ErrAlways(ctx) << "-z force-bti only supported on AArch64";
-    if (ctx.arg.zBtiReport != "none")
-      ErrAlways(ctx) << "-z bti-report only supported on AArch64";
-    if (ctx.arg.zPauthReport != "none")
-      ErrAlways(ctx) << "-z pauth-report only supported on AArch64";
-    if (ctx.arg.zGcsReport != "none")
-      ErrAlways(ctx) << "-z gcs-report only supported on AArch64";
-    if (ctx.arg.zGcs != GcsPolicy::Implicit)
-      ErrAlways(ctx) << "-z gcs only supported on AArch64";
-    if (ctx.arg.zExecuteOnlyReport != "none")
-      ErrAlways(ctx) << "-z execute-only-report only supported on AArch64";
-  }
-
-  if (ctx.arg.emachine != EM_386 && ctx.arg.emachine != EM_X86_64 &&
-      ctx.arg.zCetReport != "none")
-    ErrAlways(ctx) << "-z cet-report only supported on X86 and X86_64";
 }
 
 static const char *getReproduceOption(opt::InputArgList &args) {



More information about the llvm-commits mailing list