Initial forward-edge CFI implementation (#3693)
* 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. * Refactor `from_artifacts` to avoid second `make_executable` (#1) This involves "parsing" twice but this is parsing just the header of an ELF file so it's not a very intensive operation and should be ok to do twice. * Address the code review feedback Copyright (c) 2022, Arm Limited. Co-authored-by: Alex Crichton <alex@alexcrichton.com>
This commit is contained in:
@@ -67,7 +67,11 @@ fn saved_reg_stack_size(
|
||||
/// point for the trait; it is never actually instantiated.
|
||||
pub struct AArch64MachineDeps;
|
||||
|
||||
impl IsaFlags for aarch64_settings::Flags {}
|
||||
impl IsaFlags for aarch64_settings::Flags {
|
||||
fn is_forward_edge_cfi_enabled(&self) -> bool {
|
||||
self.use_bti()
|
||||
}
|
||||
}
|
||||
|
||||
impl ABIMachineSpec for AArch64MachineDeps {
|
||||
type I = Inst;
|
||||
@@ -549,13 +553,21 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if flags.unwind_info() && call_conv.extends_apple_aarch64() {
|
||||
// The macOS unwinder seems to require this.
|
||||
insts.push(Inst::Unwind {
|
||||
inst: UnwindInst::Aarch64SetPointerAuth {
|
||||
return_addresses: false,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
if isa_flags.use_bti() {
|
||||
insts.push(Inst::Bti {
|
||||
targets: BranchTargetType::C,
|
||||
});
|
||||
}
|
||||
|
||||
if flags.unwind_info() && call_conv.extends_apple_aarch64() {
|
||||
// The macOS unwinder seems to require this.
|
||||
insts.push(Inst::Unwind {
|
||||
inst: UnwindInst::Aarch64SetPointerAuth {
|
||||
return_addresses: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
insts
|
||||
|
||||
@@ -880,6 +880,11 @@
|
||||
;; supported.
|
||||
(Xpaclri)
|
||||
|
||||
;; Branch target identification; equivalent to a no-op if Branch Target
|
||||
;; Identification (FEAT_BTI) is not supported.
|
||||
(Bti
|
||||
(targets BranchTargetType))
|
||||
|
||||
;; Marker, no-op in generated code: SP "virtual offset" is adjusted. This
|
||||
;; controls how AMode::NominalSPOffset args are lowered.
|
||||
(VirtualSPOffsetAdj
|
||||
@@ -1568,6 +1573,15 @@
|
||||
(B)
|
||||
))
|
||||
|
||||
;; Branch target types
|
||||
(type BranchTargetType
|
||||
(enum
|
||||
(None)
|
||||
(C)
|
||||
(J)
|
||||
(JC)
|
||||
))
|
||||
|
||||
;; Extractors for target features ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(decl pure sign_return_address_disabled () Unit)
|
||||
(extern constructor sign_return_address_disabled sign_return_address_disabled)
|
||||
|
||||
@@ -3332,6 +3332,16 @@ impl MachInstEmit for Inst {
|
||||
sink.put4(0xd503233f | key << 6);
|
||||
}
|
||||
&Inst::Xpaclri => sink.put4(0xd50320ff),
|
||||
&Inst::Bti { targets } => {
|
||||
let targets = match targets {
|
||||
BranchTargetType::None => 0b00,
|
||||
BranchTargetType::C => 0b01,
|
||||
BranchTargetType::J => 0b10,
|
||||
BranchTargetType::JC => 0b11,
|
||||
};
|
||||
|
||||
sink.put4(0xd503241f | targets << 6);
|
||||
}
|
||||
&Inst::VirtualSPOffsetAdj { offset } => {
|
||||
trace!(
|
||||
"virtual sp offset adjusted by {} -> {}",
|
||||
|
||||
@@ -58,6 +58,13 @@ fn test_aarch64_binemit() {
|
||||
));
|
||||
insns.push((Inst::Pacisp { key: APIKey::B }, "7F2303D5", "pacibsp"));
|
||||
insns.push((Inst::Xpaclri, "FF2003D5", "xpaclri"));
|
||||
insns.push((
|
||||
Inst::Bti {
|
||||
targets: BranchTargetType::J,
|
||||
},
|
||||
"9F2403D5",
|
||||
"bti j",
|
||||
));
|
||||
insns.push((Inst::Nop0, "", "nop-zero-len"));
|
||||
insns.push((Inst::Nop4, "1F2003D5", "nop"));
|
||||
insns.push((Inst::Csdb, "9F2203D5", "csdb"));
|
||||
|
||||
@@ -36,10 +36,10 @@ mod emit_tests;
|
||||
// Instructions (top level): definition
|
||||
|
||||
pub use crate::isa::aarch64::lower::isle::generated_code::{
|
||||
ALUOp, ALUOp3, AMode, APIKey, AtomicRMWLoopOp, AtomicRMWOp, BitOp, FPUOp1, FPUOp2, FPUOp3,
|
||||
FpuRoundMode, FpuToIntOp, IntToFpuOp, MInst as Inst, MoveWideOp, VecALUModOp, VecALUOp,
|
||||
VecExtendOp, VecLanesOp, VecMisc2, VecPairOp, VecRRLongOp, VecRRNarrowOp, VecRRPairLongOp,
|
||||
VecRRRLongModOp, VecRRRLongOp, VecShiftImmModOp, VecShiftImmOp,
|
||||
ALUOp, ALUOp3, AMode, APIKey, AtomicRMWLoopOp, AtomicRMWOp, BitOp, BranchTargetType, FPUOp1,
|
||||
FPUOp2, FPUOp3, FpuRoundMode, FpuToIntOp, IntToFpuOp, MInst as Inst, MoveWideOp, VecALUModOp,
|
||||
VecALUOp, VecExtendOp, VecLanesOp, VecMisc2, VecPairOp, VecRRLongOp, VecRRNarrowOp,
|
||||
VecRRPairLongOp, VecRRRLongModOp, VecRRRLongOp, VecShiftImmModOp, VecShiftImmOp,
|
||||
};
|
||||
|
||||
/// A floating-point unit (FPU) operation with two args, a register and an immediate.
|
||||
@@ -1072,6 +1072,7 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
|
||||
// Neither LR nor SP is an allocatable register, so there is no need
|
||||
// to do anything.
|
||||
}
|
||||
&Inst::Bti { .. } => {}
|
||||
&Inst::VirtualSPOffsetAdj { .. } => {}
|
||||
|
||||
&Inst::ElfTlsGetAddr { rd, .. } => {
|
||||
@@ -1266,6 +1267,19 @@ impl MachInst for Inst {
|
||||
fn ref_type_regclass(_: &settings::Flags) -> RegClass {
|
||||
RegClass::Int
|
||||
}
|
||||
|
||||
fn gen_block_start(
|
||||
is_indirect_branch_target: bool,
|
||||
is_forward_edge_cfi_enabled: bool,
|
||||
) -> Option<Self> {
|
||||
if is_indirect_branch_target && is_forward_edge_cfi_enabled {
|
||||
Some(Inst::Bti {
|
||||
targets: BranchTargetType::J,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
@@ -2700,7 +2714,7 @@ impl Inst {
|
||||
"csel {}, xzr, {}, hs ; ",
|
||||
"csdb ; ",
|
||||
"adr {}, pc+16 ; ",
|
||||
"ldrsw {}, [{}, {}, LSL 2] ; ",
|
||||
"ldrsw {}, [{}, {}, uxtw #2] ; ",
|
||||
"add {}, {}, {} ; ",
|
||||
"br {} ; ",
|
||||
"jt_entries {:?}"
|
||||
@@ -2812,6 +2826,16 @@ impl Inst {
|
||||
"paci".to_string() + key + "sp"
|
||||
}
|
||||
&Inst::Xpaclri => "xpaclri".to_string(),
|
||||
&Inst::Bti { targets } => {
|
||||
let targets = match targets {
|
||||
BranchTargetType::None => "",
|
||||
BranchTargetType::C => " c",
|
||||
BranchTargetType::J => " j",
|
||||
BranchTargetType::JC => " jc",
|
||||
};
|
||||
|
||||
"bti".to_string() + targets
|
||||
}
|
||||
&Inst::VirtualSPOffsetAdj { offset } => {
|
||||
state.virtual_sp_offset += offset;
|
||||
format!("virtual_sp_offset_adjust {}", offset)
|
||||
|
||||
@@ -86,7 +86,7 @@ impl Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6> {
|
||||
}
|
||||
|
||||
fn use_lse(&mut self, _: Inst) -> Option<()> {
|
||||
if self.isa_flags.use_lse() {
|
||||
if self.isa_flags.has_lse() {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -657,18 +657,20 @@ pub(crate) fn lower_branch(
|
||||
// emit_island // this forces an island at this point
|
||||
// // if the jumptable would push us past
|
||||
// // the deadline
|
||||
// subs idx, #jt_size
|
||||
// cmp idx, #jt_size
|
||||
// b.hs default
|
||||
// csel vTmp2, xzr, idx, hs
|
||||
// csdb
|
||||
// adr vTmp1, PC+16
|
||||
// ldr vTmp2, [vTmp1, idx, lsl #2]
|
||||
// add vTmp2, vTmp2, vTmp1
|
||||
// br vTmp2
|
||||
// ldr vTmp2, [vTmp1, vTmp2, uxtw #2]
|
||||
// add vTmp1, vTmp1, vTmp2
|
||||
// br vTmp1
|
||||
// [jumptable offsets relative to JT base]
|
||||
let jt_size = targets.len() - 1;
|
||||
assert!(jt_size <= std::u32::MAX as usize);
|
||||
|
||||
ctx.emit(Inst::EmitIsland {
|
||||
needed_space: 4 * (6 + jt_size) as CodeOffset,
|
||||
needed_space: 4 * (8 + jt_size) as CodeOffset,
|
||||
});
|
||||
|
||||
let ridx = put_input_in_reg(
|
||||
@@ -707,8 +709,10 @@ pub(crate) fn lower_branch(
|
||||
// Emit the compound instruction that does:
|
||||
//
|
||||
// b.hs default
|
||||
// csel rB, xzr, rIndex, hs
|
||||
// csdb
|
||||
// adr rA, jt
|
||||
// ldrsw rB, [rA, rIndex, UXTW 2]
|
||||
// ldrsw rB, [rA, rB, uxtw #2]
|
||||
// add rA, rA, rB
|
||||
// br rA
|
||||
// [jt entries]
|
||||
|
||||
@@ -115,6 +115,10 @@ impl TargetIsa for AArch64Backend {
|
||||
self.isa_flags.iter().collect()
|
||||
}
|
||||
|
||||
fn is_branch_protection_enabled(&self) -> bool {
|
||||
self.isa_flags.use_bti()
|
||||
}
|
||||
|
||||
fn dynamic_vector_bytes(&self, _dyn_ty: Type) -> u32 {
|
||||
16
|
||||
}
|
||||
|
||||
@@ -226,6 +226,11 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
||||
/// Get the ISA-dependent flag values that were used to make this trait object.
|
||||
fn isa_flags(&self) -> Vec<settings::Value>;
|
||||
|
||||
/// Get a flag indicating whether branch protection is enabled.
|
||||
fn is_branch_protection_enabled(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Get the ISA-dependent maximum vector register size, in bytes.
|
||||
fn dynamic_vector_bytes(&self, dynamic_ty: ir::Type) -> u32;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user