Skip to content

Commit 43c50c2

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 2e14a0e commit 43c50c2

File tree

22 files changed

+353
-60
lines changed

22 files changed

+353
-60
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/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

@@ -1323,3 +1330,10 @@ fn is_reg_clobbered_by_call(call_conv_of_callee: isa::CallConv, r: RealReg) -> b
13231330
}
13241331
}
13251332
}
1333+
1334+
fn has_bool_setting(name: &str, isa_flags: &Vec<settings::Value>) -> bool {
1335+
isa_flags
1336+
.iter()
1337+
.find(|&f| f.name == name)
1338+
.map_or(false, |f| f.as_bool().unwrap_or(false))
1339+
}

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

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

733+
;; Branch target identification; equivalent to a no-op if Branch Target
734+
;; Identification (FEAT_BTI) is not supported.
735+
(Bti
736+
(targets BranchTargetType))
737+
733738
;; Marker, no-op in generated code: SP "virtual offset" is adjusted. This
734739
;; controls how AMode::NominalSPOffset args are lowered.
735740
(VirtualSPOffsetAdj
@@ -1278,6 +1283,15 @@
12781283
(Xchg)
12791284
))
12801285

1286+
;; Branch target types
1287+
(type BranchTargetType
1288+
(enum
1289+
(None)
1290+
(C)
1291+
(J)
1292+
(JC)
1293+
))
1294+
12811295
;; Extractors for target features ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
12821296
(decl use_lse () Inst)
12831297
(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
@@ -3048,6 +3048,16 @@ impl MachInstEmit for Inst {
30483048
add.emit(&[], sink, emit_info, state);
30493049
}
30503050
}
3051+
&Inst::Bti { targets } => {
3052+
let targets = match targets {
3053+
BranchTargetType::None => 0b00,
3054+
BranchTargetType::C => 0b01,
3055+
BranchTargetType::J => 0b10,
3056+
BranchTargetType::JC => 0b11,
3057+
};
3058+
3059+
sink.put4(0xd503241f | targets << 6);
3060+
}
30513061
&Inst::VirtualSPOffsetAdj { offset } => {
30523062
log::trace!(
30533063
"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> {
@@ -1236,7 +1237,7 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
12361237
self.fixed_frame_storage_size,
12371238
);
12381239

1239-
insts.extend(M::gen_debug_frame_info(&self.flags, &self.isa_flags).into_iter());
1240+
insts.extend(M::gen_prologue_start(&self.flags, &self.isa_flags).into_iter());
12401241

12411242
if self.setup_frame {
12421243
// set up frame

cranelift/codegen/src/machinst/blockorder.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ pub struct BlockLoweringOrder {
106106
/// which is used by VCode emission to sink the blocks at the last
107107
/// moment (when we actually emit bytes into the MachBuffer).
108108
cold_blocks: FxHashSet<BlockIndex>,
109+
/// CLIF BBs that are indirect branch targets.
110+
indirect_branch_targets: FxHashSet<Block>,
109111
}
110112

111113
/// The origin of a block in the lowered block-order: either an original CLIF
@@ -224,14 +226,19 @@ impl BlockLoweringOrder {
224226
let mut block_succs: SmallVec<[(Inst, usize, Block); 128]> = SmallVec::new();
225227
let mut block_succ_range = SecondaryMap::with_default((0, 0));
226228
let mut fallthrough_return_block = None;
229+
let mut indirect_branch_targets = FxHashSet::default();
227230
for block in f.layout.blocks() {
228231
let block_succ_start = block_succs.len();
229232
let mut succ_idx = 0;
230-
visit_block_succs(f, block, |inst, succ| {
233+
visit_block_succs(f, block, |inst, succ, from_table| {
231234
block_out_count[block] += 1;
232235
block_in_count[succ] += 1;
233236
block_succs.push((inst, succ_idx, succ));
234237
succ_idx += 1;
238+
239+
if from_table {
240+
indirect_branch_targets.insert(succ);
241+
}
235242
});
236243
let block_succ_end = block_succs.len();
237244
block_succ_range[block] = (block_succ_start, block_succ_end);
@@ -476,6 +483,7 @@ impl BlockLoweringOrder {
476483
lowered_succ_ranges,
477484
orig_map,
478485
cold_blocks,
486+
indirect_branch_targets,
479487
};
480488
log::trace!("BlockLoweringOrder: {:?}", result);
481489
result
@@ -496,6 +504,11 @@ impl BlockLoweringOrder {
496504
pub fn is_cold(&self, block: BlockIndex) -> bool {
497505
self.cold_blocks.contains(&block)
498506
}
507+
508+
/// Determine whether the given CLIF BB is an indirect branch target.
509+
pub fn is_indirect_branch_target(&self, block: Block) -> bool {
510+
self.indirect_branch_targets.contains(&block)
511+
}
499512
}
500513

501514
#[cfg(test)]

cranelift/codegen/src/machinst/lower.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,16 @@ pub trait LowerBackend {
269269
fn maybe_pinned_reg(&self) -> Option<Reg> {
270270
None
271271
}
272+
273+
/// Generate the instructions that must appear at the beginning of a basic
274+
/// block, if any.
275+
fn start_block<C: LowerCtx<I = Self::MInst>>(
276+
&self,
277+
_is_indirect_branch_target: bool,
278+
_ctx: &mut C,
279+
) -> CodegenResult<()> {
280+
Ok(())
281+
}
272282
}
273283

274284
/// Machine-independent lowering driver / machine-instruction container. Maintains a correspondence
@@ -1093,12 +1103,13 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
10931103
// Main lowering loop over lowered blocks.
10941104
for (bindex, lb) in lowered_order.iter().enumerate().rev() {
10951105
let bindex = BlockIndex::new(bindex);
1106+
let orig_block = lb.orig_block();
10961107

10971108
// Lower the block body in reverse order (see comment in
10981109
// `lower_clif_block()` for rationale).
10991110

11001111
// End branches.
1101-
if let Some(bb) = lb.orig_block() {
1112+
if let Some(bb) = orig_block {
11021113
self.collect_branches_and_targets(bindex, bb, &mut branches, &mut targets);
11031114
if branches.len() > 0 {
11041115
self.lower_clif_branches(backend, bindex, bb, &branches, &targets)?;
@@ -1134,7 +1145,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
11341145
}
11351146

11361147
// Original block body.
1137-
if let Some(bb) = lb.orig_block() {
1148+
if let Some(bb) = orig_block {
11381149
self.lower_clif_block(backend, bb)?;
11391150
self.emit_value_label_markers_for_block_args(bb);
11401151
}
@@ -1143,6 +1154,15 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
11431154
// Set up the function with arg vreg inits.
11441155
self.gen_arg_setup();
11451156
self.finish_ir_inst(SourceLoc::default());
1157+
} else {
1158+
let is_indirect_branch_target = if let Some(bb) = orig_block {
1159+
self.vcode.block_order().is_indirect_branch_target(bb)
1160+
} else {
1161+
false
1162+
};
1163+
1164+
backend.start_block(is_indirect_branch_target, &mut self)?;
1165+
self.finish_ir_inst(SourceLoc::default());
11461166
}
11471167

11481168
self.finish_bb();
@@ -1493,26 +1513,32 @@ impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
14931513
}
14941514

14951515
/// Visit all successors of a block with a given visitor closure.
1496-
pub(crate) fn visit_block_succs<F: FnMut(Inst, Block)>(f: &Function, block: Block, mut visit: F) {
1516+
pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>(
1517+
f: &Function,
1518+
block: Block,
1519+
mut visit: F,
1520+
) {
14971521
for inst in f.layout.block_likely_branches(block) {
14981522
if f.dfg[inst].opcode().is_branch() {
14991523
visit_branch_targets(f, inst, &mut visit);
15001524
}
15011525
}
15021526
}
15031527

1504-
fn visit_branch_targets<F: FnMut(Inst, Block)>(f: &Function, inst: Inst, visit: &mut F) {
1528+
fn visit_branch_targets<F: FnMut(Inst, Block, bool)>(f: &Function, inst: Inst, visit: &mut F) {
15051529
match f.dfg[inst].analyze_branch(&f.dfg.value_lists) {
15061530
BranchInfo::NotABranch => {}
15071531
BranchInfo::SingleDest(dest, _) => {
1508-
visit(inst, dest);
1532+
visit(inst, dest, false);
15091533
}
15101534
BranchInfo::Table(table, maybe_dest) => {
15111535
if let Some(dest) = maybe_dest {
1512-
visit(inst, dest);
1536+
// The default block is reached via a direct conditional branch,
1537+
// so it is not part of the table.
1538+
visit(inst, dest, false);
15131539
}
15141540
for &dest in f.jump_tables[table].as_slice() {
1515-
visit(inst, dest);
1541+
visit(inst, dest, true);
15161542
}
15171543
}
15181544
}

0 commit comments

Comments
 (0)