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 thinLTOObjectSuffixReplace; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 862195098c1aa..c5de522daa177 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -406,6 +406,11 @@ static void checkOptions(Ctx &ctx) { ErrAlways(ctx) << "-z gcs only supported on AArch64"; } + if (ctx.arg.emachine != EM_AARCH64 && ctx.arg.emachine != EM_ARM && + ctx.arg.zExecuteOnlyReport != "none") + ErrAlways(ctx) + << "-z execute-only-report only supported on AArch64 and ARM"; + if (ctx.arg.emachine != EM_PPC64) { if (ctx.arg.tocOptimize) ErrAlways(ctx) << "--toc-optimize is only supported on PowerPC64 targets"; @@ -1620,10 +1625,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("execute-only-report", &ctx.arg.zExecuteOnlyReport), + 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 option = StringRef(arg->getValue()).split('='); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 0d61e8d8d91a4..f249bb198e98d 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -64,6 +64,7 @@ template class Writer { void sortOrphanSections(); void finalizeSections(); void checkExecuteOnly(); + void checkExecuteOnlyReport(); void setReservedSymbolSections(); SmallVector, 0> createPhdrs(Partition &part); @@ -323,6 +324,7 @@ template void Writer::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. @@ -2176,6 +2178,41 @@ template void Writer::checkExecuteOnly() { "data and code"; } +// Check which input sections of RX output sections don't have the +// SHF_AARCH64_PURECODE or SHF_ARM_PURECODE flag set. +template void Writer::checkExecuteOnlyReport() { + if (ctx.arg.zExecuteOnlyReport == "none") + return; + + auto reportUnless = [&](bool cond) -> ELFSyncStream { + if (cond) + return {ctx, DiagLevel::None}; + if (ctx.arg.zExecuteOnlyReport == "error") + return {ctx, DiagLevel::Err}; + if (ctx.arg.zExecuteOnlyReport == "warning") + return {ctx, DiagLevel::Warn}; + return {ctx, DiagLevel::None}; + }; + + uint64_t purecodeFlag = + ctx.arg.emachine == EM_AARCH64 ? SHF_AARCH64_PURECODE : SHF_ARM_PURECODE; + StringRef purecodeFlagName = ctx.arg.emachine == EM_AARCH64 + ? "SHF_AARCH64_PURECODE" + : "SHF_ARM_PURECODE"; + SmallVector storage; + for (OutputSection *osec : ctx.outputSections) { + if (osec->getPhdrFlags() != (PF_R | PF_X)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + if (isa(sec)) + continue; + reportUnless(sec->flags & purecodeFlag) + << "-z execute-only-report: " << sec << " does not have " + << purecodeFlagName << " flag set"; + } + } +} + // The linker is expected to define SECNAME_start and SECNAME_end // symbols for a few sections. This function defines them. template void Writer::addStartEndSymbols() { 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..8a3d56db840d8 --- /dev/null +++ b/lld/test/ELF/aarch64-execute-only-report.s @@ -0,0 +1,44 @@ +// REQUIRES: aarch64 + +// RUN: rm -rf %t && mkdir %t && cd %t +// RUN: llvm-mc --triple=aarch64 --filetype=obj %s -o a.o + +// RUN: ld.lld --defsym absolute=0xf0000000 -z execute-only-report=none --fatal-warnings a.o + +// RUN: ld.lld --defsym absolute=0xf0000000 -z execute-only-report=warning a.o 2>&1 | \ +// RUN: FileCheck --check-prefix=WARNING %s +// RUN: ld.lld --defsym absolute=0xf0000000 --execute-only -z execute-only-report=warning a.o 2>&1 | \ +// RUN: FileCheck --check-prefix=WARNING %s + +// WARNING-NOT: warning: -z execute-only-report: a.o:(.text) does not have SHF_AARCH64_PURECODE flag set +// WARNING-NOT: warning: -z execute-only-report: a.o:(.text.foo) does not have SHF_AARCH64_PURECODE flag set +// WARNING: warning: -z execute-only-report: a.o:(.text.bar) does not have SHF_AARCH64_PURECODE flag set +// WARNING-NOT: warning: -z execute-only-report: :({{.*}}) does not have SHF_AARCH64_PURECODE flag set + +// RUN: not ld.lld --defsym absolute=0xf0000000 -z execute-only-report=error a.o 2>&1 | \ +// RUN: FileCheck --check-prefix=ERROR %s +// RUN: not ld.lld --defsym absolute=0xf0000000 --execute-only -z execute-only-report=error a.o 2>&1 | \ +// RUN: FileCheck --check-prefix=ERROR %s + +// ERROR-NOT: error: -z execute-only-report: a.o:(.text) does not have SHF_AARCH64_PURECODE flag set +// ERROR-NOT: error: -z execute-only-report: a.o:(.text.foo) does not have SHF_AARCH64_PURECODE flag set +// ERROR: error: -z execute-only-report: a.o:(.text.bar) does not have SHF_AARCH64_PURECODE flag set +// ERROR-NOT: error: -z execute-only-report: :({{.*}}) does not have SHF_AARCH64_PURECODE flag set + +.section .text,"axy",@progbits,unique,0 +.globl _start +_start: + bl foo + bl bar + bl absolute + ret + +.section .text.foo,"axy",@progbits,unique,0 +.globl foo +foo: + ret + +.section .text.bar,"ax",@progbits,unique,0 +.globl bar +bar: + ret diff --git a/lld/test/ELF/arm-execute-only-report.s b/lld/test/ELF/arm-execute-only-report.s new file mode 100644 index 0000000000000..869aded71fb6f --- /dev/null +++ b/lld/test/ELF/arm-execute-only-report.s @@ -0,0 +1,40 @@ +// REQUIRES: arm + +// RUN: rm -rf %t && mkdir %t && cd %t +// RUN: llvm-mc --triple=armv7 --filetype=obj %s -o a.o + +// RUN: ld.lld --defsym absolute=0xf0000000 -z execute-only-report=none --fatal-warnings a.o + +// RUN: ld.lld --defsym absolute=0xf0000000 -z execute-only-report=warning a.o 2>&1 | \ +// RUN: FileCheck --check-prefix=WARNING %s + +// WARNING-NOT: warning: -z execute-only-report: a.o:(.text) does not have SHF_ARM_PURECODE flag set +// WARNING-NOT: warning: -z execute-only-report: a.o:(.text.foo) does not have SHF_ARM_PURECODE flag set +// WARNING: warning: -z execute-only-report: a.o:(.text.bar) does not have SHF_ARM_PURECODE flag set +// WARNING-NOT: warning: -z execute-only-report: :({{.*}}) does not have SHF_ARM_PURECODE flag set + +// RUN: not ld.lld --defsym absolute=0xf0000000 -z execute-only-report=error a.o 2>&1 | \ +// RUN: FileCheck --check-prefix=ERROR %s + +// ERROR-NOT: error: -z execute-only-report: a.o:(.text) does not have SHF_ARM_PURECODE flag set +// ERROR-NOT: error: -z execute-only-report: a.o:(.text.foo) does not have SHF_ARM_PURECODE flag set +// ERROR: error: -z execute-only-report: a.o:(.text.bar) does not have SHF_ARM_PURECODE flag set +// ERROR-NOT: error: -z execute-only-report: :({{.*}}) does not have SHF_ARM_PURECODE flag set + +.section .text,"axy",%progbits,unique,0 +.globl _start +_start: + bl foo + bl bar + bl absolute + bx lr + +.section .text.foo,"axy",%progbits,unique,0 +.globl foo +foo: + bx lr + +.section .text.bar,"ax",%progbits,unique,0 +.globl bar +bar: + bx lr diff --git a/lld/test/ELF/target-specific-options.s b/lld/test/ELF/target-specific-options.s index 0f126f0186f8b..9d57776fcfbef 100644 --- a/lld/test/ELF/target-specific-options.s +++ b/lld/test/ELF/target-specific-options.s @@ -13,5 +13,15 @@ # RUN: not ld.lld %t --toc-optimize -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR-TOC # ERR-TOC: error: --toc-optimize is only supported on PowerPC64 targets +# RUN: not ld.lld %t -z execute-only-report=warning -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR-EXECUTE-ONLY +# RUN: not ld.lld %t -z execute-only-report=error -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR-EXECUTE-ONLY +# ERR-EXECUTE-ONLY: error: -z execute-only-report only supported on AArch64 and ARM + +# RUN: not ld.lld %t -z execute-only-report=foo -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR-EXECUTE-ONLY-INVALID +# ERR-EXECUTE-ONLY-INVALID: error: unknown -z execute-only-report= value: foo + .globl _start _start: