-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[RISCV][Zicfilp] Enable Zicfilp CFI compiler behaviors by looking at module flags #152121
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
base: main
Are you sure you want to change the base?
Conversation
Expected Behavior: Stop codegening LPAD insns by testing if the target has the Zicfilp extension and instead, codegen LPAD insns if the LLVM module has all of these flags: + `cf-protection-branch`: Needs to be a non-zero integer (which means `true`) + `cf-branch-label-scheme`: Needs to be `unlabeled` Context: In clang, Zicfilp-based control flow integrity (the `unlabeled` scheme) can now be enabled by giving the `-fcf-protection=branch -mcf-branch-label-scheme=unlabeled` options. With these options, the clang frontend adds the above-mentioned flags to LLVM modules. Here we want to align LPAD insn codegen to be enabled by the semantics of those LLVM module flags, instead of relying on the inaccurate indicator of whether the Zicfilp extension is available, so the toolchain's behavior is more streamlined and expected. Also, since LPAD insns can be executed regardless of whether Zicfilp is available in target or not (due to LPAD insn being encoded as a standard hint insn), clang accepts the above-mentioned CLI options even if Zicfilp is not enabled and expects backend to still generate LPAD insns. This patch enables LPAD insn generation in such cases.
@llvm/pr-subscribers-llvm-support @llvm/pr-subscribers-backend-risc-v Author: Ming-Yi Lai (mylai-mtk) ChangesExpected Behavior: Stop codegening LPAD insns by testing if the target has the Zicfilp extension and instead, codegen LPAD insns if the LLVM module has all of these flags:
Context: In clang, Zicfilp-based control flow integrity (the Also, since LPAD insns can be executed regardless of whether Zicfilp is available in target or not (due to LPAD insn being encoded as a standard hint insn), clang accepts the above-mentioned CLI options even if Zicfilp is not enabled and expects backend to still generate LPAD insns. This patch enables LPAD insn generation in such cases. Full diff: https://github.com/llvm/llvm-project/pull/152121.diff 3 Files Affected:
diff --git a/llvm/include/llvm/Support/RISCVISAUtils.h b/llvm/include/llvm/Support/RISCVISAUtils.h
index 165bb08d66431..e2dd833de74fd 100644
--- a/llvm/include/llvm/Support/RISCVISAUtils.h
+++ b/llvm/include/llvm/Support/RISCVISAUtils.h
@@ -14,7 +14,9 @@
#define LLVM_SUPPORT_RISCVISAUTILS_H
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Compiler.h"
+#include <cassert>
#include <map>
#include <string>
@@ -43,6 +45,23 @@ struct ExtensionComparator {
typedef std::map<std::string, ExtensionVersion, ExtensionComparator>
OrderedExtensionMap;
+enum class ZicfilpLabelSchemeKind {
+ Invalid,
+ Unlabeled,
+ FuncSig,
+};
+
+// See clang::getCFBranchLabelSchemeFlagVal() for possible CFBranchLabelScheme
+inline ZicfilpLabelSchemeKind
+getZicfilpLabelScheme(const StringRef CFBranchLabelScheme) {
+ const auto Ret = StringSwitch<ZicfilpLabelSchemeKind>(CFBranchLabelScheme)
+ .Case("unlabeled", ZicfilpLabelSchemeKind::Unlabeled)
+ .Case("func-sig", ZicfilpLabelSchemeKind::FuncSig)
+ .Default(ZicfilpLabelSchemeKind::Invalid);
+ assert(Ret != ZicfilpLabelSchemeKind::Invalid);
+ return Ret;
+}
+
} // namespace RISCVISAUtils
} // namespace llvm
diff --git a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
index 43621b8f0f33d..554f7d5494731 100644
--- a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
+++ b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
@@ -19,11 +19,14 @@
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/RISCVISAUtils.h"
#define DEBUG_TYPE "riscv-indrect-branch-tracking"
#define PASS_NAME "RISC-V Indirect Branch Tracking"
using namespace llvm;
+using namespace llvm::RISCVISAUtils;
cl::opt<uint32_t> PreferredLandingPadLabel(
"riscv-landing-pad-label", cl::ReallyHidden,
@@ -64,9 +67,25 @@ static void emitLpad(MachineBasicBlock &MBB, const RISCVInstrInfo *TII,
bool RISCVIndirectBranchTracking::runOnMachineFunction(MachineFunction &MF) {
const auto &Subtarget = MF.getSubtarget<RISCVSubtarget>();
const RISCVInstrInfo *TII = Subtarget.getInstrInfo();
- if (!Subtarget.hasStdExtZicfilp())
+
+ const Module *const M = MF.getFunction().getParent();
+ if (!M)
+ return false;
+ if (const Metadata *const Flag = M->getModuleFlag("cf-protection-branch");
+ !Flag || mdconst::extract<ConstantInt>(Flag)->isZero())
return false;
+ StringRef CFBranchLabelScheme;
+ if (const Metadata *const MD = M->getModuleFlag("cf-branch-label-scheme"))
+ CFBranchLabelScheme = cast<MDString>(MD)->getString();
+ else
+ report_fatal_error("missing cf-branch-label-scheme module flag");
+
+ const ZicfilpLabelSchemeKind Scheme =
+ getZicfilpLabelScheme(CFBranchLabelScheme);
+ if (Scheme != ZicfilpLabelSchemeKind::Unlabeled)
+ report_fatal_error("unsupported cf-branch-label-scheme module flag");
+
uint32_t FixedLabel = 0;
if (PreferredLandingPadLabel.getNumOccurrences() > 0) {
if (!isUInt<20>(PreferredLandingPadLabel))
diff --git a/llvm/test/CodeGen/RISCV/lpad.ll b/llvm/test/CodeGen/RISCV/lpad.ll
index 93eda6f10eedb..f69b4dad068b5 100644
--- a/llvm/test/CodeGen/RISCV/lpad.ll
+++ b/llvm/test/CodeGen/RISCV/lpad.ll
@@ -289,3 +289,8 @@ define void @interrupt() "interrupt"="machine" {
; FIXED-ONE-NEXT: mret
ret void
}
+
+!llvm.module.flags = !{!0, !1}
+
+!0 = !{i32 8, !"cf-protection-branch", i32 1}
+!1 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}
|
if (const Metadata *const MD = M->getModuleFlag("cf-branch-label-scheme")) | ||
CFBranchLabelScheme = cast<MDString>(MD)->getString(); | ||
else | ||
report_fatal_error("missing cf-branch-label-scheme module flag"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use reportFatalUsageError
which won't generate a stack trace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is inlined to somewhere (getSubtargetImpl()
) that have multiple possible call paths, so I think it's better to print stack for this one.
Do you plan to also address the code that uses
|
…adoption instead of Zicfilp extension availability
…re adoption instead of Zicfilp extension availability
…p CFI feature adoption instead of Zicfilp extension availability
It looks like I have to, otherwise tests would just fail. As a result, I reviewed all behaviors that depended on |
As soon as you have module flags, you need to ensure that when LTO merges two modules, that you get the right errors or merge behaviour. I understand that, unlike subtarget features, when linking all modules (and functions inside that module) need the same protection configuration, right? This semantic is probably the easiest to implement. Please write a test for this and put it in |
Thanks for pointing this out. I had added the LTO tests. |
Update: Model ZICFILP-func-sig and ZICFILP-unlabeled as real target features Reason: During my prototyping of some ELF header field emission (cf. riscv-non-isa/riscv-elf-psabi-doc#474), and I found that in the emission function only "real" target features could be accessed, so I think it's better to just consolidate the module flags into real target features. |
e0044a7
to
f9f6bba
Compare
…sZicfilpCFI() even if new Subtarget is not created
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is broadly correct, but I would like another reviewer.
My comments below are details, not a fundamental problem with the overall approach.
Hi, I have been modeling the By modeling Zicfilp CFI module flags as subtarget features, I hope the following could be achieved:
-> References to
-> By enforcing the consistency between the feature bits and module flags when creating The problem arose when I was trying to handle the module-wide application of the Zicfilp CFI module flags: In In my experience with LLVM, I always assumed that So in a nutshell, I want to ask:
(By the way, I considered recommending this approach to Zicfiss in #152251, but given the uncertainty I'm currently facing, I think it's better that I get approval for this approach beforehand.) Update: I found that MC-level tools like objdump could never have any reference to |
Ping. The current patch set is stable from my standpoint, so please take time to review it again. For the second reviewer required by @ lenary, I would like to invite @topperc to leave some comments about the current approach of modelling module flags as target features. Thanks. |
The features in STI.TM are basically all 0 in LTO linking. It’s populated by mcpu and mattr backend command line options. Clang sets them when generating IR, but they aren’t set during LTO linking. For LTO, everything needs to be in IR. Each Function gets its own target-cpu and target-feature attributes that are used to construct a subtarget for that Function. There is no module subtarget. I’ll look more at the patch tomorrow. |
Expected Behavior:
Stop from adopting control flow integrity (CFI) behaviors, e.g. generating LPAD/LUI insns, use software guarded jumps, etc., by testing if the target has the Zicfilp extension and instead, do CFI if the LLVM module has all of these flags:
cf-protection-branch
: Needs to be a non-zero integer (which meanstrue
)cf-branch-label-scheme
: Needs to beunlabeled
orfunc-sig
unlabeled
as of nowContext:
In clang, Zicfilp-based CFI (the
unlabeled
scheme) can now be enabled by giving the-fcf-protection=branch -mcf-branch-label-scheme=unlabeled
options. With these options, the clang frontend adds the above-mentioned flags to LLVM modules. Here we want to align compiler CFI behaviors to be enabled by the semantics of those LLVM module flags, instead of relying on the inaccurate indicator of whether the Zicfilp extension is available, so the toolchain's behavior is more streamlined and expected.Also, since LPAD insns can be executed regardless of whether Zicfilp extension is available on target or not (due to LPAD insn being encoded as a standard hint insn), clang accepts the above-mentioned CLI options even if Zicfilp is not available and expects backend to still generate LPAD insns. This patch enables LPAD insn generation and other CFI-tailored compiler changes in such cases.