[lld][AArch64] Add support for GCS (#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.

This is so that there's a means of explicitly disabling GCS even when
all input objects have the GCS bit set.
This commit is contained in:
John Brawn
2024-05-21 17:34:17 +01:00
committed by GitHub
parent 446f66d685
commit cfeb25cd7e
3 changed files with 175 additions and 0 deletions

View File

@@ -102,6 +102,9 @@ enum class GnuStackKind { None, Exec, NoExec };
// For --lto=
enum LtoKind : uint8_t {UnifiedThin, UnifiedRegular, Default};
// For -z gcs=
enum class GcsPolicy { Implicit, Never, Always };
struct SymbolVersion {
llvm::StringRef name;
bool isExternCpp;
@@ -188,6 +191,7 @@ 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;
@@ -341,6 +345,7 @@ struct Config {
UnresolvedPolicy unresolvedSymbols;
UnresolvedPolicy unresolvedSymbolsInShlib;
Target2Policy target2;
GcsPolicy zGcs;
bool power10Stubs;
ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
BuildIdKind buildId = BuildIdKind::None;

View File

@@ -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 != GcsPolicy::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 GcsPolicy getZGcs(opt::InputArgList &args) {
GcsPolicy ret = GcsPolicy::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")
ret = GcsPolicy::Implicit;
else if (kv.second == "never")
ret = GcsPolicy::Never;
else if (kv.second == "always")
ret = GcsPolicy::Always;
else
error("unknown -z gcs= value: " + 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
@@ -1438,6 +1461,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");
@@ -1510,6 +1534,7 @@ 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("gcs-report", &config->zGcsReport),
std::make_pair("pauth-report", &config->zPauthReport)};
for (opt::Arg *arg : args.filtered(OPT_z)) {
std::pair<StringRef, StringRef> option =
@@ -2677,6 +2702,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 "
@@ -2729,6 +2759,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 == GcsPolicy::Always)
config->andFeatures |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
else if (config->zGcs == GcsPolicy::Never)
config->andFeatures &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
}
static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {

View File

@@ -0,0 +1,134 @@
# REQUIRES: aarch64
# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu func1-gcs.s -o func1-gcs.o
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu func2.s -o func2.o
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu func2-gcs.s -o func2-gcs.o
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu func3.s -o func3.o
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu func3-gcs.s -o func3-gcs.o
## GCS should be enabled when it's enabled in all inputs or when it's forced on.
# RUN: ld.lld func1-gcs.o func2-gcs.o func3-gcs.o -o gcs
# RUN: llvm-readelf -n gcs | FileCheck --check-prefix GCS %s
# RUN: ld.lld func1-gcs.o func3-gcs.o --shared -o gcs.so
# RUN: llvm-readelf -n gcs.so | FileCheck --check-prefix GCS %s
# RUN: ld.lld func1-gcs.o func2.o func3-gcs.o -o force-gcs -z gcs=always
# RUN: llvm-readelf -n force-gcs | FileCheck --check-prefix GCS %s
# RUN: ld.lld func2-gcs.o func3.o --shared -o force-gcs.so -z gcs=always
# RUN: llvm-readelf -n force-gcs.so | FileCheck --check-prefix GCS %s
# RUN: ld.lld func2-gcs.o func3.o --shared -o force-gcs2.so -z gcs=never -z gcs=always
# RUN: llvm-readelf -n force-gcs2.so | FileCheck --check-prefix GCS %s
# GCS: Properties: aarch64 feature: GCS
## GCS should not be enabled if it's not enabled in at least one input.
# RUN: ld.lld func1-gcs.o func2.o func3-gcs.o -o no-gcs
# RUN: llvm-readelf -n no-gcs | count 0
# RUN: ld.lld func2-gcs.o func3.o --shared -o no-gcs.so
## GCS should be disabled with gcs=never, even if GCS is present in all inputs.
# RUN: ld.lld func1-gcs.o func2-gcs.o func3-gcs.o -z gcs=never -o never-gcs
# RUN: llvm-readelf -n never-gcs | count 0
# RUN: ld.lld func1-gcs.o func2-gcs.o func3-gcs.o -z gcs=always -z gcs=never -o never-gcs2
# RUN: llvm-readelf -n never-gcs2 | count 0
## gcs-report should report any input files that don't have the gcs property.
# RUN: ld.lld func1-gcs.o func2.o func3-gcs.o -o /dev/null -z gcs-report=warning 2>&1 | FileCheck --check-prefix=REPORT-WARN %s
# RUN: ld.lld func1-gcs.o func2.o func3-gcs.o -o /dev/null -z gcs-report=warning -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-WARN %s
# RUN: ld.lld func1-gcs.o func2.o func3-gcs.o -o /dev/null -z gcs-report=warning -z gcs=never 2>&1 | FileCheck --check-prefix=REPORT-WARN %s
# RUN: not ld.lld func2-gcs.o func3.o --shared -o /dev/null -z gcs-report=error 2>&1 | FileCheck --check-prefix=REPORT-ERROR %s
# RUN: not ld.lld func2-gcs.o func3.o --shared -o /dev/null -z gcs-report=error -z gcs=always 2>&1 | FileCheck --check-prefix=REPORT-ERROR %s
# RUN: not ld.lld func2-gcs.o func3.o --shared -o /dev/null -z gcs-report=error -z gcs=never 2>&1 | FileCheck --check-prefix=REPORT-ERROR %s
# RUN: ld.lld func1-gcs.o func2-gcs.o func3-gcs.o -o /dev/null -z gcs-report=warning 2>&1 | count 0
# RUN: ld.lld func1-gcs.o func2-gcs.o func3-gcs.o -o /dev/null -z gcs-report=warning -z gcs=always 2>&1 | count 0
# RUN: ld.lld func1-gcs.o func2-gcs.o func3-gcs.o -o /dev/null -z gcs-report=warning -z gcs=never 2>&1 | count 0
# 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
## 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
# INVALID: error: unknown -z gcs= value: nonsense
#--- func1-gcs.s
.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
#--- func2.s
.text
.globl func2
.type func2,@function
func2:
.globl func3
.type func3, @function
bl func3
ret
#--- func2-gcs.s
.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,@function
func2:
.globl func3
.type func3, @function
bl func3
ret
#--- func3.s
.text
.globl func3
.type func3,@function
func3:
ret
#--- func3-gcs.s
.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,@function
func3:
ret