Skip to content

Commit fb71d22

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 0f94493 commit fb71d22

File tree

32 files changed

+428
-72
lines changed

32 files changed

+428
-72
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ 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(
8+
9+
setting.add_bool(
910
"has_lse",
1011
"Has Large System Extensions (FEAT_LSE) support.",
1112
"",
1213
false,
1314
);
14-
1515
setting.add_bool(
1616
"has_pauth",
1717
"Has Pointer authentication (FEAT_PAuth) support; enables the use of \
@@ -44,8 +44,13 @@ fn define_settings(_shared: &SettingGroup) -> SettingGroup {
4444
"",
4545
false,
4646
);
47+
setting.add_bool(
48+
"use_bti",
49+
"Use Branch Target Identification (FEAT_BTI) instructions.",
50+
"",
51+
false,
52+
);
4753

48-
setting.add_predicate("use_lse", predicate!(has_lse));
4954
setting.build()
5055
}
5156

cranelift/codegen/src/alias_analysis.rs

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

240-
visit_block_succs(self.func, block, |_inst, succ| {
240+
visit_block_succs(self.func, block, |_inst, succ, _from_table| {
241241
let succ_first_inst = self
242242
.func
243243
.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: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ fn saved_reg_stack_size(
6767
/// point for the trait; it is never actually instantiated.
6868
pub struct AArch64MachineDeps;
6969

70-
impl IsaFlags for aarch64_settings::Flags {}
70+
impl IsaFlags for aarch64_settings::Flags {
71+
fn is_forward_edge_cfi_enabled(&self) -> bool {
72+
self.use_bti()
73+
}
74+
}
7175

7276
impl ABIMachineSpec for AArch64MachineDeps {
7377
type I = Inst;
@@ -541,13 +545,21 @@ impl ABIMachineSpec for AArch64MachineDeps {
541545
},
542546
});
543547
}
544-
} else if flags.unwind_info() && call_conv.extends_apple_aarch64() {
545-
// The macOS unwinder seems to require this.
546-
insts.push(Inst::Unwind {
547-
inst: UnwindInst::Aarch64SetPointerAuth {
548-
return_addresses: false,
549-
},
550-
});
548+
} else {
549+
if isa_flags.use_bti() {
550+
insts.push(Inst::Bti {
551+
targets: BranchTargetType::C,
552+
});
553+
}
554+
555+
if flags.unwind_info() && call_conv.extends_apple_aarch64() {
556+
// The macOS unwinder seems to require this.
557+
insts.push(Inst::Unwind {
558+
inst: UnwindInst::Aarch64SetPointerAuth {
559+
return_addresses: false,
560+
},
561+
});
562+
}
551563
}
552564

553565
insts

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,11 @@
780780
(Pacisp
781781
(key APIKey))
782782

783+
;; Branch target identification; equivalent to a no-op if Branch Target
784+
;; Identification (FEAT_BTI) is not supported.
785+
(Bti
786+
(targets BranchTargetType))
787+
783788
;; Marker, no-op in generated code: SP "virtual offset" is adjusted. This
784789
;; controls how AMode::NominalSPOffset args are lowered.
785790
(VirtualSPOffsetAdj
@@ -1355,6 +1360,15 @@
13551360
(B)
13561361
))
13571362

1363+
;; Branch target types
1364+
(type BranchTargetType
1365+
(enum
1366+
(None)
1367+
(C)
1368+
(J)
1369+
(JC)
1370+
))
1371+
13581372
;; Extractors for target features ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13591373
(decl use_lse () Inst)
13601374
(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
@@ -3121,6 +3121,16 @@ impl MachInstEmit for Inst {
31213121

31223122
sink.put4(0xd503233f | key << 6);
31233123
}
3124+
&Inst::Bti { targets } => {
3125+
let targets = match targets {
3126+
BranchTargetType::None => 0b00,
3127+
BranchTargetType::C => 0b01,
3128+
BranchTargetType::J => 0b10,
3129+
BranchTargetType::JC => 0b11,
3130+
};
3131+
3132+
sink.put4(0xd503241f | targets << 6);
3133+
}
31243134
&Inst::VirtualSPOffsetAdj { offset } => {
31253135
trace!(
31263136
"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
@@ -57,6 +57,13 @@ fn test_aarch64_binemit() {
5757
"retab",
5858
));
5959
insns.push((Inst::Pacisp { key: APIKey::B }, "7F2303D5", "pacibsp"));
60+
insns.push((
61+
Inst::Bti {
62+
targets: BranchTargetType::J,
63+
},
64+
"9F2403D5",
65+
"bti j",
66+
));
6067
insns.push((Inst::Nop0, "", "nop-zero-len"));
6168
insns.push((Inst::Nop4, "1F2003D5", "nop"));
6269
insns.push((Inst::Csdb, "9F2203D5", "csdb"));

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

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

3838
pub use crate::isa::aarch64::lower::isle::generated_code::{
39-
ALUOp, ALUOp3, APIKey, AtomicRMWLoopOp, AtomicRMWOp, BitOp, FPUOp1, FPUOp2, FPUOp3,
40-
FpuRoundMode, FpuToIntOp, IntToFpuOp, MInst as Inst, MoveWideOp, VecALUModOp, VecALUOp,
39+
ALUOp, ALUOp3, APIKey, AtomicRMWLoopOp, AtomicRMWOp, BitOp, BranchTargetType, FPUOp1, FPUOp2,
40+
FPUOp3, FpuRoundMode, FpuToIntOp, IntToFpuOp, MInst as Inst, MoveWideOp, VecALUModOp, VecALUOp,
4141
VecExtendOp, VecLanesOp, VecMisc2, VecPairOp, VecRRLongOp, VecRRNarrowOp, VecRRPairLongOp,
4242
VecRRRLongOp, VecShiftImmOp,
4343
};
@@ -1040,6 +1040,7 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
10401040
memarg_operands(mem, collector);
10411041
}
10421042
&Inst::Pacisp { .. } => {}
1043+
&Inst::Bti { .. } => {}
10431044
&Inst::VirtualSPOffsetAdj { .. } => {}
10441045

10451046
&Inst::ElfTlsGetAddr { .. } => {
@@ -1234,6 +1235,19 @@ impl MachInst for Inst {
12341235
fn ref_type_regclass(_: &settings::Flags) -> RegClass {
12351236
RegClass::Int
12361237
}
1238+
1239+
fn gen_block_start(
1240+
is_indirect_branch_target: bool,
1241+
is_forward_edge_cfi_enabled: bool,
1242+
) -> Option<Self> {
1243+
if is_indirect_branch_target && is_forward_edge_cfi_enabled {
1244+
Some(Inst::Bti {
1245+
targets: BranchTargetType::J,
1246+
})
1247+
} else {
1248+
None
1249+
}
1250+
}
12371251
}
12381252

12391253
//=============================================================================
@@ -2602,7 +2616,7 @@ impl Inst {
26022616
"csel {}, xzr, {}, hs ; ",
26032617
"csdb ; ",
26042618
"adr {}, pc+16 ; ",
2605-
"ldrsw {}, [{}, {}, LSL 2] ; ",
2619+
"ldrsw {}, [{}, {}, uxtw #2] ; ",
26062620
"add {}, {}, {} ; ",
26072621
"br {} ; ",
26082622
"jt_entries {:?}"
@@ -2715,6 +2729,16 @@ impl Inst {
27152729

27162730
"paci".to_string() + key + "sp"
27172731
}
2732+
&Inst::Bti { targets } => {
2733+
let targets = match targets {
2734+
BranchTargetType::None => "",
2735+
BranchTargetType::C => " c",
2736+
BranchTargetType::J => " j",
2737+
BranchTargetType::JC => " jc",
2738+
};
2739+
2740+
"bti".to_string() + targets
2741+
}
27182742
&Inst::VirtualSPOffsetAdj { offset } => {
27192743
state.virtual_sp_offset += offset;
27202744
format!("virtual_sp_offset_adjust {}", offset)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
7272
isle_prelude_methods!();
7373

7474
fn use_lse(&mut self, _: Inst) -> Option<()> {
75-
if self.isa_flags.use_lse() {
75+
if self.isa_flags.has_lse() {
7676
Some(())
7777
} else {
7878
None

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,18 +1796,20 @@ pub(crate) fn lower_branch(
17961796
// emit_island // this forces an island at this point
17971797
// // if the jumptable would push us past
17981798
// // the deadline
1799-
// subs idx, #jt_size
1799+
// cmp idx, #jt_size
18001800
// b.hs default
1801+
// csel vTmp2, xzr, idx, hs
1802+
// csdb
18011803
// adr vTmp1, PC+16
1802-
// ldr vTmp2, [vTmp1, idx, lsl #2]
1803-
// add vTmp2, vTmp2, vTmp1
1804-
// br vTmp2
1804+
// ldr vTmp2, [vTmp1, vTmp2, uxtw #2]
1805+
// add vTmp1, vTmp1, vTmp2
1806+
// br vTmp1
18051807
// [jumptable offsets relative to JT base]
18061808
let jt_size = targets.len() - 1;
18071809
assert!(jt_size <= std::u32::MAX as usize);
18081810

18091811
ctx.emit(Inst::EmitIsland {
1810-
needed_space: 4 * (6 + jt_size) as CodeOffset,
1812+
needed_space: 4 * (8 + jt_size) as CodeOffset,
18111813
});
18121814

18131815
let ridx = put_input_in_reg(
@@ -1846,8 +1848,10 @@ pub(crate) fn lower_branch(
18461848
// Emit the compound instruction that does:
18471849
//
18481850
// b.hs default
1851+
// csel rB, xzr, rIndex, hs
1852+
// csdb
18491853
// adr rA, jt
1850-
// ldrsw rB, [rA, rIndex, UXTW 2]
1854+
// ldrsw rB, [rA, rB, uxtw #2]
18511855
// add rA, rA, rB
18521856
// br rA
18531857
// [jt entries]

0 commit comments

Comments
 (0)