Skip to content

Commit 2595dec

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 d620705 commit 2595dec

File tree

32 files changed

+428
-73
lines changed

32 files changed

+428
-73
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
@@ -785,6 +785,11 @@
785785
;; supported.
786786
(Xpaclri)
787787

788+
;; Branch target identification; equivalent to a no-op if Branch Target
789+
;; Identification (FEAT_BTI) is not supported.
790+
(Bti
791+
(targets BranchTargetType))
792+
788793
;; Marker, no-op in generated code: SP "virtual offset" is adjusted. This
789794
;; controls how AMode::NominalSPOffset args are lowered.
790795
(VirtualSPOffsetAdj
@@ -1360,6 +1365,15 @@
13601365
(B)
13611366
))
13621367

1368+
;; Branch target types
1369+
(type BranchTargetType
1370+
(enum
1371+
(None)
1372+
(C)
1373+
(J)
1374+
(JC)
1375+
))
1376+
13631377
;; Extractors for target features ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13641378
(decl pure sign_return_address_disabled () Unit)
13651379
(extern constructor sign_return_address_disabled sign_return_address_disabled)

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3122,6 +3122,16 @@ impl MachInstEmit for Inst {
31223122
sink.put4(0xd503233f | key << 6);
31233123
}
31243124
&Inst::Xpaclri => sink.put4(0xd50320ff),
3125+
&Inst::Bti { targets } => {
3126+
let targets = match targets {
3127+
BranchTargetType::None => 0b00,
3128+
BranchTargetType::C => 0b01,
3129+
BranchTargetType::J => 0b10,
3130+
BranchTargetType::JC => 0b11,
3131+
};
3132+
3133+
sink.put4(0xd503241f | targets << 6);
3134+
}
31253135
&Inst::VirtualSPOffsetAdj { offset } => {
31263136
trace!(
31273137
"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
@@ -58,6 +58,13 @@ fn test_aarch64_binemit() {
5858
));
5959
insns.push((Inst::Pacisp { key: APIKey::B }, "7F2303D5", "pacibsp"));
6060
insns.push((Inst::Xpaclri, "FF2003D5", "xpaclri"));
61+
insns.push((
62+
Inst::Bti {
63+
targets: BranchTargetType::J,
64+
},
65+
"9F2403D5",
66+
"bti j",
67+
));
6168
insns.push((Inst::Nop0, "", "nop-zero-len"));
6269
insns.push((Inst::Nop4, "1F2003D5", "nop"));
6370
insns.push((Inst::Csdb, "9F2203D5", "csdb"));

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

Lines changed: 27 additions & 4 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
};
@@ -1038,8 +1038,8 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
10381038
// Neither LR nor SP is an allocatable register, so there is no need
10391039
// to do anything.
10401040
}
1041+
&Inst::Bti { .. } => {}
10411042
&Inst::VirtualSPOffsetAdj { .. } => {}
1042-
10431043
&Inst::ElfTlsGetAddr { .. } => {
10441044
collector.reg_def(Writable::from_reg(regs::xreg(0)));
10451045
let mut clobbers = AArch64MachineDeps::get_regs_clobbered_by_call(CallConv::SystemV);
@@ -1232,6 +1232,19 @@ impl MachInst for Inst {
12321232
fn ref_type_regclass(_: &settings::Flags) -> RegClass {
12331233
RegClass::Int
12341234
}
1235+
1236+
fn gen_block_start(
1237+
is_indirect_branch_target: bool,
1238+
is_forward_edge_cfi_enabled: bool,
1239+
) -> Option<Self> {
1240+
if is_indirect_branch_target && is_forward_edge_cfi_enabled {
1241+
Some(Inst::Bti {
1242+
targets: BranchTargetType::J,
1243+
})
1244+
} else {
1245+
None
1246+
}
1247+
}
12351248
}
12361249

12371250
//=============================================================================
@@ -2600,7 +2613,7 @@ impl Inst {
26002613
"csel {}, xzr, {}, hs ; ",
26012614
"csdb ; ",
26022615
"adr {}, pc+16 ; ",
2603-
"ldrsw {}, [{}, {}, LSL 2] ; ",
2616+
"ldrsw {}, [{}, {}, uxtw #2] ; ",
26042617
"add {}, {}, {} ; ",
26052618
"br {} ; ",
26062619
"jt_entries {:?}"
@@ -2714,6 +2727,16 @@ impl Inst {
27142727
"paci".to_string() + key + "sp"
27152728
}
27162729
&Inst::Xpaclri => "xpaclri".to_string(),
2730+
&Inst::Bti { targets } => {
2731+
let targets = match targets {
2732+
BranchTargetType::None => "",
2733+
BranchTargetType::C => " c",
2734+
BranchTargetType::J => " j",
2735+
BranchTargetType::JC => " jc",
2736+
};
2737+
2738+
"bti".to_string() + targets
2739+
}
27172740
&Inst::VirtualSPOffsetAdj { offset } => {
27182741
state.virtual_sp_offset += offset;
27192742
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
@@ -80,7 +80,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
8080
}
8181

8282
fn use_lse(&mut self, _: Inst) -> Option<()> {
83-
if self.isa_flags.use_lse() {
83+
if self.isa_flags.has_lse() {
8484
Some(())
8585
} else {
8686
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)