Skip to content

Commit b45cc6d

Browse files
committed
Initial forward-edge CFI implementation
Give the user the option to start all basic blocks that are targets of indirect branches with the BTI instruction introduced by the Branch Target Identification extension to the Arm instruction set architecture. Copyright (c) 2022, Arm Limited.
1 parent 010e028 commit b45cc6d

File tree

25 files changed

+362
-67
lines changed

25 files changed

+362
-67
lines changed

cranelift/codegen/meta/src/isa/arm64.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,19 @@ use crate::shared::Definitions as SharedDefinitions;
55

66
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
77
let mut setting = SettingGroupBuilder::new("arm64");
8-
let has_lse = setting.add_bool("has_lse", "Has Large System Extensions support.", "", false);
8+
let has_lse = setting.add_bool(
9+
"has_lse",
10+
"Has Large System Extensions (FEAT_LSE) support.",
11+
"",
12+
false,
13+
);
14+
15+
setting.add_bool(
16+
"use_bti",
17+
"Use Branch Target Identification (FEAT_BTI) instructions.",
18+
"",
19+
false,
20+
);
921

1022
setting.add_predicate("use_lse", predicate!(has_lse));
1123
setting.build()

cranelift/codegen/src/alias_analysis.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ impl<'a> AliasAnalysis<'a> {
236236
log::trace!("after inst{}: state is {:?}", inst.index(), state);
237237
}
238238

239-
visit_block_succs(self.func, block, |_inst, succ| {
239+
visit_block_succs(self.func, block, |_inst, succ, _from_table| {
240240
let succ_first_inst = self
241241
.func
242242
.layout

cranelift/codegen/src/inst_predicates.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,26 +130,32 @@ pub fn has_memory_fence_semantics(op: Opcode) -> bool {
130130
}
131131

132132
/// Visit all successors of a block with a given visitor closure.
133-
pub(crate) fn visit_block_succs<F: FnMut(Inst, Block)>(f: &Function, block: Block, mut visit: F) {
133+
pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>(
134+
f: &Function,
135+
block: Block,
136+
mut visit: F,
137+
) {
134138
for inst in f.layout.block_likely_branches(block) {
135139
if f.dfg[inst].opcode().is_branch() {
136140
visit_branch_targets(f, inst, &mut visit);
137141
}
138142
}
139143
}
140144

141-
fn visit_branch_targets<F: FnMut(Inst, Block)>(f: &Function, inst: Inst, visit: &mut F) {
145+
fn visit_branch_targets<F: FnMut(Inst, Block, bool)>(f: &Function, inst: Inst, visit: &mut F) {
142146
match f.dfg[inst].analyze_branch(&f.dfg.value_lists) {
143147
BranchInfo::NotABranch => {}
144148
BranchInfo::SingleDest(dest, _) => {
145-
visit(inst, dest);
149+
visit(inst, dest, false);
146150
}
147151
BranchInfo::Table(table, maybe_dest) => {
148152
if let Some(dest) = maybe_dest {
149-
visit(inst, dest);
153+
// The default block is reached via a direct conditional branch,
154+
// so it is not part of the table.
155+
visit(inst, dest, false);
150156
}
151157
for &dest in f.jump_tables[table].as_slice() {
152-
visit(inst, dest);
158+
visit(inst, dest, true);
153159
}
154160
}
155161
}

cranelift/codegen/src/isa/aarch64/abi.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,9 +622,9 @@ impl ABIMachineSpec for AArch64MachineDeps {
622622
}
623623
}
624624

625-
fn gen_debug_frame_info(
625+
fn gen_prologue_start(
626626
flags: &settings::Flags,
627-
_isa_flags: &Vec<settings::Value>,
627+
isa_flags: &Vec<settings::Value>,
628628
) -> SmallInstVec<Inst> {
629629
let mut insts = SmallVec::new();
630630
if flags.unwind_info() {
@@ -634,6 +634,13 @@ impl ABIMachineSpec for AArch64MachineDeps {
634634
},
635635
});
636636
}
637+
638+
if has_bool_setting("use_bti", isa_flags) {
639+
insts.push(Inst::Bti {
640+
targets: BranchTargetType::C,
641+
});
642+
}
643+
637644
insts
638645
}
639646

@@ -1333,3 +1340,10 @@ fn is_reg_clobbered_by_call(call_conv_of_callee: isa::CallConv, r: RealReg) -> b
13331340
}
13341341
}
13351342
}
1343+
1344+
fn has_bool_setting(name: &str, isa_flags: &Vec<settings::Value>) -> bool {
1345+
isa_flags
1346+
.iter()
1347+
.find(|&f| f.name == name)
1348+
.map_or(false, |f| f.as_bool().unwrap_or(false))
1349+
}

cranelift/codegen/src/isa/aarch64/inst.isle

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,11 @@
732732
(rd WritableReg)
733733
(mem AMode))
734734

735+
;; Branch target identification; equivalent to a no-op if Branch Target
736+
;; Identification (FEAT_BTI) is not supported.
737+
(Bti
738+
(targets BranchTargetType))
739+
735740
;; Marker, no-op in generated code: SP "virtual offset" is adjusted. This
736741
;; controls how AMode::NominalSPOffset args are lowered.
737742
(VirtualSPOffsetAdj
@@ -1280,6 +1285,15 @@
12801285
(Xchg)
12811286
))
12821287

1288+
;; Branch target types
1289+
(type BranchTargetType
1290+
(enum
1291+
(None)
1292+
(C)
1293+
(J)
1294+
(JC)
1295+
))
1296+
12831297
;; Extractors for target features ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
12841298
(decl use_lse () Inst)
12851299
(extern extractor use_lse use_lse)

cranelift/codegen/src/isa/aarch64/inst/emit.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,6 +3044,16 @@ impl MachInstEmit for Inst {
30443044
add.emit(&[], sink, emit_info, state);
30453045
}
30463046
}
3047+
&Inst::Bti { targets } => {
3048+
let targets = match targets {
3049+
BranchTargetType::None => 0b00,
3050+
BranchTargetType::C => 0b01,
3051+
BranchTargetType::J => 0b10,
3052+
BranchTargetType::JC => 0b11,
3053+
};
3054+
3055+
sink.put4(0xd503241f | targets << 6);
3056+
}
30473057
&Inst::VirtualSPOffsetAdj { offset } => {
30483058
log::trace!(
30493059
"virtual sp offset adjusted by {} -> {}",

cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ fn test_aarch64_binemit() {
3838
//
3939
// $ echo "mov x1, x2" | aarch64inst.sh
4040
insns.push((Inst::Ret { rets: vec![] }, "C0035FD6", "ret"));
41+
insns.push((
42+
Inst::Bti {
43+
targets: BranchTargetType::J,
44+
},
45+
"9F2403D5",
46+
"bti j",
47+
));
4148
insns.push((Inst::Nop0, "", "nop-zero-len"));
4249
insns.push((Inst::Nop4, "1F2003D5", "nop"));
4350
insns.push((

cranelift/codegen/src/isa/aarch64/inst/mod.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ mod emit_tests;
3636
// Instructions (top level): definition
3737

3838
pub use crate::isa::aarch64::lower::isle::generated_code::{
39-
ALUOp, ALUOp3, AtomicRMWLoopOp, AtomicRMWOp, BitOp, FPUOp1, FPUOp2, FPUOp3, FpuRoundMode,
40-
FpuToIntOp, IntToFpuOp, MInst as Inst, MoveWideOp, VecALUOp, VecExtendOp, VecLanesOp, VecMisc2,
41-
VecPairOp, VecRRLongOp, VecRRNarrowOp, VecRRPairLongOp, VecRRRLongOp, VecShiftImmOp,
39+
ALUOp, ALUOp3, AtomicRMWLoopOp, AtomicRMWOp, BitOp, BranchTargetType, FPUOp1, FPUOp2, FPUOp3,
40+
FpuRoundMode, FpuToIntOp, IntToFpuOp, MInst as Inst, MoveWideOp, VecALUOp, VecExtendOp,
41+
VecLanesOp, VecMisc2, VecPairOp, VecRRLongOp, VecRRNarrowOp, VecRRPairLongOp, VecRRRLongOp,
42+
VecShiftImmOp,
4243
};
4344

4445
/// A floating-point unit (FPU) operation with two args, a register and an immediate.
@@ -1025,6 +1026,7 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
10251026
collector.reg_def(rd);
10261027
memarg_operands(mem, collector);
10271028
}
1029+
&Inst::Bti { .. } => {}
10281030
&Inst::VirtualSPOffsetAdj { .. } => {}
10291031

10301032
&Inst::ElfTlsGetAddr { .. } => {
@@ -2703,6 +2705,16 @@ impl Inst {
27032705
}
27042706
ret
27052707
}
2708+
&Inst::Bti { targets } => {
2709+
let targets = match targets {
2710+
BranchTargetType::None => "",
2711+
BranchTargetType::C => " c",
2712+
BranchTargetType::J => " j",
2713+
BranchTargetType::JC => " jc",
2714+
};
2715+
2716+
"bti".to_string() + targets
2717+
}
27062718
&Inst::VirtualSPOffsetAdj { offset } => {
27072719
state.virtual_sp_offset += offset;
27082720
format!("virtual_sp_offset_adjust {}", offset)

cranelift/codegen/src/isa/aarch64/lower.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,4 +1559,18 @@ impl LowerBackend for AArch64Backend {
15591559
fn maybe_pinned_reg(&self) -> Option<Reg> {
15601560
Some(xreg(PINNED_REG))
15611561
}
1562+
1563+
fn start_block<C: LowerCtx<I = Inst>>(
1564+
&self,
1565+
is_indirect_branch_target: bool,
1566+
ctx: &mut C,
1567+
) -> CodegenResult<()> {
1568+
if self.isa_flags.use_bti() && is_indirect_branch_target {
1569+
ctx.emit(Inst::Bti {
1570+
targets: BranchTargetType::J,
1571+
});
1572+
}
1573+
1574+
Ok(())
1575+
}
15621576
}

cranelift/codegen/src/machinst/abi_impl.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,9 +403,10 @@ pub trait ABIMachineSpec {
403403
/// Generate a meta-instruction that adjusts the nominal SP offset.
404404
fn gen_nominal_sp_adj(amount: i32) -> Self::I;
405405

406-
/// Generates extra unwind instructions for a new frame for this
407-
/// architecture, whether the frame has a prologue sequence or not.
408-
fn gen_debug_frame_info(
406+
/// Generates the mandatory part of the prologue, irrespective of whether
407+
/// the usual frame-setup sequence for this architecture is required or not,
408+
/// e.g. extra unwind instructions.
409+
fn gen_prologue_start(
409410
_flags: &settings::Flags,
410411
_isa_flags: &Vec<settings::Value>,
411412
) -> SmallInstVec<Self::I> {
@@ -1238,7 +1239,7 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
12381239
self.fixed_frame_storage_size,
12391240
);
12401241

1241-
insts.extend(M::gen_debug_frame_info(&self.flags, &self.isa_flags).into_iter());
1242+
insts.extend(M::gen_prologue_start(&self.flags, &self.isa_flags).into_iter());
12421243

12431244
if self.setup_frame {
12441245
// set up frame

0 commit comments

Comments
 (0)