[lld] [lld][AArch64] Add support for GCS (PR #90732)
John Brawn via llvm-commits
llvm-commits at lists.llvm.org
Wed May 1 07:15:50 PDT 2024
https://github.com/john-brawn-arm created https://github.com/llvm/llvm-project/pull/90732
This adds the -z gcs and -z gcs-report options, which behave similarly to -z shtk and -z cet-report, except that -z gcs accepts a parameter:
* -z gcs=implicit is the default behaviour, where the GCS bit is inferred from the input objects.
* -z gcs=never clears the GCS bit, ignoring the input objects.
* -z gcs=always sets the GCS bit, ignoring the input objects.
* -z gcs is the same as -z gcs=always.
This is so that there's a means of explicitly disabling GCS even when all input objects have the GCS bit set.
>From 386e93630a3aa2846f50e88732004cddf358bce4 Mon Sep 17 00:00:00 2001
From: John Brawn <john.brawn at arm.com>
Date: Wed, 13 Dec 2023 16:22:23 +0000
Subject: [PATCH] [lld][AArch64] Add support for GCS
This adds the -z gcs and -z gcs-report options, which behave similarly
to -z shtk and -z cet-report, except that -z gcs accepts a parameter:
* -z gcs=implicit is the default behaviour, where the GCS bit is
inferred from the input objects.
* -z gcs=never clears the GCS bit, ignoring the input objects.
* -z gcs=always sets the GCS bit, ignoring the input objects.
* -z gcs is the same as -z gcs=always.
This is so that there's a means of explicitly disabling GCS even when
all input objects have the GCS bit set.
---
lld/ELF/Config.h | 2 +
lld/ELF/Driver.cpp | 38 +++++++++++++-
lld/test/ELF/Inputs/aarch64-func2-gcs.s | 19 +++++++
lld/test/ELF/Inputs/aarch64-func3-gcs.s | 16 ++++++
lld/test/ELF/aarch64-feature-gcs.s | 66 +++++++++++++++++++++++++
5 files changed, 140 insertions(+), 1 deletion(-)
create mode 100644 lld/test/ELF/Inputs/aarch64-func2-gcs.s
create mode 100644 lld/test/ELF/Inputs/aarch64-func3-gcs.s
create mode 100644 lld/test/ELF/aarch64-feature-gcs.s
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 33bfa42b0fcbf0..8279cd4aafc16f 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -188,6 +188,8 @@ struct Config {
StringRef zBtiReport = "none";
StringRef zCetReport = "none";
StringRef zPauthReport = "none";
+ StringRef zGcsReport = "none";
+ StringRef zGcs = "implicit";
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 a5b47f020f8726..b5dab029f110e0 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -466,6 +466,10 @@ static void checkOptions() {
error("-z bti-report only supported on AArch64");
if (config->zPauthReport != "none")
error("-z pauth-report only supported on AArch64");
+ if (config->zGcsReport != "none")
+ error("-z gcs-report only supported on AArch64");
+ if (config->zGcs != "implicit")
+ error("-z gcs only supported on AArch64");
}
if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
@@ -560,6 +564,25 @@ static uint8_t getZStartStopVisibility(opt::InputArgList &args) {
return ret;
}
+static StringRef getZGcs(opt::InputArgList &args) {
+ StringRef ret = "implicit";
+ for (auto *arg : args.filtered(OPT_z)) {
+ std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
+ if (kv.first == "gcs") {
+ arg->claim();
+ if (kv.second == "implicit" || kv.second == "always" ||
+ kv.second == "never")
+ ret = kv.second;
+ else if (StringRef(arg->getValue()) == "gcs")
+ // -z gcs is the same as -z gcs=always
+ ret = "always";
+ else
+ error("unknown -z gcs= value: " + StringRef(kv.second));
+ }
+ }
+ return ret;
+}
+
// Report a warning for an unknown -z option.
static void checkZOptions(opt::InputArgList &args) {
// This function is called before getTarget(), when certain options are not
@@ -1436,6 +1459,7 @@ static void readConfigs(opt::InputArgList &args) {
config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true);
config->zForceBti = hasZOption(args, "force-bti");
config->zForceIbt = hasZOption(args, "force-ibt");
+ config->zGcs = getZGcs(args);
config->zGlobal = hasZOption(args, "global");
config->zGnustack = getZGnuStack(args);
config->zHazardplt = hasZOption(args, "hazardplt");
@@ -1508,7 +1532,8 @@ static void readConfigs(opt::InputArgList &args) {
auto reports = {std::make_pair("bti-report", &config->zBtiReport),
std::make_pair("cet-report", &config->zCetReport),
- std::make_pair("pauth-report", &config->zPauthReport)};
+ std::make_pair("pauth-report", &config->zPauthReport),
+ std::make_pair("gcs-report", &config->zGcsReport)};
for (opt::Arg *arg : args.filtered(OPT_z)) {
std::pair<StringRef, StringRef> option =
StringRef(arg->getValue()).split('=');
@@ -2667,6 +2692,11 @@ static void readSecurityNotes() {
toString(f) + ": -z bti-report: file does not have "
"GNU_PROPERTY_AARCH64_FEATURE_1_BTI property");
+ checkAndReportMissingFeature(
+ config->zGcsReport, features, GNU_PROPERTY_AARCH64_FEATURE_1_GCS,
+ toString(f) + ": -z gcs-report: file does not have "
+ "GNU_PROPERTY_AARCH64_FEATURE_1_GCS property");
+
checkAndReportMissingFeature(
config->zCetReport, features, GNU_PROPERTY_X86_FEATURE_1_IBT,
toString(f) + ": -z cet-report: file does not have "
@@ -2719,6 +2749,12 @@ static void readSecurityNotes() {
// Force enable Shadow Stack.
if (config->zShstk)
config->andFeatures |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+
+ // Force enable/disable GCS
+ if (config->zGcs == "always")
+ config->andFeatures |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+ else if (config->zGcs == "never")
+ config->andFeatures &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
}
static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
diff --git a/lld/test/ELF/Inputs/aarch64-func2-gcs.s b/lld/test/ELF/Inputs/aarch64-func2-gcs.s
new file mode 100644
index 00000000000000..4e7e95dcaff7cc
--- /dev/null
+++ b/lld/test/ELF/Inputs/aarch64-func2-gcs.s
@@ -0,0 +1,19 @@
+.section ".note.gnu.property", "a"
+.long 4
+.long 0x10
+.long 0x5
+.asciz "GNU"
+
+.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND
+.long 4
+.long 4 // GNU_PROPERTY_AARCH64_FEATURE_1_GCS
+.long 0
+
+.text
+.globl func2
+.type func2, at function
+func2:
+ .globl func3
+ .type func3, @function
+ bl func3
+ ret
diff --git a/lld/test/ELF/Inputs/aarch64-func3-gcs.s b/lld/test/ELF/Inputs/aarch64-func3-gcs.s
new file mode 100644
index 00000000000000..28460338822cdb
--- /dev/null
+++ b/lld/test/ELF/Inputs/aarch64-func3-gcs.s
@@ -0,0 +1,16 @@
+.section ".note.gnu.property", "a"
+.long 4
+.long 0x10
+.long 0x5
+.asciz "GNU"
+
+.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND
+.long 4
+.long 4 // GNU_PROPERTY_AARCH64_FEATURE_1_GCS
+.long 0
+
+.text
+.globl func3
+.type func3, at function
+func3:
+ ret
diff --git a/lld/test/ELF/aarch64-feature-gcs.s b/lld/test/ELF/aarch64-feature-gcs.s
new file mode 100644
index 00000000000000..31cb3b4cdee9c8
--- /dev/null
+++ b/lld/test/ELF/aarch64-feature-gcs.s
@@ -0,0 +1,66 @@
+# REQUIRES: aarch64
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func2-gcs.s -o %t2.o
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3-gcs.s -o %t3.o
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func2.s -o %t2no.o
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3.s -o %t3no.o
+
+# GCS should be enabled when it's enabled in all inputs or when it's forced on.
+
+# RUN: ld.lld %t1.o %t2.o %t3.o --shared -o %t.exe
+# RUN: llvm-readelf -n %t.exe | FileCheck --check-prefix GCS %s
+# RUN: ld.lld %t1.o %t3.o --shared -o %t.so
+# RUN: llvm-readelf -n %t.so | FileCheck --check-prefix GCS %s
+# RUN: ld.lld %t1.o %t2no.o %t3.o --shared -o %tforce.exe -z gcs
+# RUN: llvm-readelf -n %tforce.exe | FileCheck --allow-empty --check-prefix GCS %s
+# RUN: ld.lld %t2.o %t3no.o --shared -o %tforce.so -z gcs=always
+# RUN: llvm-readelf -n %tforce.so | FileCheck --allow-empty --check-prefix GCS %s
+# RUN: ld.lld %t2.o %t3no.o --shared -o %tforce2.so -z gcs=never -z gcs=always
+# RUN: llvm-readelf -n %tforce2.so | FileCheck --allow-empty --check-prefix GCS %s
+
+# GCS: Properties: aarch64 feature: GCS
+
+# GCS should not be enabled if it's not enabled in at least one input, and we
+# should warn or error when using the report option.
+
+# RUN: ld.lld %t1.o %t2no.o %t3.o --shared -o %tno.exe
+# RUN: llvm-readelf -n %tno.exe | FileCheck --allow-empty --check-prefix NOGCS %s
+# RUN: ld.lld %t2.o %t3no.o --shared -o %tno.so
+# RUN: llvm-readelf -n %tno.so | FileCheck --allow-empty --check-prefix NOGCS %s
+# RUN: ld.lld %t1.o %t2no.o %t3.o --shared -o %tno.exe -z gcs-report=warning 2>&1 | FileCheck --check-prefix=REPORT-WARN %s
+# RUN: llvm-readelf -n %tno.exe | FileCheck --allow-empty --check-prefix NOGCS %s
+# RUN: not ld.lld %t2.o %t3no.o --shared -o %tno.so -z gcs-report=error 2>&1 | FileCheck --check-prefix=REPORT-ERROR %s
+
+# NOGCS-NOT: Properties
+# REPORT-WARN: warning: {{.*}}tmp2no.o: -z gcs-report: file does not have GNU_PROPERTY_AARCH64_FEATURE_1_GCS property
+# REPORT-ERROR: error: {{.*}}tmp3no.o: -z gcs-report: file does not have GNU_PROPERTY_AARCH64_FEATURE_1_GCS property
+
+# GCS should be disabled with gcs=never, even if GCS is present in all inputs.
+
+# RUN: ld.lld %t1.o %t2.o %t3.o -z gcs=never --shared -o %tnever.exe
+# RUN: llvm-readelf -n %tnever.exe | FileCheck --allow-empty --check-prefix NOGCS %s
+# RUN: ld.lld %t1.o %t2.o %t3.o -z gcs=always -z gcs=never --shared -o %tnever2.exe
+# RUN: llvm-readelf -n %tnever2.exe | FileCheck --allow-empty --check-prefix NOGCS %s
+
+# An invalid gcs option should give an error
+# RUN: not ld.lld %t1.o %t2.o %t3.o -z gcs=nonsense -o %tinvalid.exe 2>&1 | FileCheck --check-prefix=INVALID %s
+
+# INVALID: error: unknown -z gcs= value: nonsense
+
+.section ".note.gnu.property", "a"
+.long 4
+.long 0x10
+.long 0x5
+.asciz "GNU"
+
+.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND
+.long 4
+.long 4 // GNU_PROPERTY_AARCH64_FEATURE_1_GCS
+.long 0
+
+.text
+.globl _start
+.type func1,%function
+func1:
+ bl func2
+ ret
More information about the llvm-commits
mailing list