Skip to content

[LLD][ELF][RISCV][Zicfilp][Zicfiss] Support -z <zicfilp-unlabeled|zicfilp-func-sig|zicfiss>-report-dynamic=<none|warning|error> options #144304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,11 @@ struct Config {
ReportPolicy zGcsReportDynamic = ReportPolicy::None;
ReportPolicy zExecuteOnlyReport = ReportPolicy::None;
ReportPolicy zZicfilpUnlabeledReport = ReportPolicy::None;
ReportPolicy zZicfilpUnlabeledReportDynamic = ReportPolicy::None;
ReportPolicy zZicfilpFuncSigReport = ReportPolicy::None;
ReportPolicy zZicfilpFuncSigReportDynamic = ReportPolicy::None;
ReportPolicy zZicfissReport = ReportPolicy::None;
ReportPolicy zZicfissReportDynamic = ReportPolicy::None;
bool ltoBBAddrMap;
llvm::StringRef ltoBasicBlockSections;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
Expand Down
173 changes: 134 additions & 39 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdlib>
#include <tuple>
#include <utility>
Expand Down Expand Up @@ -425,11 +426,20 @@ static void checkOptions(Ctx &ctx) {
if (ctx.arg.zZicfilpUnlabeledReport != ReportPolicy::None)
ErrAlways(ctx) << "-z zicfilip-unlabeled-report is only supported on "
"RISC-V targets";
if (ctx.arg.zZicfilpUnlabeledReportDynamic != ReportPolicy::None)
ErrAlways(ctx) << "-z zicfilip-unlabeled-report-dynamic is only supported"
" on RISC-V targets";
if (ctx.arg.zZicfilpFuncSigReport != ReportPolicy::None)
ErrAlways(ctx) << "-z zicfilip-func-sig-report is only supported on "
"RISC-V targets";
if (ctx.arg.zZicfilpFuncSigReportDynamic != ReportPolicy::None)
ErrAlways(ctx) << "-z zicfilip-func-sig-report-dynamic is only supported "
"on RISC-V targets";
if (ctx.arg.zZicfissReport != ReportPolicy::None)
ErrAlways(ctx) << "-z zicfiss-report is only supported on RISC-V targets";
if (ctx.arg.zZicfissReportDynamic != ReportPolicy::None)
ErrAlways(ctx) << "-z zicfiss-report-dynamic is only supported on RISC-V "
"targets";
if (ctx.arg.zZicfilp != ZicfilpPolicy::Implicit)
ErrAlways(ctx) << "-z zicfilp is only supported on RISC-V targets";
if (ctx.arg.zZicfiss != ZicfissPolicy::Implicit)
Expand Down Expand Up @@ -1686,45 +1696,90 @@ 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("execute-only-report", &ctx.arg.zExecuteOnlyReport),
std::make_pair("gcs-report", &ctx.arg.zGcsReport),
std::make_pair("gcs-report-dynamic", &ctx.arg.zGcsReportDynamic),
std::make_pair("pauth-report", &ctx.arg.zPauthReport),
std::make_pair("zicfilp-unlabeled-report",
&ctx.arg.zZicfilpUnlabeledReport),
std::make_pair("zicfilp-func-sig-report", &ctx.arg.zZicfilpFuncSigReport),
std::make_pair("zicfiss-report", &ctx.arg.zZicfissReport)};
bool hasGcsReportDynamic = false;
bool hasZicfilpUnlabeledReportDynamic = false;
bool hasZicfilpFuncSigReportDynamic = false;
bool hasZicfissReportDynamic = false;
struct ReportOptDesc {
const StringRef name;
ReportPolicy &policy;
bool *const seen;

ReportOptDesc(const StringRef name, ReportPolicy &policy, bool *seen)
: name(name), policy(policy), seen(seen) {}
};
// Sort the descriptions by name according to StringRef::compare() so we can
// binary search it
ReportOptDesc reports[] = {
{{"bti-report"}, ctx.arg.zBtiReport, nullptr},
{{"cet-report"}, ctx.arg.zCetReport, nullptr},
{{"execute-only-report"}, ctx.arg.zExecuteOnlyReport, nullptr},
{{"gcs-report"}, ctx.arg.zGcsReport, nullptr},
{{"gcs-report-dynamic"}, ctx.arg.zGcsReportDynamic, &hasGcsReportDynamic},
{{"pauth-report"}, ctx.arg.zPauthReport, nullptr},
{{"zicfilp-func-sig-report"}, ctx.arg.zZicfilpFuncSigReport, nullptr},
{{"zicfilp-func-sig-report-dynamic"},
ctx.arg.zZicfilpFuncSigReportDynamic,
&hasZicfilpFuncSigReportDynamic},
{{"zicfilp-unlabeled-report"}, ctx.arg.zZicfilpUnlabeledReport, nullptr},
{{"zicfilp-unlabeled-report-dynamic"},
ctx.arg.zZicfilpUnlabeledReportDynamic,
&hasZicfilpUnlabeledReportDynamic},
{{"zicfiss-report"}, ctx.arg.zZicfissReport, nullptr},
{{"zicfiss-report-dynamic"},
ctx.arg.zZicfissReportDynamic,
&hasZicfissReportDynamic}};
for (opt::Arg *arg : args.filtered(OPT_z)) {
std::pair<StringRef, StringRef> option =
StringRef(arg->getValue()).split('=');
for (auto reportArg : reports) {
if (option.first != reportArg.first)
continue;
arg->claim();
if (option.second == "none")
*reportArg.second = ReportPolicy::None;
else if (option.second == "warning")
*reportArg.second = ReportPolicy::Warning;
else if (option.second == "error")
*reportArg.second = ReportPolicy::Error;
else {
ErrAlways(ctx) << "unknown -z " << reportArg.first
<< "= value: " << option.second;
continue;
}
hasGcsReportDynamic |= option.first == "gcs-report-dynamic";
ReportOptDesc *const desc = std::lower_bound(
std::begin(reports), std::end(reports), option.first,
[](const ReportOptDesc &d, const StringRef &s) { return d.name < s; });
if (desc == std::end(reports) || desc->name != option.first)
continue;

arg->claim();
if (option.second == "none")
desc->policy = ReportPolicy::None;
else if (option.second == "warning")
desc->policy = ReportPolicy::Warning;
else if (option.second == "error")
desc->policy = ReportPolicy::Error;
else {
ErrAlways(ctx) << "unknown -z " << desc->name
<< "= value: " << option.second;
continue;
}
if (desc->seen)
*desc->seen = true;
}

// When -zgcs-report-dynamic is unspecified, it inherits -zgcs-report
// but is capped at warning to avoid needing to rebuild the shared library
// with GCS enabled.
if (!hasGcsReportDynamic && ctx.arg.zGcsReport != ReportPolicy::None)
ctx.arg.zGcsReportDynamic = ReportPolicy::Warning;
struct ReportDynamicOptDesc {
ReportPolicy &dynamicPolicy;
const ReportPolicy objectPolicy;
const bool seenDynamicPolicy;

ReportDynamicOptDesc(ReportPolicy &dynamicPolicy,
const ReportPolicy objectPolicy,
const bool seenDynamicPolicy)
: dynamicPolicy(dynamicPolicy), objectPolicy(objectPolicy),
seenDynamicPolicy(seenDynamicPolicy) {}
};
const ReportDynamicOptDesc reportDynamics[] = {
{ctx.arg.zGcsReportDynamic, ctx.arg.zGcsReport, hasGcsReportDynamic},
{ctx.arg.zZicfilpUnlabeledReportDynamic, ctx.arg.zZicfilpUnlabeledReport,
hasZicfilpUnlabeledReportDynamic},
{ctx.arg.zZicfilpFuncSigReportDynamic, ctx.arg.zZicfilpFuncSigReport,
hasZicfilpFuncSigReportDynamic},
{ctx.arg.zZicfissReportDynamic, ctx.arg.zZicfissReport,
hasZicfissReportDynamic}};
for (const ReportDynamicOptDesc &desc : reportDynamics) {
// When -z xxx-report-dynamic is unspecified, it inherits -z xxx-report
// but is capped at warning to avoid needing to rebuild the shared library
// with XXX enabled.
if (!desc.seenDynamicPolicy && desc.objectPolicy != ReportPolicy::None)
desc.dynamicPolicy = ReportPolicy::Warning;
}

for (opt::Arg *arg : args.filtered(OPT_compress_sections)) {
SmallVector<StringRef, 0> fields;
Expand Down Expand Up @@ -3066,13 +3121,15 @@ static void readSecurityNotes(Ctx &ctx) {
ctx.arg.andFeatures &= ~GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS;
}

// 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)
// If we are utilising AArch64 GCS/RISC-V ZICFILP-unlabeled/RISC-V
// ZICFILP-func-sig/RISC-V ZICFISS at any stage, the sharedFiles should be
// checked to ensure they also support this feature. The -z xxx-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 -z xxx-report
// is set to either `warning` or `error`.
for (SharedFile *f : ctx.sharedFiles) {
if (ctx.arg.emachine == EM_AARCH64 &&
(ctx.arg.andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
reportUnless(ctx.arg.zGcsReportDynamic,
f->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
<< f
Expand All @@ -3081,6 +3138,44 @@ static void readSecurityNotes(Ctx &ctx) {
<< "dynamic loader might not enable GCS or refuse to load the "
"program unless all shared library "
<< "dependencies have the GCS marking.";

if (ctx.arg.emachine == EM_RISCV) {
if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
reportUnless(ctx.arg.zZicfilpUnlabeledReportDynamic,
f->andFeatures &
GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
<< f << ": " << "ZICFILP-unlabeled"
<< " is enabled, but this shared library lacks the necessary "
"property note. The dynamic loader might not enable "
<< "ZICFILP-unlabeled"
<< " or refuse to load the program unless all shared library "
"dependencies have the "
<< "ZICFILP-unlabeled" << " marking.";

if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG)
reportUnless(ctx.arg.zZicfilpFuncSigReportDynamic,
f->andFeatures &
GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG)
<< f << ": " << "ZICFILP-func-sig"
<< " is enabled, but this shared library lacks the necessary "
"property note. The dynamic loader might not enable "
<< "ZICFILP-func-sig"
<< " or refuse to load the program unless all shared library "
"dependencies have the "
<< "ZICFILP-func-sig" << " marking.";

if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)
reportUnless(ctx.arg.zZicfissReportDynamic,
f->andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)
<< f << ": " << "ZICFISS"
<< " is enabled, but this shared library lacks the necessary "
"property note. The dynamic loader might not enable "
<< "ZICFISS"
<< " or refuse to load the program unless all shared library "
"dependencies have the "
<< "ZICFISS" << " marking.";
}
}
}

static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
Expand Down
14 changes: 11 additions & 3 deletions lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1450,8 +1450,17 @@ std::vector<uint32_t> SharedFile::parseVerneed(const ELFFile<ELFT> &obj,
// readGnuProperty, but we don't have the InputSection information.
template <typename ELFT>
void SharedFile::parseGnuAndFeatures(const ELFFile<ELFT> &obj) {
if (ctx.arg.emachine != EM_AARCH64)
unsigned featureAndType;
switch (ctx.arg.emachine) {
case EM_AARCH64:
featureAndType = GNU_PROPERTY_AARCH64_FEATURE_1_AND;
break;
case EM_RISCV:
featureAndType = GNU_PROPERTY_RISCV_FEATURE_1_AND;
break;
default:
return;
}
const uint8_t *base = obj.base();
auto phdrs = CHECK2(obj.program_headers(), this);
for (auto phdr : phdrs) {
Expand All @@ -1463,8 +1472,7 @@ void SharedFile::parseGnuAndFeatures(const ELFFile<ELFT> &obj) {
continue;

ArrayRef<uint8_t> desc = note.getDesc(phdr.p_align);
parseGnuPropertyNote<ELFT>(ctx, *this, GNU_PROPERTY_AARCH64_FEATURE_1_AND,
desc, base);
parseGnuPropertyNote<ELFT>(ctx, *this, featureAndType, desc, base);
}
}

Expand Down
17 changes: 16 additions & 1 deletion lld/test/ELF/riscv-feature-zicfilp-func-sig.s
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,24 @@
# REPORT-WARN: warning: f2.o: -z zicfilp-func-sig-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG property
# REPORT-ERROR: error: f3.o: -z zicfilp-func-sig-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG property

## zicfilp-func-sig-report-dynamic should report any dynamic objects that does
## not have the ZICFILP-func-sig property. This also ensures the inhertance from
## zicfilp-func-sig-report is working correctly.
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-func-sig-report=warning -z zicfilp=func-sig 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-func-sig-report=error -z zicfilp=func-sig 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-func-sig-report-dynamic=none -z zicfilp=func-sig 2>&1 | count 0
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-func-sig-report-dynamic=warning -z zicfilp=func-sig 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
# RUN: not ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-func-sig-report-dynamic=error -z zicfilp=func-sig 2>&1 | FileCheck --check-prefix=REPORT-ERROR-DYNAMIC %s
# RUN: ld.lld f1-s.o f3-s.o out.force.so -z zicfilp-func-sig-report-dynamic=error -z zicfilp=func-sig 2>&1 | count 0
# REPORT-WARN-DYNAMIC: warning: out.no.so: ZICFILP-func-sig is enabled, but this shared library lacks the necessary property note. The dynamic loader might not enable ZICFILP-func-sig or refuse to load the program unless all shared library dependencies have the ZICFILP-func-sig marking.
# REPORT-WARN-DYNAMIC-NOT: {{.}}
# REPORT-ERROR-DYNAMIC: error: out.no.so: ZICFILP-func-sig is enabled, but this shared library lacks the necessary property note. The dynamic loader might not enable ZICFILP-func-sig or refuse to load the program unless all shared library dependencies have the ZICFILP-func-sig marking.
# REPORT-ERROR-DYNAMIC-NOT: error:

## An invalid -z zicfilp-func-sig-report option should give an error
# RUN: not ld.lld f2-s.o -z zicfilp-func-sig-report=x 2>&1 | FileCheck --check-prefix=INVALID %s
# RUN: not ld.lld f2-s.o -z zicfilp-func-sig-report=x -z zicfilp-func-sig-report-dynamic=x 2>&1 | FileCheck --check-prefix=INVALID %s
# INVALID: error: unknown -z zicfilp-func-sig-report= value: x
# INVALID: error: unknown -z zicfilp-func-sig-report-dynamic= value: x

## ZICFILP-unlabeled and ZICFILP-func-sig should conflict with each other.
# RUN: ld.lld f3-u.o -o out.override -z zicfilp=func-sig 2>&1 | FileCheck --check-prefix=FORCE-CONFLICT %s
Expand Down
17 changes: 16 additions & 1 deletion lld/test/ELF/riscv-feature-zicfilp-unlabeled.s
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,25 @@
# REPORT-WARN: warning: f2.o: -z zicfilp-unlabeled-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED property
# REPORT-ERROR: error: f3.o: -z zicfilp-unlabeled-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED property

## zicfilp-unlabeled-report-dynamic should report any dynamic objects that does
## not have the ZICFILP-unlabeled property. This also ensures the inhertance
## from zicfilp-unlabeled-report is working correctly.
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-unlabeled-report=warning -z zicfilp=unlabeled 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-unlabeled-report=error -z zicfilp=unlabeled 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-unlabeled-report-dynamic=none -z zicfilp=unlabeled 2>&1 | count 0
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-unlabeled-report-dynamic=warning -z zicfilp=unlabeled 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
# RUN: not ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfilp-unlabeled-report-dynamic=error -z zicfilp=unlabeled 2>&1 | FileCheck --check-prefix=REPORT-ERROR-DYNAMIC %s
# RUN: ld.lld f1-s.o f3-s.o out.force.so -z zicfilp-unlabeled-report-dynamic=error -z zicfilp=unlabeled 2>&1 | count 0
# REPORT-WARN-DYNAMIC: warning: out.no.so: ZICFILP-unlabeled is enabled, but this shared library lacks the necessary property note. The dynamic loader might not enable ZICFILP-unlabeled or refuse to load the program unless all shared library dependencies have the ZICFILP-unlabeled marking.
# REPORT-WARN-DYNAMIC-NOT: {{.}}
# REPORT-ERROR-DYNAMIC: error: out.no.so: ZICFILP-unlabeled is enabled, but this shared library lacks the necessary property note. The dynamic loader might not enable ZICFILP-unlabeled or refuse to load the program unless all shared library dependencies have the ZICFILP-unlabeled marking.
# REPORT-ERROR-DYNAMIC-NOT: error:

## An invalid -z zicfilp-unlabeled-report option should give an error
# RUN: not ld.lld f2-s.o -z zicfilp=x -z zicfilp-unlabeled-report=x 2>&1 | FileCheck --check-prefix=INVALID %s
# RUN: not ld.lld f2-s.o -z zicfilp=x -z zicfilp-unlabeled-report=x -z zicfilp-unlabeled-report-dynamic=x 2>&1 | FileCheck --check-prefix=INVALID %s
# INVALID: error: unknown -z zicfilp= value: x
# INVALID: error: unknown -z zicfilp-unlabeled-report= value: x
# INVALID: error: unknown -z zicfilp-unlabeled-report-dynamic= value: x

## ZICFILP-unlabeled and ZICFILP-func-sig should conflict with each other
# RUN: not ld.lld f1-c.o 2>&1 | FileCheck --check-prefix=CONFLICT %s
Expand Down
17 changes: 16 additions & 1 deletion lld/test/ELF/riscv-feature-zicfiss.s
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,25 @@
# REPORT-WARN: warning: f2.o: -z zicfiss-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS property
# REPORT-ERROR: error: f3.o: -z zicfiss-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS property

## zicfiss-report-dynamic should report any dynamic objects that does not have
## the ZICFISS property. This also ensures the inhertance from zicfiss-report
## is working correctly.
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfiss-report=warning -z zicfiss=always 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfiss-report=error -z zicfiss=always 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfiss-report-dynamic=none -z zicfiss=always 2>&1 | count 0
# RUN: ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfiss-report-dynamic=warning -z zicfiss=always 2>&1 | FileCheck --check-prefix=REPORT-WARN-DYNAMIC %s
# RUN: not ld.lld f1-s.o f3-s.o out.no.so out.force.so -z zicfiss-report-dynamic=error -z zicfiss=always 2>&1 | FileCheck --check-prefix=REPORT-ERROR-DYNAMIC %s
# RUN: ld.lld f1-s.o f3-s.o out.force.so -z zicfiss-report-dynamic=error -z zicfiss=always 2>&1 | count 0
# REPORT-WARN-DYNAMIC: warning: out.no.so: ZICFISS is enabled, but this shared library lacks the necessary property note. The dynamic loader might not enable ZICFISS or refuse to load the program unless all shared library dependencies have the ZICFISS marking.
# REPORT-WARN-DYNAMIC-NOT: {{.}}
# REPORT-ERROR-DYNAMIC: error: out.no.so: ZICFISS is enabled, but this shared library lacks the necessary property note. The dynamic loader might not enable ZICFISS or refuse to load the program unless all shared library dependencies have the ZICFISS marking.
# REPORT-ERROR-DYNAMIC-NOT: error:

## An invalid -z zicfiss-report option should give an error
# RUN: not ld.lld f2-s.o f3-s.o -z zicfiss=x -z zicfiss-report=x 2>&1 | FileCheck --check-prefix=INVALID %s
# RUN: not ld.lld f2-s.o f3-s.o -z zicfiss=x -z zicfiss-report=x -z zicfiss-report-dynamic=x 2>&1 | FileCheck --check-prefix=INVALID %s
# INVALID: error: unknown -z zicfiss= value: x
# INVALID: error: unknown -z zicfiss-report= value: x
# INVALID: error: unknown -z zicfiss-report-dynamic= value: x

#--- rv32-f1-s.s
.section ".note.gnu.property", "a"
Expand Down