Initial back-edge CFI implementation (#3606)
Give the user the option to sign and to authenticate function return addresses with the operations introduced by the Pointer Authentication extension to the Arm instruction set architecture. Copyright (c) 2021, Arm Limited.
This commit is contained in:
@@ -5,7 +5,45 @@ use crate::shared::Definitions as SharedDefinitions;
|
|||||||
|
|
||||||
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
|
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
|
||||||
let mut setting = SettingGroupBuilder::new("arm64");
|
let mut setting = SettingGroupBuilder::new("arm64");
|
||||||
let has_lse = setting.add_bool("has_lse", "Has Large System Extensions support.", "", false);
|
let has_lse = setting.add_bool(
|
||||||
|
"has_lse",
|
||||||
|
"Has Large System Extensions (FEAT_LSE) support.",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
setting.add_bool(
|
||||||
|
"has_pauth",
|
||||||
|
"Has Pointer authentication (FEAT_PAuth) support; enables the use of \
|
||||||
|
non-HINT instructions, but does not have an effect on code generation \
|
||||||
|
by itself.",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
setting.add_bool(
|
||||||
|
"sign_return_address_all",
|
||||||
|
"If function return address signing is enabled, then apply it to all \
|
||||||
|
functions; does not have an effect on code generation by itself.",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
setting.add_bool(
|
||||||
|
"sign_return_address",
|
||||||
|
"Use pointer authentication instructions to sign function return \
|
||||||
|
addresses; HINT-space instructions using the A key are generated \
|
||||||
|
and simple functions that do not use the stack are not affected \
|
||||||
|
unless overridden by other settings.",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
setting.add_bool(
|
||||||
|
"sign_return_address_with_bkey",
|
||||||
|
"Use the B key with pointer authentication instructions instead of \
|
||||||
|
the default A key; does not have an effect on code generation by \
|
||||||
|
itself. Some platform ABIs may require this, for example.",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
setting.add_predicate("use_lse", predicate!(has_lse));
|
setting.add_predicate("use_lse", predicate!(has_lse));
|
||||||
setting.build()
|
setting.build()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::ir::MemFlags;
|
|||||||
use crate::ir::Opcode;
|
use crate::ir::Opcode;
|
||||||
use crate::ir::{ExternalName, LibCall, Signature};
|
use crate::ir::{ExternalName, LibCall, Signature};
|
||||||
use crate::isa;
|
use crate::isa;
|
||||||
use crate::isa::aarch64::{inst::EmitState, inst::*};
|
use crate::isa::aarch64::{inst::EmitState, inst::*, settings as aarch64_settings};
|
||||||
use crate::isa::unwind::UnwindInst;
|
use crate::isa::unwind::UnwindInst;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
@@ -67,9 +67,13 @@ fn saved_reg_stack_size(
|
|||||||
/// point for the trait; it is never actually instantiated.
|
/// point for the trait; it is never actually instantiated.
|
||||||
pub(crate) struct AArch64MachineDeps;
|
pub(crate) struct AArch64MachineDeps;
|
||||||
|
|
||||||
|
impl IsaFlags for aarch64_settings::Flags {}
|
||||||
|
|
||||||
impl ABIMachineSpec for AArch64MachineDeps {
|
impl ABIMachineSpec for AArch64MachineDeps {
|
||||||
type I = Inst;
|
type I = Inst;
|
||||||
|
|
||||||
|
type F = aarch64_settings::Flags;
|
||||||
|
|
||||||
fn word_bits() -> u32 {
|
fn word_bits() -> u32 {
|
||||||
64
|
64
|
||||||
}
|
}
|
||||||
@@ -377,9 +381,23 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_ret(rets: Vec<Reg>) -> Inst {
|
fn gen_ret(setup_frame: bool, isa_flags: &aarch64_settings::Flags, rets: Vec<Reg>) -> Inst {
|
||||||
|
if isa_flags.sign_return_address() && (setup_frame || isa_flags.sign_return_address_all()) {
|
||||||
|
let key = if isa_flags.sign_return_address_with_bkey() {
|
||||||
|
APIKey::B
|
||||||
|
} else {
|
||||||
|
APIKey::A
|
||||||
|
};
|
||||||
|
|
||||||
|
Inst::AuthenticatedRet {
|
||||||
|
key,
|
||||||
|
is_hint: !isa_flags.has_pauth(),
|
||||||
|
rets,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Inst::Ret { rets }
|
Inst::Ret { rets }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u32) -> SmallInstVec<Inst> {
|
fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u32) -> SmallInstVec<Inst> {
|
||||||
let imm = imm as u64;
|
let imm = imm as u64;
|
||||||
@@ -493,19 +511,39 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_debug_frame_info(
|
fn gen_prologue_start(
|
||||||
|
setup_frame: bool,
|
||||||
call_conv: isa::CallConv,
|
call_conv: isa::CallConv,
|
||||||
flags: &settings::Flags,
|
flags: &settings::Flags,
|
||||||
_isa_flags: &Vec<settings::Value>,
|
isa_flags: &aarch64_settings::Flags,
|
||||||
) -> SmallInstVec<Inst> {
|
) -> SmallInstVec<Inst> {
|
||||||
let mut insts = SmallVec::new();
|
let mut insts = SmallVec::new();
|
||||||
if flags.unwind_info() && call_conv.extends_apple_aarch64() {
|
|
||||||
|
if isa_flags.sign_return_address() && (setup_frame || isa_flags.sign_return_address_all()) {
|
||||||
|
let key = if isa_flags.sign_return_address_with_bkey() {
|
||||||
|
APIKey::B
|
||||||
|
} else {
|
||||||
|
APIKey::A
|
||||||
|
};
|
||||||
|
|
||||||
|
insts.push(Inst::Pacisp { key });
|
||||||
|
|
||||||
|
if flags.unwind_info() {
|
||||||
|
insts.push(Inst::Unwind {
|
||||||
|
inst: UnwindInst::Aarch64SetPointerAuth {
|
||||||
|
return_addresses: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if flags.unwind_info() && call_conv.extends_apple_aarch64() {
|
||||||
|
// The macOS unwinder seems to require this.
|
||||||
insts.push(Inst::Unwind {
|
insts.push(Inst::Unwind {
|
||||||
inst: UnwindInst::Aarch64SetPointerAuth {
|
inst: UnwindInst::Aarch64SetPointerAuth {
|
||||||
return_addresses: false,
|
return_addresses: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
insts
|
insts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -672,6 +672,16 @@
|
|||||||
(Ret
|
(Ret
|
||||||
(rets VecReg))
|
(rets VecReg))
|
||||||
|
|
||||||
|
;; A machine return instruction with pointer authentication using SP as the
|
||||||
|
;; modifier. This instruction requires pointer authentication support
|
||||||
|
;; (FEAT_PAuth) unless `is_hint` is true, in which case it is equivalent to
|
||||||
|
;; the combination of a no-op and a return instruction on platforms without
|
||||||
|
;; the relevant support.
|
||||||
|
(AuthenticatedRet
|
||||||
|
(key APIKey)
|
||||||
|
(is_hint bool)
|
||||||
|
(rets VecReg))
|
||||||
|
|
||||||
;; An unconditional branch.
|
;; An unconditional branch.
|
||||||
(Jump
|
(Jump
|
||||||
(dest BranchTarget))
|
(dest BranchTarget))
|
||||||
@@ -746,6 +756,12 @@
|
|||||||
(rd WritableReg)
|
(rd WritableReg)
|
||||||
(mem AMode))
|
(mem AMode))
|
||||||
|
|
||||||
|
;; Pointer authentication code for instruction address with modifier in SP;
|
||||||
|
;; equivalent to a no-op if Pointer authentication (FEAT_PAuth) is not
|
||||||
|
;; supported.
|
||||||
|
(Pacisp
|
||||||
|
(key APIKey))
|
||||||
|
|
||||||
;; Marker, no-op in generated code: SP "virtual offset" is adjusted. This
|
;; Marker, no-op in generated code: SP "virtual offset" is adjusted. This
|
||||||
;; controls how AMode::NominalSPOffset args are lowered.
|
;; controls how AMode::NominalSPOffset args are lowered.
|
||||||
(VirtualSPOffsetAdj
|
(VirtualSPOffsetAdj
|
||||||
@@ -1308,6 +1324,13 @@
|
|||||||
(Xchg)
|
(Xchg)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
;; Keys for instruction address PACs
|
||||||
|
(type APIKey
|
||||||
|
(enum
|
||||||
|
(A)
|
||||||
|
(B)
|
||||||
|
))
|
||||||
|
|
||||||
;; Extractors for target features ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;; Extractors for target features ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
(decl use_lse () Inst)
|
(decl use_lse () Inst)
|
||||||
(extern extractor use_lse use_lse)
|
(extern extractor use_lse use_lse)
|
||||||
|
|||||||
@@ -2774,6 +2774,19 @@ impl MachInstEmit for Inst {
|
|||||||
&Inst::Ret { .. } => {
|
&Inst::Ret { .. } => {
|
||||||
sink.put4(0xd65f03c0);
|
sink.put4(0xd65f03c0);
|
||||||
}
|
}
|
||||||
|
&Inst::AuthenticatedRet { key, is_hint, .. } => {
|
||||||
|
let key = match key {
|
||||||
|
APIKey::A => 0b0,
|
||||||
|
APIKey::B => 0b1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_hint {
|
||||||
|
sink.put4(0xd50323bf | key << 6); // autiasp / autibsp
|
||||||
|
Inst::Ret { rets: vec![] }.emit(&[], sink, emit_info, state);
|
||||||
|
} else {
|
||||||
|
sink.put4(0xd65f0bff | key << 10); // retaa / retab
|
||||||
|
}
|
||||||
|
}
|
||||||
&Inst::Call { ref info } => {
|
&Inst::Call { ref info } => {
|
||||||
if let Some(s) = state.take_stack_map() {
|
if let Some(s) = state.take_stack_map() {
|
||||||
sink.add_stack_map(StackMapExtent::UpcomingBytes(4), s);
|
sink.add_stack_map(StackMapExtent::UpcomingBytes(4), s);
|
||||||
@@ -3064,6 +3077,14 @@ impl MachInstEmit for Inst {
|
|||||||
add.emit(&[], sink, emit_info, state);
|
add.emit(&[], sink, emit_info, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&Inst::Pacisp { key } => {
|
||||||
|
let key = match key {
|
||||||
|
APIKey::A => 0b0,
|
||||||
|
APIKey::B => 0b1,
|
||||||
|
};
|
||||||
|
|
||||||
|
sink.put4(0xd503233f | key << 6);
|
||||||
|
}
|
||||||
&Inst::VirtualSPOffsetAdj { offset } => {
|
&Inst::VirtualSPOffsetAdj { offset } => {
|
||||||
trace!(
|
trace!(
|
||||||
"virtual sp offset adjusted by {} -> {}",
|
"virtual sp offset adjusted by {} -> {}",
|
||||||
|
|||||||
@@ -38,6 +38,25 @@ fn test_aarch64_binemit() {
|
|||||||
//
|
//
|
||||||
// $ echo "mov x1, x2" | aarch64inst.sh
|
// $ echo "mov x1, x2" | aarch64inst.sh
|
||||||
insns.push((Inst::Ret { rets: vec![] }, "C0035FD6", "ret"));
|
insns.push((Inst::Ret { rets: vec![] }, "C0035FD6", "ret"));
|
||||||
|
insns.push((
|
||||||
|
Inst::AuthenticatedRet {
|
||||||
|
key: APIKey::A,
|
||||||
|
is_hint: true,
|
||||||
|
rets: vec![],
|
||||||
|
},
|
||||||
|
"BF2303D5C0035FD6",
|
||||||
|
"autiasp ; ret",
|
||||||
|
));
|
||||||
|
insns.push((
|
||||||
|
Inst::AuthenticatedRet {
|
||||||
|
key: APIKey::B,
|
||||||
|
is_hint: false,
|
||||||
|
rets: vec![],
|
||||||
|
},
|
||||||
|
"FF0F5FD6",
|
||||||
|
"retab",
|
||||||
|
));
|
||||||
|
insns.push((Inst::Pacisp { key: APIKey::B }, "7F2303D5", "pacibsp"));
|
||||||
insns.push((Inst::Nop0, "", "nop-zero-len"));
|
insns.push((Inst::Nop0, "", "nop-zero-len"));
|
||||||
insns.push((Inst::Nop4, "1F2003D5", "nop"));
|
insns.push((Inst::Nop4, "1F2003D5", "nop"));
|
||||||
insns.push((Inst::Csdb, "9F2203D5", "csdb"));
|
insns.push((Inst::Csdb, "9F2203D5", "csdb"));
|
||||||
|
|||||||
@@ -36,9 +36,10 @@ mod emit_tests;
|
|||||||
// Instructions (top level): definition
|
// Instructions (top level): definition
|
||||||
|
|
||||||
pub use crate::isa::aarch64::lower::isle::generated_code::{
|
pub use crate::isa::aarch64::lower::isle::generated_code::{
|
||||||
ALUOp, ALUOp3, AtomicRMWLoopOp, AtomicRMWOp, BitOp, FPUOp1, FPUOp2, FPUOp3, FpuRoundMode,
|
ALUOp, ALUOp3, APIKey, AtomicRMWLoopOp, AtomicRMWOp, BitOp, FPUOp1, FPUOp2, FPUOp3,
|
||||||
FpuToIntOp, IntToFpuOp, MInst as Inst, MoveWideOp, VecALUOp, VecExtendOp, VecLanesOp, VecMisc2,
|
FpuRoundMode, FpuToIntOp, IntToFpuOp, MInst as Inst, MoveWideOp, VecALUOp, VecExtendOp,
|
||||||
VecPairOp, VecRRLongOp, VecRRNarrowOp, VecRRPairLongOp, VecRRRLongOp, VecShiftImmOp,
|
VecLanesOp, VecMisc2, VecPairOp, VecRRLongOp, VecRRNarrowOp, VecRRPairLongOp, VecRRRLongOp,
|
||||||
|
VecShiftImmOp,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A floating-point unit (FPU) operation with two args, a register and an immediate.
|
/// A floating-point unit (FPU) operation with two args, a register and an immediate.
|
||||||
@@ -982,6 +983,11 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
|
|||||||
collector.reg_use(ret);
|
collector.reg_use(ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&Inst::AuthenticatedRet { ref rets, .. } => {
|
||||||
|
for &ret in rets {
|
||||||
|
collector.reg_use(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
&Inst::Jump { .. } => {}
|
&Inst::Jump { .. } => {}
|
||||||
&Inst::Call { ref info, .. } => {
|
&Inst::Call { ref info, .. } => {
|
||||||
collector.reg_uses(&info.uses[..]);
|
collector.reg_uses(&info.uses[..]);
|
||||||
@@ -1030,6 +1036,7 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
|
|||||||
collector.reg_def(rd);
|
collector.reg_def(rd);
|
||||||
memarg_operands(mem, collector);
|
memarg_operands(mem, collector);
|
||||||
}
|
}
|
||||||
|
&Inst::Pacisp { .. } => {}
|
||||||
&Inst::VirtualSPOffsetAdj { .. } => {}
|
&Inst::VirtualSPOffsetAdj { .. } => {}
|
||||||
|
|
||||||
&Inst::ElfTlsGetAddr { .. } => {
|
&Inst::ElfTlsGetAddr { .. } => {
|
||||||
@@ -1089,7 +1096,7 @@ impl MachInst for Inst {
|
|||||||
|
|
||||||
fn is_term(&self) -> MachTerminator {
|
fn is_term(&self) -> MachTerminator {
|
||||||
match self {
|
match self {
|
||||||
&Inst::Ret { .. } => MachTerminator::Ret,
|
&Inst::Ret { .. } | &Inst::AuthenticatedRet { .. } => MachTerminator::Ret,
|
||||||
&Inst::Jump { .. } => MachTerminator::Uncond,
|
&Inst::Jump { .. } => MachTerminator::Uncond,
|
||||||
&Inst::CondBr { .. } => MachTerminator::Cond,
|
&Inst::CondBr { .. } => MachTerminator::Cond,
|
||||||
&Inst::IndirectBr { .. } => MachTerminator::Indirect,
|
&Inst::IndirectBr { .. } => MachTerminator::Indirect,
|
||||||
@@ -2476,6 +2483,18 @@ impl Inst {
|
|||||||
format!("blr {}", rn)
|
format!("blr {}", rn)
|
||||||
}
|
}
|
||||||
&Inst::Ret { .. } => "ret".to_string(),
|
&Inst::Ret { .. } => "ret".to_string(),
|
||||||
|
&Inst::AuthenticatedRet { key, is_hint, .. } => {
|
||||||
|
let key = match key {
|
||||||
|
APIKey::A => "a",
|
||||||
|
APIKey::B => "b",
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_hint {
|
||||||
|
"auti".to_string() + key + "sp ; ret"
|
||||||
|
} else {
|
||||||
|
"reta".to_string() + key
|
||||||
|
}
|
||||||
|
}
|
||||||
&Inst::Jump { ref dest } => {
|
&Inst::Jump { ref dest } => {
|
||||||
let dest = dest.pretty_print(0, allocs);
|
let dest = dest.pretty_print(0, allocs);
|
||||||
format!("b {}", dest)
|
format!("b {}", dest)
|
||||||
@@ -2650,6 +2669,14 @@ impl Inst {
|
|||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
&Inst::Pacisp { key } => {
|
||||||
|
let key = match key {
|
||||||
|
APIKey::A => "a",
|
||||||
|
APIKey::B => "b",
|
||||||
|
};
|
||||||
|
|
||||||
|
"paci".to_string() + key + "sp"
|
||||||
|
}
|
||||||
&Inst::VirtualSPOffsetAdj { offset } => {
|
&Inst::VirtualSPOffsetAdj { offset } => {
|
||||||
state.virtual_sp_offset += offset;
|
state.virtual_sp_offset += offset;
|
||||||
format!("virtual_sp_offset_adjust {}", offset)
|
format!("virtual_sp_offset_adjust {}", offset)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use crate::settings as shared_settings;
|
|||||||
use alloc::{boxed::Box, vec::Vec};
|
use alloc::{boxed::Box, vec::Vec};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use regalloc2::MachineEnv;
|
use regalloc2::MachineEnv;
|
||||||
use target_lexicon::{Aarch64Architecture, Architecture, Triple};
|
use target_lexicon::{Aarch64Architecture, Architecture, OperatingSystem, Triple};
|
||||||
|
|
||||||
// New backend:
|
// New backend:
|
||||||
mod abi;
|
mod abi;
|
||||||
@@ -59,7 +59,7 @@ impl AArch64Backend {
|
|||||||
flags: shared_settings::Flags,
|
flags: shared_settings::Flags,
|
||||||
) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> {
|
) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> {
|
||||||
let emit_info = EmitInfo::new(flags.clone());
|
let emit_info = EmitInfo::new(flags.clone());
|
||||||
let abi = Box::new(abi::AArch64ABICallee::new(func, self)?);
|
let abi = Box::new(abi::AArch64ABICallee::new(func, self, &self.isa_flags)?);
|
||||||
compile::compile::<AArch64Backend>(func, self, abi, &self.machine_env, emit_info)
|
compile::compile::<AArch64Backend>(func, self, abi, &self.machine_env, emit_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,6 +147,21 @@ impl TargetIsa for AArch64Backend {
|
|||||||
|
|
||||||
#[cfg(feature = "unwind")]
|
#[cfg(feature = "unwind")]
|
||||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||||
|
let is_apple_os = match self.triple.operating_system {
|
||||||
|
OperatingSystem::Darwin
|
||||||
|
| OperatingSystem::Ios
|
||||||
|
| OperatingSystem::MacOSX { .. }
|
||||||
|
| OperatingSystem::Tvos => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.isa_flags.sign_return_address()
|
||||||
|
&& self.isa_flags.sign_return_address_with_bkey()
|
||||||
|
&& !is_apple_os
|
||||||
|
{
|
||||||
|
unimplemented!("Specifying that the B key is used with pointer authentication instructions in the CIE is not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
Some(inst::unwind::systemv::create_cie())
|
Some(inst::unwind::systemv::create_cie())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ use crate::ir::MemFlags;
|
|||||||
use crate::ir::Signature;
|
use crate::ir::Signature;
|
||||||
use crate::ir::Type;
|
use crate::ir::Type;
|
||||||
use crate::isa;
|
use crate::isa;
|
||||||
use crate::isa::s390x::inst::*;
|
use crate::isa::s390x::{inst::*, settings as s390x_settings};
|
||||||
use crate::isa::unwind::UnwindInst;
|
use crate::isa::unwind::UnwindInst;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::machinst::{RealReg, Reg, RegClass, Writable};
|
use crate::machinst::{RealReg, Reg, RegClass, Writable};
|
||||||
@@ -206,9 +206,13 @@ impl Into<MemArg> for StackAMode {
|
|||||||
/// point for the trait; it is never actually instantiated.
|
/// point for the trait; it is never actually instantiated.
|
||||||
pub struct S390xMachineDeps;
|
pub struct S390xMachineDeps;
|
||||||
|
|
||||||
|
impl IsaFlags for s390x_settings::Flags {}
|
||||||
|
|
||||||
impl ABIMachineSpec for S390xMachineDeps {
|
impl ABIMachineSpec for S390xMachineDeps {
|
||||||
type I = Inst;
|
type I = Inst;
|
||||||
|
|
||||||
|
type F = s390x_settings::Flags;
|
||||||
|
|
||||||
fn word_bits() -> u32 {
|
fn word_bits() -> u32 {
|
||||||
64
|
64
|
||||||
}
|
}
|
||||||
@@ -391,7 +395,7 @@ impl ABIMachineSpec for S390xMachineDeps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_ret(rets: Vec<Reg>) -> Inst {
|
fn gen_ret(_setup_frame: bool, _isa_flags: &s390x_settings::Flags, rets: Vec<Reg>) -> Inst {
|
||||||
Inst::Ret {
|
Inst::Ret {
|
||||||
link: gpr(14),
|
link: gpr(14),
|
||||||
rets,
|
rets,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ impl S390xBackend {
|
|||||||
func: &Function,
|
func: &Function,
|
||||||
) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> {
|
) -> CodegenResult<(VCode<inst::Inst>, regalloc2::Output)> {
|
||||||
let emit_info = EmitInfo::new(self.isa_flags.clone());
|
let emit_info = EmitInfo::new(self.isa_flags.clone());
|
||||||
let abi = Box::new(abi::S390xABICallee::new(func, self)?);
|
let abi = Box::new(abi::S390xABICallee::new(func, self, &self.isa_flags)?);
|
||||||
compile::compile::<S390xBackend>(func, self, abi, &self.machine_env, emit_info)
|
compile::compile::<S390xBackend>(func, self, abi, &self.machine_env, emit_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
use crate::ir::types::*;
|
use crate::ir::types::*;
|
||||||
use crate::ir::{self, types, ExternalName, LibCall, MemFlags, Opcode, Signature, TrapCode, Type};
|
use crate::ir::{self, types, ExternalName, LibCall, MemFlags, Opcode, Signature, TrapCode, Type};
|
||||||
use crate::isa;
|
use crate::isa;
|
||||||
use crate::isa::{unwind::UnwindInst, x64::inst::*, CallConv};
|
use crate::isa::{unwind::UnwindInst, x64::inst::*, x64::settings as x64_settings, CallConv};
|
||||||
use crate::machinst::abi_impl::*;
|
use crate::machinst::abi_impl::*;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
@@ -29,9 +29,13 @@ pub(crate) type X64ABICaller = ABICallerImpl<X64ABIMachineSpec>;
|
|||||||
/// Implementation of ABI primitives for x64.
|
/// Implementation of ABI primitives for x64.
|
||||||
pub(crate) struct X64ABIMachineSpec;
|
pub(crate) struct X64ABIMachineSpec;
|
||||||
|
|
||||||
|
impl IsaFlags for x64_settings::Flags {}
|
||||||
|
|
||||||
impl ABIMachineSpec for X64ABIMachineSpec {
|
impl ABIMachineSpec for X64ABIMachineSpec {
|
||||||
type I = Inst;
|
type I = Inst;
|
||||||
|
|
||||||
|
type F = x64_settings::Flags;
|
||||||
|
|
||||||
fn word_bits() -> u32 {
|
fn word_bits() -> u32 {
|
||||||
64
|
64
|
||||||
}
|
}
|
||||||
@@ -270,7 +274,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_ret(rets: Vec<Reg>) -> Self::I {
|
fn gen_ret(_setup_frame: bool, _isa_flags: &x64_settings::Flags, rets: Vec<Reg>) -> Self::I {
|
||||||
Inst::ret(rets)
|
Inst::ret(rets)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ impl X64Backend {
|
|||||||
// This performs lowering to VCode, register-allocates the code, computes
|
// This performs lowering to VCode, register-allocates the code, computes
|
||||||
// block layout and finalizes branches. The result is ready for binary emission.
|
// block layout and finalizes branches. The result is ready for binary emission.
|
||||||
let emit_info = EmitInfo::new(flags.clone(), self.x64_flags.clone());
|
let emit_info = EmitInfo::new(flags.clone(), self.x64_flags.clone());
|
||||||
let abi = Box::new(abi::X64ABICallee::new(&func, self)?);
|
let abi = Box::new(abi::X64ABICallee::new(&func, self, &self.x64_flags)?);
|
||||||
compile::compile::<Self>(&func, self, abi, &self.reg_env, emit_info)
|
compile::compile::<Self>(&func, self, abi, &self.reg_env, emit_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -271,6 +271,9 @@ impl StackAMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait implemented by machine-specific backend to represent ISA flags.
|
||||||
|
pub trait IsaFlags: Clone {}
|
||||||
|
|
||||||
/// Trait implemented by machine-specific backend to provide information about
|
/// Trait implemented by machine-specific backend to provide information about
|
||||||
/// register assignments and to allow generating the specific instructions for
|
/// register assignments and to allow generating the specific instructions for
|
||||||
/// stack loads/saves, prologues/epilogues, etc.
|
/// stack loads/saves, prologues/epilogues, etc.
|
||||||
@@ -278,6 +281,9 @@ pub trait ABIMachineSpec {
|
|||||||
/// The instruction type.
|
/// The instruction type.
|
||||||
type I: VCodeInst;
|
type I: VCodeInst;
|
||||||
|
|
||||||
|
/// The ISA flags type.
|
||||||
|
type F: IsaFlags;
|
||||||
|
|
||||||
/// Returns the number of bits in a word, that is 32/64 for 32/64-bit architecture.
|
/// Returns the number of bits in a word, that is 32/64 for 32/64-bit architecture.
|
||||||
fn word_bits() -> u32;
|
fn word_bits() -> u32;
|
||||||
|
|
||||||
@@ -340,7 +346,7 @@ pub trait ABIMachineSpec {
|
|||||||
) -> Self::I;
|
) -> Self::I;
|
||||||
|
|
||||||
/// Generate a return instruction.
|
/// Generate a return instruction.
|
||||||
fn gen_ret(rets: Vec<Reg>) -> Self::I;
|
fn gen_ret(setup_frame: bool, isa_flags: &Self::F, rets: Vec<Reg>) -> Self::I;
|
||||||
|
|
||||||
/// Generate an add-with-immediate. Note that even if this uses a scratch
|
/// Generate an add-with-immediate. Note that even if this uses a scratch
|
||||||
/// register, it must satisfy two requirements:
|
/// register, it must satisfy two requirements:
|
||||||
@@ -387,12 +393,14 @@ pub trait ABIMachineSpec {
|
|||||||
/// Generate a meta-instruction that adjusts the nominal SP offset.
|
/// Generate a meta-instruction that adjusts the nominal SP offset.
|
||||||
fn gen_nominal_sp_adj(amount: i32) -> Self::I;
|
fn gen_nominal_sp_adj(amount: i32) -> Self::I;
|
||||||
|
|
||||||
/// Generates extra unwind instructions for a new frame for this
|
/// Generates the mandatory part of the prologue, irrespective of whether
|
||||||
/// architecture, whether the frame has a prologue sequence or not.
|
/// the usual frame-setup sequence for this architecture is required or not,
|
||||||
fn gen_debug_frame_info(
|
/// e.g. extra unwind instructions.
|
||||||
|
fn gen_prologue_start(
|
||||||
|
_setup_frame: bool,
|
||||||
_call_conv: isa::CallConv,
|
_call_conv: isa::CallConv,
|
||||||
_flags: &settings::Flags,
|
_flags: &settings::Flags,
|
||||||
_isa_flags: &Vec<settings::Value>,
|
_isa_flags: &Self::F,
|
||||||
) -> SmallInstVec<Self::I> {
|
) -> SmallInstVec<Self::I> {
|
||||||
// By default, generates nothing.
|
// By default, generates nothing.
|
||||||
smallvec![]
|
smallvec![]
|
||||||
@@ -722,7 +730,7 @@ pub struct ABICalleeImpl<M: ABIMachineSpec> {
|
|||||||
/// The settings controlling this function's compilation.
|
/// The settings controlling this function's compilation.
|
||||||
flags: settings::Flags,
|
flags: settings::Flags,
|
||||||
/// The ISA-specific flag values controlling this function's compilation.
|
/// The ISA-specific flag values controlling this function's compilation.
|
||||||
isa_flags: Vec<settings::Value>,
|
isa_flags: M::F,
|
||||||
/// Whether or not this function is a "leaf", meaning it calls no other
|
/// Whether or not this function is a "leaf", meaning it calls no other
|
||||||
/// functions
|
/// functions
|
||||||
is_leaf: bool,
|
is_leaf: bool,
|
||||||
@@ -763,7 +771,7 @@ fn get_special_purpose_param_register(
|
|||||||
|
|
||||||
impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
||||||
/// Create a new body ABI instance.
|
/// Create a new body ABI instance.
|
||||||
pub fn new(f: &ir::Function, isa: &dyn TargetIsa) -> CodegenResult<Self> {
|
pub fn new(f: &ir::Function, isa: &dyn TargetIsa, isa_flags: &M::F) -> CodegenResult<Self> {
|
||||||
trace!("ABI: func signature {:?}", f.signature);
|
trace!("ABI: func signature {:?}", f.signature);
|
||||||
|
|
||||||
let flags = isa.flags().clone();
|
let flags = isa.flags().clone();
|
||||||
@@ -857,7 +865,7 @@ impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
|||||||
ret_area_ptr: None,
|
ret_area_ptr: None,
|
||||||
call_conv,
|
call_conv,
|
||||||
flags,
|
flags,
|
||||||
isa_flags: isa.isa_flags(),
|
isa_flags: isa_flags.clone(),
|
||||||
is_leaf: f.is_leaf(),
|
is_leaf: f.is_leaf(),
|
||||||
stack_limit,
|
stack_limit,
|
||||||
probestack_min_frame,
|
probestack_min_frame,
|
||||||
@@ -1275,7 +1283,7 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
M::gen_ret(rets)
|
M::gen_ret(self.setup_frame, &self.isa_flags, rets)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_num_spillslots(&mut self, slots: usize) {
|
fn set_num_spillslots(&mut self, slots: usize) {
|
||||||
@@ -1399,7 +1407,13 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
insts.extend(
|
insts.extend(
|
||||||
M::gen_debug_frame_info(self.call_conv, &self.flags, &self.isa_flags).into_iter(),
|
M::gen_prologue_start(
|
||||||
|
self.setup_frame,
|
||||||
|
self.call_conv,
|
||||||
|
&self.flags,
|
||||||
|
&self.isa_flags,
|
||||||
|
)
|
||||||
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.setup_frame {
|
if self.setup_frame {
|
||||||
@@ -1473,7 +1487,7 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||||||
// This `ret` doesn't need any return registers attached
|
// This `ret` doesn't need any return registers attached
|
||||||
// because we are post-regalloc and don't need to
|
// because we are post-regalloc and don't need to
|
||||||
// represent the implicit uses anymore.
|
// represent the implicit uses anymore.
|
||||||
insts.push(M::gen_ret(vec![]));
|
insts.push(M::gen_ret(self.setup_frame, &self.isa_flags, vec![]));
|
||||||
|
|
||||||
trace!("Epilogue: {:?}", insts);
|
trace!("Epilogue: {:?}", insts);
|
||||||
insts
|
insts
|
||||||
|
|||||||
30
cranelift/filetests/filetests/isa/aarch64/call-pauth.clif
Normal file
30
cranelift/filetests/filetests/isa/aarch64/call-pauth.clif
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
test compile precise-output
|
||||||
|
set unwind_info=false
|
||||||
|
target aarch64 sign_return_address
|
||||||
|
|
||||||
|
function %f1(i64) -> i64 {
|
||||||
|
fn0 = %g(i64) -> i64
|
||||||
|
|
||||||
|
block0(v0: i64):
|
||||||
|
v1 = call fn0(v0)
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
|
||||||
|
; paciasp
|
||||||
|
; stp fp, lr, [sp, #-16]!
|
||||||
|
; mov fp, sp
|
||||||
|
; block0:
|
||||||
|
; ldr x5, 8 ; b 12 ; data TestCase { length: 1, ascii: [103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] } + 0
|
||||||
|
; blr x5
|
||||||
|
; ldp fp, lr, [sp], #16
|
||||||
|
; autiasp ; ret
|
||||||
|
|
||||||
|
function %f2(i64, i64) -> i64 {
|
||||||
|
block0(v0: i64, v1: i64):
|
||||||
|
v2 = iadd.i64 v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
|
||||||
|
; block0:
|
||||||
|
; add x0, x0, x1
|
||||||
|
; ret
|
||||||
@@ -132,6 +132,14 @@ pub fn builder_with_options(infer_native_flags: bool) -> Result<isa::Builder, &'
|
|||||||
if std::arch::is_aarch64_feature_detected!("lse") {
|
if std::arch::is_aarch64_feature_detected!("lse") {
|
||||||
isa_builder.enable("has_lse").unwrap();
|
isa_builder.enable("has_lse").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if std::arch::is_aarch64_feature_detected!("paca") {
|
||||||
|
isa_builder.enable("has_pauth").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg!(target_os = "macos") {
|
||||||
|
isa_builder.enable("sign_return_address_with_bkey").unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is no is_s390x_feature_detected macro yet, so for now
|
// There is no is_s390x_feature_detected macro yet, so for now
|
||||||
|
|||||||
@@ -10,7 +10,25 @@
|
|||||||
//
|
//
|
||||||
// - AAPCS64 section 6.2.3 The Frame Pointer[0]
|
// - AAPCS64 section 6.2.3 The Frame Pointer[0]
|
||||||
pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
|
pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
|
||||||
*(fp as *mut usize).offset(1)
|
let mut pc = *(fp as *mut usize).offset(1);
|
||||||
|
|
||||||
|
// The return address might be signed, so we need to strip the highest bits
|
||||||
|
// (where the authentication code might be located) in order to obtain a
|
||||||
|
// valid address. We use the `XPACLRI` instruction, which is executed as a
|
||||||
|
// no-op by processors that do not support pointer authentication, so that
|
||||||
|
// the implementation is backward-compatible and there is no duplication.
|
||||||
|
// However, this instruction requires the LR register for both its input and
|
||||||
|
// output.
|
||||||
|
std::arch::asm!(
|
||||||
|
"mov lr, {pc}",
|
||||||
|
"xpaclri",
|
||||||
|
"mov {pc}, lr",
|
||||||
|
pc = inout(reg) pc,
|
||||||
|
out("lr") _,
|
||||||
|
options(nomem, nostack, preserves_flags, pure),
|
||||||
|
);
|
||||||
|
|
||||||
|
pc
|
||||||
}
|
}
|
||||||
pub unsafe fn get_next_older_fp_from_fp(fp: usize) -> usize {
|
pub unsafe fn get_next_older_fp_from_fp(fp: usize) -> usize {
|
||||||
*(fp as *mut usize)
|
*(fp as *mut usize)
|
||||||
|
|||||||
@@ -449,6 +449,18 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
enabled = match flag {
|
enabled = match flag {
|
||||||
"has_lse" => Some(std::arch::is_aarch64_feature_detected!("lse")),
|
"has_lse" => Some(std::arch::is_aarch64_feature_detected!("lse")),
|
||||||
|
// No effect on its own, but in order to simplify the code on a
|
||||||
|
// platform without pointer authentication support we fail if
|
||||||
|
// "has_pauth" is enabled, but "sign_return_address" is not.
|
||||||
|
"has_pauth" => Some(std::arch::is_aarch64_feature_detected!("paca")),
|
||||||
|
// No effect on its own.
|
||||||
|
"sign_return_address_all" => Some(true),
|
||||||
|
// The pointer authentication instructions act as a `NOP` when
|
||||||
|
// unsupported (but keep in mind "has_pauth" as well), so it is
|
||||||
|
// safe to enable them.
|
||||||
|
"sign_return_address" => Some(true),
|
||||||
|
// No effect on its own.
|
||||||
|
"sign_return_address_with_bkey" => Some(true),
|
||||||
// fall through to the very bottom to indicate that support is
|
// fall through to the very bottom to indicate that support is
|
||||||
// not enabled to test whether this feature is enabled on the
|
// not enabled to test whether this feature is enabled on the
|
||||||
// host.
|
// host.
|
||||||
@@ -582,8 +594,6 @@ mod tests {
|
|||||||
assert_eq!(engine.config().cache_config.cache_hits(), 1);
|
assert_eq!(engine.config().cache_config.cache_hits(), 1);
|
||||||
assert_eq!(engine.config().cache_config.cache_misses(), 1);
|
assert_eq!(engine.config().cache_config.cache_misses(), 1);
|
||||||
|
|
||||||
// FIXME(#1523) need debuginfo on aarch64 before we run this test there
|
|
||||||
if !cfg!(target_arch = "aarch64") {
|
|
||||||
let mut cfg = Config::new();
|
let mut cfg = Config::new();
|
||||||
cfg.debug_info(true).cache_config_load(&config_path)?;
|
cfg.debug_info(true).cache_config_load(&config_path)?;
|
||||||
let engine = Engine::new(&cfg)?;
|
let engine = Engine::new(&cfg)?;
|
||||||
@@ -593,7 +603,6 @@ mod tests {
|
|||||||
Module::new(&engine, "(module (func))")?;
|
Module::new(&engine, "(module (func))")?;
|
||||||
assert_eq!(engine.config().cache_config.cache_hits(), 1);
|
assert_eq!(engine.config().cache_config.cache_hits(), 1);
|
||||||
assert_eq!(engine.config().cache_config.cache_misses(), 1);
|
assert_eq!(engine.config().cache_config.cache_misses(), 1);
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,6 +195,14 @@ mod test {
|
|||||||
"--disable-logging",
|
"--disable-logging",
|
||||||
"--cranelift-enable",
|
"--cranelift-enable",
|
||||||
"has_lse",
|
"has_lse",
|
||||||
|
"--cranelift-enable",
|
||||||
|
"has_pauth",
|
||||||
|
"--cranelift-enable",
|
||||||
|
"sign_return_address",
|
||||||
|
"--cranelift-enable",
|
||||||
|
"sign_return_address_all",
|
||||||
|
"--cranelift-enable",
|
||||||
|
"sign_return_address_with_bkey",
|
||||||
"-o",
|
"-o",
|
||||||
output_path.to_str().unwrap(),
|
output_path.to_str().unwrap(),
|
||||||
input_path.to_str().unwrap(),
|
input_path.to_str().unwrap(),
|
||||||
|
|||||||
Reference in New Issue
Block a user