use crate::isa::unwind::UnwindInst;
use crate::machinst::Reg;
use crate::result::CodegenResult;
use crate::{binemit::CodeOffset, CodegenError};
use alloc::vec::Vec;
use gimli::write::{Address, FrameDescriptionEntry};
#[cfg(feature = "enable-serde")]
use serde_derive::{Deserialize, Serialize};
type Register = u16;
#[allow(missing_docs)]
#[derive(Debug, PartialEq, Eq)]
pub enum RegisterMappingError {
MissingBank,
UnsupportedArchitecture,
UnsupportedRegisterBank(&'static str),
}
impl std::error::Error for RegisterMappingError {}
impl std::fmt::Display for RegisterMappingError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
RegisterMappingError::MissingBank => write!(f, "unable to find bank for register info"),
RegisterMappingError::UnsupportedArchitecture => write!(
f,
"register mapping is currently only implemented for x86_64"
),
RegisterMappingError::UnsupportedRegisterBank(bank) => {
write!(f, "unsupported register bank: {}", bank)
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub(crate) enum CallFrameInstruction {
Cfa(Register, i32),
CfaRegister(Register),
CfaOffset(i32),
Restore(Register),
Undefined(Register),
SameValue(Register),
Offset(Register, i32),
ValOffset(Register, i32),
Register(Register, Register),
RememberState,
RestoreState,
ArgsSize(u32),
Aarch64SetPointerAuth {
return_addresses: bool,
},
}
impl From<gimli::write::CallFrameInstruction> for CallFrameInstruction {
fn from(cfi: gimli::write::CallFrameInstruction) -> Self {
use gimli::write::CallFrameInstruction;
match cfi {
CallFrameInstruction::Cfa(reg, offset) => Self::Cfa(reg.0, offset),
CallFrameInstruction::CfaRegister(reg) => Self::CfaRegister(reg.0),
CallFrameInstruction::CfaOffset(offset) => Self::CfaOffset(offset),
CallFrameInstruction::Restore(reg) => Self::Restore(reg.0),
CallFrameInstruction::Undefined(reg) => Self::Undefined(reg.0),
CallFrameInstruction::SameValue(reg) => Self::SameValue(reg.0),
CallFrameInstruction::Offset(reg, offset) => Self::Offset(reg.0, offset),
CallFrameInstruction::ValOffset(reg, offset) => Self::ValOffset(reg.0, offset),
CallFrameInstruction::Register(reg1, reg2) => Self::Register(reg1.0, reg2.0),
CallFrameInstruction::RememberState => Self::RememberState,
CallFrameInstruction::RestoreState => Self::RestoreState,
CallFrameInstruction::ArgsSize(size) => Self::ArgsSize(size),
_ => {
panic!("CallFrameInstruction with Expression not supported");
}
}
}
}
impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
fn into(self) -> gimli::write::CallFrameInstruction {
use gimli::{write::CallFrameInstruction, write::Expression, Register};
match self {
Self::Cfa(reg, offset) => CallFrameInstruction::Cfa(Register(reg), offset),
Self::CfaRegister(reg) => CallFrameInstruction::CfaRegister(Register(reg)),
Self::CfaOffset(offset) => CallFrameInstruction::CfaOffset(offset),
Self::Restore(reg) => CallFrameInstruction::Restore(Register(reg)),
Self::Undefined(reg) => CallFrameInstruction::Undefined(Register(reg)),
Self::SameValue(reg) => CallFrameInstruction::SameValue(Register(reg)),
Self::Offset(reg, offset) => CallFrameInstruction::Offset(Register(reg), offset),
Self::ValOffset(reg, offset) => CallFrameInstruction::ValOffset(Register(reg), offset),
Self::Register(reg1, reg2) => {
CallFrameInstruction::Register(Register(reg1), Register(reg2))
}
Self::RememberState => CallFrameInstruction::RememberState,
Self::RestoreState => CallFrameInstruction::RestoreState,
Self::ArgsSize(size) => CallFrameInstruction::ArgsSize(size),
Self::Aarch64SetPointerAuth { return_addresses } => {
let mut expr = Expression::new();
expr.op(if return_addresses {
gimli::DW_OP_lit1
} else {
gimli::DW_OP_lit0
});
const RA_SIGN_STATE: Register = Register(34);
CallFrameInstruction::ValExpression(RA_SIGN_STATE, expr)
}
}
}
}
pub(crate) trait RegisterMapper<Reg> {
fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
fn fp(&self) -> Option<Register> {
None
}
fn lr(&self) -> Option<Register> {
None
}
fn lr_offset(&self) -> Option<u32> {
None
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct UnwindInfo {
instructions: Vec<(u32, CallFrameInstruction)>,
len: u32,
}
pub(crate) fn caller_sp_to_cfa_offset() -> u32 {
0
}
pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<Reg>>(
insts: &[(CodeOffset, UnwindInst)],
code_len: usize,
mr: &MR,
) -> CodegenResult<UnwindInfo> {
let mut instructions = vec![];
let mut cfa_offset = 0;
let mut clobber_offset_to_cfa = 0;
for &(instruction_offset, ref inst) in insts {
match inst {
&UnwindInst::PushFrameRegs {
offset_upward_to_caller_sp,
} => {
instructions.push((
instruction_offset,
CallFrameInstruction::CfaOffset(offset_upward_to_caller_sp as i32),
));
instructions.push((
instruction_offset,
CallFrameInstruction::Offset(
mr.fp().unwrap(),
-(offset_upward_to_caller_sp as i32),
),
));
if let Some(lr) = mr.lr() {
instructions.push((
instruction_offset,
CallFrameInstruction::Offset(
lr,
-(offset_upward_to_caller_sp as i32)
+ mr.lr_offset().expect("LR offset not provided") as i32,
),
));
}
}
&UnwindInst::DefineNewFrame {
offset_upward_to_caller_sp,
offset_downward_to_clobbers,
} => {
if let Some(fp) = mr.fp() {
instructions.push((instruction_offset, CallFrameInstruction::CfaRegister(fp)));
}
cfa_offset = offset_upward_to_caller_sp;
clobber_offset_to_cfa = offset_upward_to_caller_sp + offset_downward_to_clobbers;
}
&UnwindInst::StackAlloc { size } => {
if mr.fp().is_none() {
cfa_offset += size;
instructions.push((
instruction_offset,
CallFrameInstruction::CfaOffset(cfa_offset as i32),
));
}
}
&UnwindInst::SaveReg {
clobber_offset,
reg,
} => {
let reg = mr
.map(reg.into())
.map_err(|e| CodegenError::RegisterMappingError(e))?;
let off = (clobber_offset as i32) - (clobber_offset_to_cfa as i32);
instructions.push((instruction_offset, CallFrameInstruction::Offset(reg, off)));
}
&UnwindInst::Aarch64SetPointerAuth { return_addresses } => {
instructions.push((
instruction_offset,
CallFrameInstruction::Aarch64SetPointerAuth { return_addresses },
));
}
}
}
Ok(UnwindInfo {
instructions,
len: code_len as u32,
})
}
impl UnwindInfo {
pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry {
let mut fde = FrameDescriptionEntry::new(address, self.len);
for (offset, inst) in &self.instructions {
fde.add_instruction(*offset, inst.clone().into());
}
fde
}
}