[lld] [AArch64][GCS][LLD] Introduce -zgcs-report-dynamic Command Line Option (PR #127787)

Jack Styles via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 19 03:51:51 PST 2025


https://github.com/Stylie777 updated https://github.com/llvm/llvm-project/pull/127787

>From 0464fa5d81c5acc8e5cc7aec09d457f8450b393b Mon Sep 17 00:00:00 2001
From: Jack Styles <jack.styles at arm.com>
Date: Tue, 18 Feb 2025 15:21:33 +0000
Subject: [PATCH 1/5] [AArch64][GCS] Convert -zgcs-report processing to use an
 Enum

To enable easier processing of inheritance for the -zgcs-report-dynamic
option, the -zgcs-report option has been converted to use an Enum value
to define its setting rather than a StringRef value.

This adds the Enum class for defining the three options, None, Warning
and Error, along with the appropirate methods for processing the users
input. To enable the error messages, a new lambda function has been
added. This processes the Enum value and passes the appropriate string
into the `report` lambda function for the error message. Error messages
have been updated where required.
---
 lld/ELF/Config.h                   |  5 +++-
 lld/ELF/Driver.cpp                 | 37 +++++++++++++++++++++++++++---
 lld/test/ELF/aarch64-feature-gcs.s |  5 ++++
 3 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index f132b11b20c63..9ae8d08cf2a9d 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -136,6 +136,9 @@ enum LtoKind : uint8_t {UnifiedThin, UnifiedRegular, Default};
 // For -z gcs=
 enum class GcsPolicy { Implicit, Never, Always };
 
+// For -z gcs-report=
+enum class GcsReportPolicy { None, Warning, Error };
+
 struct SymbolVersion {
   llvm::StringRef name;
   bool isExternCpp;
@@ -228,7 +231,6 @@ struct Config {
   StringRef zBtiReport = "none";
   StringRef zCetReport = "none";
   StringRef zPauthReport = "none";
-  StringRef zGcsReport = "none";
   bool ltoBBAddrMap;
   llvm::StringRef ltoBasicBlockSections;
   std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
@@ -393,6 +395,7 @@ struct Config {
   UnresolvedPolicy unresolvedSymbolsInShlib;
   Target2Policy target2;
   GcsPolicy zGcs;
+  GcsReportPolicy zGcsReport;
   bool power10Stubs;
   ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
   BuildIdKind buildId = BuildIdKind::None;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 70a293875f27b..184b6da63841a 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -400,7 +400,7 @@ static void checkOptions(Ctx &ctx) {
       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")
+    if (ctx.arg.zGcsReport != GcsReportPolicy::None)
       ErrAlways(ctx) << "-z gcs-report only supported on AArch64";
     if (ctx.arg.zGcs != GcsPolicy::Implicit)
       ErrAlways(ctx) << "-z gcs only supported on AArch64";
@@ -569,6 +569,27 @@ static GcsPolicy getZGcs(Ctx &ctx, opt::InputArgList &args) {
   return ret;
 }
 
+static GcsReportPolicy getZGcsReport(Ctx &ctx, opt::InputArgList &args) {
+  GcsReportPolicy ret = GcsReportPolicy::None;
+
+  for (auto *arg : args.filtered(OPT_z)) {
+    std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
+    if (kv.first == "gcs-report") {
+      arg->claim();
+      if (kv.second == "none")
+        ret = GcsReportPolicy::None;
+      else if (kv.second == "warning")
+        ret = GcsReportPolicy::Warning;
+      else if (kv.second == "error")
+        ret = GcsReportPolicy::Error;
+      else
+        ErrAlways(ctx) << "unknown -z gcs-report= value: " << kv.second;
+    }
+  }
+
+  return ret;
+}
+
 // Report a warning for an unknown -z option.
 static void checkZOptions(Ctx &ctx, opt::InputArgList &args) {
   // This function is called before getTarget(), when certain options are not
@@ -1548,6 +1569,7 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   ctx.arg.zForceBti = hasZOption(args, "force-bti");
   ctx.arg.zForceIbt = hasZOption(args, "force-ibt");
   ctx.arg.zGcs = getZGcs(ctx, args);
+  ctx.arg.zGcsReport = getZGcsReport(ctx, args);
   ctx.arg.zGlobal = hasZOption(args, "global");
   ctx.arg.zGnustack = getZGnuStack(args);
   ctx.arg.zHazardplt = hasZOption(args, "hazardplt");
@@ -1622,7 +1644,6 @@ 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("gcs-report", &ctx.arg.zGcsReport),
                   std::make_pair("pauth-report", &ctx.arg.zPauthReport)};
   for (opt::Arg *arg : args.filtered(OPT_z)) {
     std::pair<StringRef, StringRef> option =
@@ -2825,6 +2846,16 @@ static void readSecurityNotes(Ctx &ctx) {
       return {ctx, DiagLevel::None};
     return report(config);
   };
+  auto reportGcsPolicy = [&](GcsReportPolicy config, bool cond) -> ELFSyncStream {
+    if (cond)
+      return {ctx, DiagLevel::None};
+    StringRef configString = "none";
+    if(config == GcsReportPolicy::Warning)
+      configString = "warning";
+    else if (config == GcsReportPolicy::Error)
+      configString = "error";
+    return report(configString);
+  };
   for (ELFFileBase *f : ctx.objectFiles) {
     uint32_t features = f->andFeatures;
 
@@ -2834,7 +2865,7 @@ static void readSecurityNotes(Ctx &ctx) {
         << ": -z bti-report: file does not have "
            "GNU_PROPERTY_AARCH64_FEATURE_1_BTI property";
 
-    reportUnless(ctx.arg.zGcsReport,
+    reportGcsPolicy(ctx.arg.zGcsReport,
                  features & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
         << f
         << ": -z gcs-report: file does not have "
diff --git a/lld/test/ELF/aarch64-feature-gcs.s b/lld/test/ELF/aarch64-feature-gcs.s
index 7a08673dbb7e6..9d71bcc1df262 100644
--- a/lld/test/ELF/aarch64-feature-gcs.s
+++ b/lld/test/ELF/aarch64-feature-gcs.s
@@ -54,6 +54,11 @@
 
 # INVALID: error: unknown -z gcs= value: nonsense
 
+## An invalid gcs option should give an error
+# RUN: not ld.lld func1-gcs.o func2-gcs.o func3-gcs.o -z gcs-report=nonsense 2>&1 | FileCheck --check-prefix=INVALID-GCS-REPORT %s
+
+# INVALID-GCS-REPORT: error: unknown -z gcs-report= value: nonsense
+
 #--- func1-gcs.s
 .section ".note.gnu.property", "a"
 .long 4

>From 118d556202a826ba7c41da05e53fa5557e1f44f6 Mon Sep 17 00:00:00 2001
From: Jack Styles <jack.styles at arm.com>
Date: Mon, 17 Feb 2025 15:22:49 +0000
Subject: [PATCH 2/5] [AArch64][LLD] Parse Shared Files Program Headers for GCS
 GNU Attribute

To enable the checking of the GNU GCS Attributes when linking together
an application, LLD needs to be able to parse a Shared Files program
headers. This will enable a new option, allowing the user to output
diagnostic information relating to if there are shared files being
used that do not support the GCS extension.

To define if a shared file support GCS, the GNU GCS Attribute will
be stored within the Program Header, so this is parsed and, if
found, the `andFeatures` value updated to reflect the support level.
---
 lld/ELF/InputFiles.cpp | 23 +++++++++++++++++++++++
 lld/ELF/InputFiles.h   |  8 ++++++++
 2 files changed, 31 insertions(+)

diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index d43de8ce6dfef..055dd5ae0a668 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -18,8 +18,10 @@
 #include "Target.h"
 #include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/DWARF.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/LTO/LTO.h"
 #include "llvm/Object/IRObjectFile.h"
 #include "llvm/Support/ARMAttributeParser.h"
@@ -502,6 +504,7 @@ void ELFFileBase::init() {
 template <class ELFT> void ELFFileBase::init(InputFile::Kind k) {
   using Elf_Shdr = typename ELFT::Shdr;
   using Elf_Sym = typename ELFT::Sym;
+  using Elf_Phdr = typename ELFT::Phdr;
 
   // Initialize trivial attributes.
   const ELFFile<ELFT> &obj = getObj<ELFT>();
@@ -513,6 +516,10 @@ template <class ELFT> void ELFFileBase::init(InputFile::Kind k) {
   elfShdrs = sections.data();
   numELFShdrs = sections.size();
 
+  ArrayRef<Elf_Phdr> pHeaders = CHECK2(obj.program_headers(), this);
+  elfPhdrs = pHeaders.data();
+  numElfPhdrs = pHeaders.size();
+
   // Find a symbol table.
   const Elf_Shdr *symtabSec =
       findSection(sections, k == SharedKind ? SHT_DYNSYM : SHT_SYMTAB);
@@ -1418,6 +1425,21 @@ std::vector<uint32_t> SharedFile::parseVerneed(const ELFFile<ELFT> &obj,
   return verneeds;
 }
 
+// To determine if a shared file can support the AArch64 GCS extension, the program headers for the object
+// need to be read. This ensures when input options are read, appropriate warning/error messages can be
+// emitted depending on the user's command line options.
+template <typename ELFT>
+uint64_t SharedFile::parseGnuAttributes(const typename ELFT::PhdrRange headers) {
+  if(numElfPhdrs == 0)
+    return 0;
+  uint64_t attributes = 0;
+  for (unsigned i = 0; i < numElfPhdrs; i++)
+    if(headers[i].p_type == PT_GNU_PROPERTY && headers[i].p_flags & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
+      attributes |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+
+  return attributes;
+}
+
 // We do not usually care about alignments of data in shared object
 // files because the loader takes care of it. However, if we promote a
 // DSO symbol to point to .bss due to copy relocation, we need to keep
@@ -1528,6 +1550,7 @@ template <class ELFT> void SharedFile::parse() {
 
   verdefs = parseVerdefs<ELFT>(obj.base(), verdefSec);
   std::vector<uint32_t> verneeds = parseVerneed<ELFT>(obj, verneedSec);
+  this->andFeatures = parseGnuAttributes<ELFT>(getELFPhdrs<ELFT>());
 
   // Parse ".gnu.version" section which is a parallel array for the symbol
   // table. If a given file doesn't have a ".gnu.version" section, we use
diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index 0b186db1ba0d1..99b1842326622 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -206,6 +206,10 @@ class ELFFileBase : public InputFile {
     return typename ELFT::ShdrRange(
         reinterpret_cast<const typename ELFT::Shdr *>(elfShdrs), numELFShdrs);
   }
+  template <typename ELFT> typename ELFT::PhdrRange getELFPhdrs() const {
+    return typename ELFT::PhdrRange(
+      reinterpret_cast<const typename ELFT::Phdr *>(elfPhdrs), numElfPhdrs);
+  }
   template <typename ELFT> typename ELFT::SymRange getELFSyms() const {
     return typename ELFT::SymRange(
         reinterpret_cast<const typename ELFT::Sym *>(elfSyms), numSymbols);
@@ -224,8 +228,10 @@ class ELFFileBase : public InputFile {
   StringRef stringTable;
   const void *elfShdrs = nullptr;
   const void *elfSyms = nullptr;
+  const void *elfPhdrs = nullptr;
   uint32_t numELFShdrs = 0;
   uint32_t firstGlobal = 0;
+  uint32_t numElfPhdrs = 0;
 
   // Below are ObjFile specific members.
 
@@ -364,6 +370,8 @@ class SharedFile : public ELFFileBase {
   template <typename ELFT>
   std::vector<uint32_t> parseVerneed(const llvm::object::ELFFile<ELFT> &obj,
                                      const typename ELFT::Shdr *sec);
+  template <typename ELFT>
+  uint64_t parseGnuAttributes(const typename ELFT::PhdrRange headers);
 };
 
 class BinaryFile : public InputFile {

>From 178eaf7e956ea699e70aebb7b7035ffc763d62cb Mon Sep 17 00:00:00 2001
From: Jack Styles <jack.styles at arm.com>
Date: Thu, 30 Jan 2025 13:19:28 +0000
Subject: [PATCH 3/5] [AArch64][GCS][LLD] Introduce -z gcs-report-dynamic
 option

When GCS was introduced to LLD, the gcs-report option allowed
for a user to gain information relating to if their relocatable objects
supported the feature. For an executable or shared-library to support
GCS, all relocatable objects must declare that they support GCS.

The gcs-report checks were only done on relocatable object files,
however for a program to enable GCS, the executable and all
shared libraries that it loads must enable GCS.
gcs-report-dynamic enables checks to be performed on all shared
objects loaded by LLD, and in cases where GCS is not supported,
a warning or error will be emitted.

It should be noted that only shared files directly passed to LLD
are checked for GCS support. Files that are noted in the `DT_NEEDED`
tags are assumed to have had their GCS support checked when they
were created.

The behaviour of the -zgcs-dynamic-report option matches that of
GNU ld. The behaviour is as follows unless the user explicitly
sets the value:
* -zgcs-report=warning or -zgcs-report=error implies
-zgcs-report-dynamic=warning.

This approach avoids inheriting an error level if the
user wishes to continue building a module without rebuilding all
the shared libraries. The same approach was taken for the GNU ld
linker, so behaviour is identical across the toolchains.

This implementation matches the error message and command
line interface used within the GNU ld Linker. See here:
https://inbox.sourceware.org/binutils/20241206153746.3760179-2-matthieu.longo@arm.com/
---
 lld/ELF/Config.h                   |  3 +-
 lld/ELF/Driver.cpp                 | 48 ++++++++++++++++++++++++++++++
 lld/docs/ReleaseNotes.rst          |  5 ++++
 lld/test/ELF/aarch64-feature-gcs.s | 21 +++++++++++++
 4 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 9ae8d08cf2a9d..3221c83d499e1 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -136,7 +136,7 @@ enum LtoKind : uint8_t {UnifiedThin, UnifiedRegular, Default};
 // For -z gcs=
 enum class GcsPolicy { Implicit, Never, Always };
 
-// For -z gcs-report=
+// For -z gcs-report= and -zgcs-report-dynamic
 enum class GcsReportPolicy { None, Warning, Error };
 
 struct SymbolVersion {
@@ -396,6 +396,7 @@ struct Config {
   Target2Policy target2;
   GcsPolicy zGcs;
   GcsReportPolicy zGcsReport;
+  GcsReportPolicy zGcsReportDynamic;
   bool power10Stubs;
   ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
   BuildIdKind buildId = BuildIdKind::None;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 184b6da63841a..e9098ff94f1a4 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -49,11 +49,15 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/LTO/LTO.h"
 #include "llvm/Object/Archive.h"
+#include "llvm/Object/ELFTypes.h"
 #include "llvm/Object/IRObjectFile.h"
+#include "llvm/Option/ArgList.h"
 #include "llvm/Remarks/HotnessThresholdParser.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/SaveAndRestore.h"
@@ -68,6 +72,7 @@
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cstdlib>
+#include <memory>
 #include <tuple>
 #include <utility>
 
@@ -402,6 +407,8 @@ static void checkOptions(Ctx &ctx) {
       ErrAlways(ctx) << "-z pauth-report only supported on AArch64";
     if (ctx.arg.zGcsReport != GcsReportPolicy::None)
       ErrAlways(ctx) << "-z gcs-report only supported on AArch64";
+    if(ctx.arg.zGcsReportDynamic != GcsReportPolicy::None)
+      ErrAlways(ctx) << "-z gcs-report-dynamic only supported on AArch64";
     if (ctx.arg.zGcs != GcsPolicy::Implicit)
       ErrAlways(ctx) << "-z gcs only supported on AArch64";
   }
@@ -590,6 +597,36 @@ static GcsReportPolicy getZGcsReport(Ctx &ctx, opt::InputArgList &args) {
   return ret;
 }
 
+static GcsReportPolicy getZGcsReportDynamic(Ctx &ctx, opt::InputArgList &args) {
+  GcsReportPolicy ret = GcsReportPolicy::None;
+  for (auto *arg : args.filtered(OPT_z)) {
+    std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
+    if (kv.first == "gcs-report-dynamic") {
+      arg->claim();
+      if (kv.second == "none")
+        ret = GcsReportPolicy::None;
+      else if (kv.second == "warning")
+        ret = GcsReportPolicy::Warning;
+      else if (kv.second == "error")
+        ret = GcsReportPolicy::Error;
+      else
+        ErrAlways(ctx) << "unknown -z gcs-report-dynamic= value: " << kv.second;
+      // once the gcs-report-dynamic option has been processed, we want to break
+      // from the loop to ensure we do not overwrite the return value if the
+      // user has also passed a value for the gcs-report option.
+      break;
+    }
+    // If the user has not defined a value for gcs-report-dynamic, but has for
+    // gcs-report, we want to inherit that value for gcs-report-dynamic. This is
+    // capped at a warning to ensure a users module can still build, while providing
+    // information relating to if a dynamic object supports GCS.
+    if (kv.first == "gcs-report" && (kv.second == "warning" || kv.second == "error"))
+      ret = GcsReportPolicy::Warning;
+  }
+
+  return ret;
+}
+
 // Report a warning for an unknown -z option.
 static void checkZOptions(Ctx &ctx, opt::InputArgList &args) {
   // This function is called before getTarget(), when certain options are not
@@ -1570,6 +1607,7 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   ctx.arg.zForceIbt = hasZOption(args, "force-ibt");
   ctx.arg.zGcs = getZGcs(ctx, args);
   ctx.arg.zGcsReport = getZGcsReport(ctx, args);
+  ctx.arg.zGcsReportDynamic = getZGcsReportDynamic(ctx, args);
   ctx.arg.zGlobal = hasZOption(args, "global");
   ctx.arg.zGnustack = getZGnuStack(args);
   ctx.arg.zHazardplt = hasZOption(args, "hazardplt");
@@ -2935,6 +2973,16 @@ static void readSecurityNotes(Ctx &ctx) {
     ctx.arg.andFeatures |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
   else if (ctx.arg.zGcs == GcsPolicy::Never)
     ctx.arg.andFeatures &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+
+  // If we are utilising GCS at any stage, the sharedFiles should be checked to ensure they also support this feature.
+  // The gcs-report-dynamic option is used to indicate if the user wants information relating to this, and will be set
+  // depending on the user's input, or warning if gcs-report is set to either `warning` or `error`.
+  if(ctx.arg.andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
+    for (SharedFile *f : ctx.sharedFiles)
+      reportGcsPolicy(ctx.arg.zGcsReportDynamic, f->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_GCS) << f
+        << ": GCS is required by -z gcs, but this shared library lacks the necessary property note. The "
+        << "dynamic loader might not enable GCS or refuse to load the program unless all shared library "
+        << "dependancies have the GCS marking.";
 }
 
 static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 6f60efd87c975..a321b5234ee79 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -25,6 +25,11 @@ Non-comprehensive list of changes in this release
 
 ELF Improvements
 ----------------
+* For AArch64, support for the -zgcs-report-dynamic option has been added. This will provide users with
+the ability to check any Dynamic Objects explicitly passed to LLD for the GNU GCS Attribute Flag. This is
+required for all files when linking with GCS enabled. Unless defined by the user, -zgcs-report-dynamic
+inherits its value from the -zgcs-report option, capped at the `warning` level to ensure that a users
+module can still compile. This behaviour is designed to match the GNU ld Linker.
 
 Breaking changes
 ----------------
diff --git a/lld/test/ELF/aarch64-feature-gcs.s b/lld/test/ELF/aarch64-feature-gcs.s
index 9d71bcc1df262..e90ead3be23ff 100644
--- a/lld/test/ELF/aarch64-feature-gcs.s
+++ b/lld/test/ELF/aarch64-feature-gcs.s
@@ -49,6 +49,22 @@
 # REPORT-WARN: warning: func2.o: -z gcs-report: file does not have GNU_PROPERTY_AARCH64_FEATURE_1_GCS property
 # REPORT-ERROR: error: func3.o: -z gcs-report: file does not have GNU_PROPERTY_AARCH64_FEATURE_1_GCS property
 
+## gcs-report-dynamic should report any dynamic objects that does not have the gcs property. This also ensures the inhertance from gcs-report is working correctly.
+
+# RUN: ld.lld func1-gcs.o func3-gcs.o no-gcs.so force-gcs.so -o /dev/null -z gcs-report=warning -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
+# RUN: ld.lld func1-gcs.o func3-gcs.o no-gcs.so force-gcs.so -o /dev/null -z gcs-report-dynamic=warning -z gcs-report=warning -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
+# RUN: ld.lld func1-gcs.o func3-gcs.o no-gcs.so force-gcs.so -o /dev/null -z gcs-report=error -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
+# RUN: ld.lld func1-gcs.o func3-gcs.o no-gcs.so force-gcs.so -o /dev/null -z gcs-report=error -z gcs-report-dynamic=warning -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
+# RUN: ld.lld func1-gcs.o func3-gcs.o no-gcs.so force-gcs.so -o /dev/null -z gcs-report-dynamic=warning -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
+# RUN: ld.lld func1-gcs.o func3-gcs.o no-gcs.so force-gcs.so -o /dev/null -z gcs-report=error -z gcs-report-dynamic=warning -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
+# RUN: not ld.lld func1-gcs.o func3-gcs.o no-gcs.so force-gcs.so -o /dev/null -z gcs-report-dynamic=error -z gcs-report=error -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-ERROR-DYNAMIC %s
+# RUN: not ld.lld func1-gcs.o func3-gcs.o no-gcs.so force-gcs.so -o /dev/null -z gcs-report-dynamic=error -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-ERROR-DYNAMIC %s
+
+# REPORT-WARN-DYNAMIC: warning: no-gcs.so: GCS is required by -z gcs, but this shared library lacks the necessary property note. The dynamic loader might not enable GCS or refuse to load the program unless all shared library dependancies have the GCS marking.
+# REPORT-WARN-DYNAMIC-NOT: warning: force-gcs.so: GCS is required by -z gcs, but this shared library lacks the necessary property note. The dynamic loader might not enable GCS or refuse to load the program unless all shared library dependancies have the GCS marking.
+# REPORT-ERROR-DYNAMIC: error: no-gcs.so: GCS is required by -z gcs, but this shared library lacks the necessary property note. The dynamic loader might not enable GCS or refuse to load the program unless all shared library dependancies have the GCS marking.
+# REPORT-ERROR-DYNAMIC-NOT: error: force-gcs.so: GCS is required by -z gcs, but this shared library lacks the necessary property note. The dynamic loader might not enable GCS or refuse to load the program unless all shared library dependancies have the GCS marking.
+
 ## An invalid gcs option should give an error
 # RUN: not ld.lld func1-gcs.o func2-gcs.o func3-gcs.o -z gcs=nonsense 2>&1 | FileCheck --check-prefix=INVALID %s
 
@@ -59,6 +75,11 @@
 
 # INVALID-GCS-REPORT: error: unknown -z gcs-report= value: nonsense
 
+## An invalid gcs-report-dynamic option should give an error
+# RUN: not ld.lld func1-gcs.o func2-gcs.o func3-gcs.o -z gcs-report-dynamic=nonsense 2>&1 | FileCheck --check-prefix=INVALID-GCS-REPORT-DYNAMIC %s
+
+# INVALID-GCS-REPORT-DYNAMIC: error: unknown -z gcs-report-dynamic= value: nonsense
+
 #--- func1-gcs.s
 .section ".note.gnu.property", "a"
 .long 4

>From 7d5ff2070efbbdcc03f7e5c3a11ff7abaeb8a57b Mon Sep 17 00:00:00 2001
From: Jack Styles <jack.styles at arm.com>
Date: Wed, 19 Feb 2025 11:50:03 +0000
Subject: [PATCH 4/5] Update ReleaseNotes

---
 lld/docs/ReleaseNotes.rst | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index a321b5234ee79..176b110af4c18 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -26,10 +26,10 @@ Non-comprehensive list of changes in this release
 ELF Improvements
 ----------------
 * For AArch64, support for the -zgcs-report-dynamic option has been added. This will provide users with
-the ability to check any Dynamic Objects explicitly passed to LLD for the GNU GCS Attribute Flag. This is
-required for all files when linking with GCS enabled. Unless defined by the user, -zgcs-report-dynamic
-inherits its value from the -zgcs-report option, capped at the `warning` level to ensure that a users
-module can still compile. This behaviour is designed to match the GNU ld Linker.
+  the ability to check any Dynamic Objects explicitly passed to LLD for the GNU GCS Attribute Flag. This is
+  required for all files when linking with GCS enabled. Unless defined by the user, -zgcs-report-dynamic
+  inherits its value from the -zgcs-report option, capped at the `warning` level to ensure that a users
+  module can still compile. This behaviour is designed to match the GNU ld Linker.
 
 Breaking changes
 ----------------

>From 314bb7c55db5dacef4cdc63400acb0a3a24f2f89 Mon Sep 17 00:00:00 2001
From: Jack Styles <jack.styles at arm.com>
Date: Wed, 19 Feb 2025 11:50:18 +0000
Subject: [PATCH 5/5] Formatting fixes

---
 lld/ELF/Driver.cpp     | 40 ++++++++++++++++++++++++----------------
 lld/ELF/InputFiles.cpp | 15 +++++++++------
 lld/ELF/InputFiles.h   |  2 +-
 3 files changed, 34 insertions(+), 23 deletions(-)

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index e9098ff94f1a4..91ce31a4ddd5f 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -60,13 +60,13 @@
 #include "llvm/Option/ArgList.h"
 #include "llvm/Remarks/HotnessThresholdParser.h"
 #include "llvm/Support/CommandLine.h"
-#include "llvm/Support/SaveAndRestore.h"
 #include "llvm/Support/Compression.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/GlobPattern.h"
 #include "llvm/Support/LEB128.h"
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include "llvm/Support/TarWriter.h"
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/TimeProfiler.h"
@@ -407,7 +407,7 @@ static void checkOptions(Ctx &ctx) {
       ErrAlways(ctx) << "-z pauth-report only supported on AArch64";
     if (ctx.arg.zGcsReport != GcsReportPolicy::None)
       ErrAlways(ctx) << "-z gcs-report only supported on AArch64";
-    if(ctx.arg.zGcsReportDynamic != GcsReportPolicy::None)
+    if (ctx.arg.zGcsReportDynamic != GcsReportPolicy::None)
       ErrAlways(ctx) << "-z gcs-report-dynamic only supported on AArch64";
     if (ctx.arg.zGcs != GcsPolicy::Implicit)
       ErrAlways(ctx) << "-z gcs only supported on AArch64";
@@ -618,9 +618,10 @@ static GcsReportPolicy getZGcsReportDynamic(Ctx &ctx, opt::InputArgList &args) {
     }
     // If the user has not defined a value for gcs-report-dynamic, but has for
     // gcs-report, we want to inherit that value for gcs-report-dynamic. This is
-    // capped at a warning to ensure a users module can still build, while providing
-    // information relating to if a dynamic object supports GCS.
-    if (kv.first == "gcs-report" && (kv.second == "warning" || kv.second == "error"))
+    // capped at a warning to ensure a users module can still build, while
+    // providing information relating to if a dynamic object supports GCS.
+    if (kv.first == "gcs-report" &&
+        (kv.second == "warning" || kv.second == "error"))
       ret = GcsReportPolicy::Warning;
   }
 
@@ -2884,11 +2885,12 @@ static void readSecurityNotes(Ctx &ctx) {
       return {ctx, DiagLevel::None};
     return report(config);
   };
-  auto reportGcsPolicy = [&](GcsReportPolicy config, bool cond) -> ELFSyncStream {
+  auto reportGcsPolicy = [&](GcsReportPolicy config,
+                             bool cond) -> ELFSyncStream {
     if (cond)
       return {ctx, DiagLevel::None};
     StringRef configString = "none";
-    if(config == GcsReportPolicy::Warning)
+    if (config == GcsReportPolicy::Warning)
       configString = "warning";
     else if (config == GcsReportPolicy::Error)
       configString = "error";
@@ -2904,7 +2906,7 @@ static void readSecurityNotes(Ctx &ctx) {
            "GNU_PROPERTY_AARCH64_FEATURE_1_BTI property";
 
     reportGcsPolicy(ctx.arg.zGcsReport,
-                 features & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
+                    features & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
         << f
         << ": -z gcs-report: file does not have "
            "GNU_PROPERTY_AARCH64_FEATURE_1_GCS property";
@@ -2974,15 +2976,21 @@ static void readSecurityNotes(Ctx &ctx) {
   else if (ctx.arg.zGcs == GcsPolicy::Never)
     ctx.arg.andFeatures &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
 
-  // If we are utilising GCS at any stage, the sharedFiles should be checked to ensure they also support this feature.
-  // The gcs-report-dynamic option is used to indicate if the user wants information relating to this, and will be set
-  // depending on the user's input, or warning if gcs-report is set to either `warning` or `error`.
-  if(ctx.arg.andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
+  // If we are utilising GCS at any stage, the sharedFiles should be checked to
+  // ensure they also support this feature. The gcs-report-dynamic option is
+  // used to indicate if the user wants information relating to this, and will
+  // be set depending on the user's input, or warning if gcs-report is set to
+  // either `warning` or `error`.
+  if (ctx.arg.andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
     for (SharedFile *f : ctx.sharedFiles)
-      reportGcsPolicy(ctx.arg.zGcsReportDynamic, f->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_GCS) << f
-        << ": GCS is required by -z gcs, but this shared library lacks the necessary property note. The "
-        << "dynamic loader might not enable GCS or refuse to load the program unless all shared library "
-        << "dependancies have the GCS marking.";
+      reportGcsPolicy(ctx.arg.zGcsReportDynamic,
+                      f->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
+          << f
+          << ": GCS is required by -z gcs, but this shared library lacks the "
+             "necessary property note. The "
+          << "dynamic loader might not enable GCS or refuse to load the "
+             "program unless all shared library "
+          << "dependancies have the GCS marking.";
 }
 
 static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index 055dd5ae0a668..71fa5d6cfd09d 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -1425,16 +1425,19 @@ std::vector<uint32_t> SharedFile::parseVerneed(const ELFFile<ELFT> &obj,
   return verneeds;
 }
 
-// To determine if a shared file can support the AArch64 GCS extension, the program headers for the object
-// need to be read. This ensures when input options are read, appropriate warning/error messages can be
-// emitted depending on the user's command line options.
+// To determine if a shared file can support the AArch64 GCS extension, the
+// program headers for the object need to be read. This ensures when input
+// options are read, appropriate warning/error messages can be emitted depending
+// on the user's command line options.
 template <typename ELFT>
-uint64_t SharedFile::parseGnuAttributes(const typename ELFT::PhdrRange headers) {
-  if(numElfPhdrs == 0)
+uint64_t
+SharedFile::parseGnuAttributes(const typename ELFT::PhdrRange headers) {
+  if (numElfPhdrs == 0)
     return 0;
   uint64_t attributes = 0;
   for (unsigned i = 0; i < numElfPhdrs; i++)
-    if(headers[i].p_type == PT_GNU_PROPERTY && headers[i].p_flags & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
+    if (headers[i].p_type == PT_GNU_PROPERTY &&
+        headers[i].p_flags & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
       attributes |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
 
   return attributes;
diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index 99b1842326622..604aa84429d53 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -208,7 +208,7 @@ class ELFFileBase : public InputFile {
   }
   template <typename ELFT> typename ELFT::PhdrRange getELFPhdrs() const {
     return typename ELFT::PhdrRange(
-      reinterpret_cast<const typename ELFT::Phdr *>(elfPhdrs), numElfPhdrs);
+        reinterpret_cast<const typename ELFT::Phdr *>(elfPhdrs), numElfPhdrs);
   }
   template <typename ELFT> typename ELFT::SymRange getELFSyms() const {
     return typename ELFT::SymRange(



More information about the llvm-commits mailing list