https://github.com/zyedidia created https://github.com/llvm/llvm-project/pull/204689
This adds a new `-mlfi=...` flag to the Clang frontend to control LFI subtarget features. For now that is just `no-loads` and `no-stores`, setting the corresponding `+no-lfi-loads` and `+no-lfi-stores` LLVM subtarget features. Currently this only affects AArch64, but these features will also be supported on the X86 LFI subtarget when it is added so I've implemented this in a target-agnostic location. Clang will also now define the `__LFI_NO_LOADS__` and `__LFI_NO_STORES__` preprocessor macros for these subtarget features, in addition to the existing `__LFI__`. >From a9e0e0a78590d27315bc409c82eac7f749249e48 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia <[email protected]> Date: Thu, 18 Jun 2026 17:58:06 -0400 Subject: [PATCH] Add -mlfi Clang option --- clang/include/clang/Options/Options.td | 5 +++++ clang/lib/Basic/Targets/AArch64.cpp | 3 --- clang/lib/Driver/ToolChains/CommonArgs.cpp | 20 +++++++++++++++++++ clang/lib/Frontend/InitPreprocessor.cpp | 11 +++++++++++ clang/test/Driver/lfi.c | 11 +++++++++++ clang/test/Preprocessor/lfi.c | 13 ++++++++++++ llvm/docs/LFI.rst | 23 +++++++++++++++++++++- 7 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 clang/test/Driver/lfi.c create mode 100644 clang/test/Preprocessor/lfi.c diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 5028684731b2d..58bf45d6cf22d 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -5802,6 +5802,11 @@ def mharden_sls_EQ : Joined<["-"], "mharden-sls=">, Group<m_Group>, " blr(ARM/AArch64), comdat(ARM/AArch64), nocomdat(ARM/AArch64)," " return(X86), indirect-jmp(X86)">; +def mlfi_EQ : CommaJoined<["-"], "mlfi=">, Group<m_Group>, + HelpText<"Configure Lightweight Fault Isolation (LFI) features. <arg> is a" + " comma-separated list of: no-loads, no-stores">, + Values<"no-loads,no-stores">; + def matomics : Flag<["-"], "matomics">, Group<m_wasm_Features_Group>; def mno_atomics : Flag<["-"], "mno-atomics">, Group<m_wasm_Features_Group>; def mbulk_memory : Flag<["-"], "mbulk-memory">, Group<m_wasm_Features_Group>; diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index 9afe6cb10729d..42fd29c2c412f 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -417,9 +417,6 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__aarch64__"); } - if (getTriple().isLFI()) - Builder.defineMacro("__LFI__"); - // Inline assembly supports AArch64 flag outputs. Builder.defineMacro("__GCC_ASM_FLAG_OUTPUTS__"); diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 48724746d9330..f854ce4a60de5 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -925,6 +925,26 @@ void tools::getTargetFeatures(const Driver &D, const llvm::Triple &Triple, break; } + // The mlfi option configures LFI subtarget features. + if (const Arg *A = Args.getLastArg(options::OPT_mlfi_EQ)) { + if (!Triple.isLFI()) { + D.Diag(diag::err_drv_unsupported_opt_for_target) + << A->getSpelling() << Triple.getTriple(); + } else { + for (StringRef Value : A->getValues()) { + StringRef Feature = llvm::StringSwitch<StringRef>(Value) + .Case("no-loads", "+no-lfi-loads") + .Case("no-stores", "+no-lfi-stores") + .Default(""); + if (Feature.empty()) + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getSpelling() << Value; + else + Features.push_back(Feature); + } + } + } + for (auto Feature : unifyTargetFeatures(Features)) { CmdArgs.push_back(IsAux ? "-aux-target-feature" : "-target-feature"); CmdArgs.push_back(Feature.data()); diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index ec009211ec6de..7450a472b8c07 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -1541,6 +1541,17 @@ static void InitializePredefinedMacros(const TargetInfo &TI, // Get other target #defines. TI.getTargetDefines(LangOpts, Builder); + + // Defines for the LFI subtarget specifying enabled subtarget features. + if (TI.getTriple().isLFI()) { + Builder.defineMacro("__LFI__"); + for (StringRef Feature : TI.getTargetOpts().Features) { + if (Feature == "+no-lfi-loads") + Builder.defineMacro("__LFI_NO_LOADS__"); + else if (Feature == "+no-lfi-stores") + Builder.defineMacro("__LFI_NO_STORES__"); + } + } } static void InitializePGOProfileMacros(const CodeGenOptions &CodeGenOpts, diff --git a/clang/test/Driver/lfi.c b/clang/test/Driver/lfi.c new file mode 100644 index 0000000000000..78f35b162e6dc --- /dev/null +++ b/clang/test/Driver/lfi.c @@ -0,0 +1,11 @@ +// Check that -mlfi= lowers LFI configuration to subtarget features. + +// RUN: %clang --target=aarch64_lfi-linux -mlfi=no-loads,no-stores -c %s -### 2>&1 | FileCheck %s +// CHECK-DAG: "-target-feature" "+no-lfi-loads" +// CHECK-DAG: "-target-feature" "+no-lfi-stores" + +// RUN: not %clang --target=aarch64_lfi-linux -mlfi=unknown -c %s -### 2>&1 | FileCheck %s --check-prefix=BAD +// BAD: unsupported argument 'unknown' to option '-mlfi=' + +// RUN: not %clang --target=aarch64-linux -mlfi=no-loads -c %s -### 2>&1 | FileCheck %s --check-prefix=NOLFI +// NOLFI: unsupported option '-mlfi=' for target diff --git a/clang/test/Preprocessor/lfi.c b/clang/test/Preprocessor/lfi.c new file mode 100644 index 0000000000000..42115054190c6 --- /dev/null +++ b/clang/test/Preprocessor/lfi.c @@ -0,0 +1,13 @@ +// Check LFI predefined macros. + +// RUN: %clang --target=aarch64_lfi-linux -E -dM %s -o - | FileCheck %s --implicit-check-not=__LFI_NO +// RUN: %clang --target=aarch64_lfi-linux -mlfi=no-loads,no-stores -E -dM %s -o - | FileCheck %s --check-prefix=CONFIG +// RUN: %clang --target=aarch64-linux -E -dM %s -o - | FileCheck %s --check-prefix=OFF --implicit-check-not=__LFI + +// CHECK: #define __LFI__ 1 + +// CONFIG-DAG: #define __LFI__ 1 +// CONFIG-DAG: #define __LFI_NO_LOADS__ 1 +// CONFIG-DAG: #define __LFI_NO_STORES__ 1 + +// OFF: #define __aarch64__ 1 diff --git a/llvm/docs/LFI.rst b/llvm/docs/LFI.rst index b0f5e31d87ee9..712b7c88d509d 100644 --- a/llvm/docs/LFI.rst +++ b/llvm/docs/LFI.rst @@ -115,7 +115,8 @@ Example: Compiler Options ++++++++++++++++ -The LFI target has several configuration options, specified via ``-mattr=``: +The LFI target has several configuration options. At the LLVM level they are +specified as subtarget features via ``-mattr=``: * ``+no-lfi-loads``: Disable sandboxing for load instructions (stores-only mode). * ``+no-lfi-stores``: Disable sandboxing for store instructions. @@ -128,6 +129,26 @@ read/write outside the sandbox region but may not transfer control outside (e.g., may not execute system calls directly). This is primarily useful in combination with some other form of memory sandboxing, such as Intel MPK. +When driving the compiler with Clang, these same options are exposed through the +``-mlfi=`` flag, which takes a comma-separated list of configuration +knobs and lowers them to the corresponding subtarget features: + +* ``-mlfi=no-loads``. +* ``-mlfi=no-stores``. + +Preprocessor Macros ++++++++++++++++++++ + +When compiling for an LFI target, Clang predefines ``__LFI__``. The enabled LFI +configuration knobs are additionally communicated to the preprocessor so that +source code can adapt to the sandbox configuration. Each macro is defined only +when the corresponding feature is active: + +* ``__LFI_NO_LOADS__``: defined when load sandboxing is disabled + (``-mlfi=no-loads`` / ``+no-lfi-loads``). +* ``__LFI_NO_STORES__``: defined when store sandboxing is disabled + (``-mlfi=no-stores`` / ``+no-lfi-stores``). + AArch64 +++++++ _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
