Path: blob/main/cranelift/codegen/src/isa/unwind/systemv.rs
1693 views
//! System V ABI unwind information.12use crate::isa::unwind::UnwindInst;3use crate::machinst::Reg;4use crate::result::CodegenResult;5use crate::{CodegenError, binemit::CodeOffset};6use alloc::vec::Vec;7use gimli::write::{Address, FrameDescriptionEntry};89#[cfg(feature = "enable-serde")]10use serde_derive::{Deserialize, Serialize};1112type Register = u16;1314/// Enumerate the errors possible in mapping Cranelift registers to their DWARF equivalent.15#[expect(missing_docs, reason = "self-describing variants")]16#[derive(Debug, PartialEq, Eq)]17pub enum RegisterMappingError {18MissingBank,19UnsupportedArchitecture,20UnsupportedRegisterBank(&'static str),21}2223// This is manually implementing Error and Display instead of using thiserror to reduce the amount24// of dependencies used by Cranelift.25impl std::error::Error for RegisterMappingError {}2627impl std::fmt::Display for RegisterMappingError {28fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {29match self {30RegisterMappingError::MissingBank => write!(f, "unable to find bank for register info"),31RegisterMappingError::UnsupportedArchitecture => write!(32f,33"register mapping is currently only implemented for x86_64"34),35RegisterMappingError::UnsupportedRegisterBank(bank) => {36write!(f, "unsupported register bank: {bank}")37}38}39}40}4142// This mirrors gimli's CallFrameInstruction, but is serializable43// This excludes CfaExpression, Expression, ValExpression due to44// https://github.com/gimli-rs/gimli/issues/513.45// TODO: if gimli ever adds serialization support, remove this type46#[derive(Clone, Debug, PartialEq, Eq)]47#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]48pub(crate) enum CallFrameInstruction {49Cfa(Register, i32),50CfaRegister(Register),51CfaOffset(i32),52Restore(Register),53Undefined(Register),54SameValue(Register),55Offset(Register, i32),56ValOffset(Register, i32),57Register(Register, Register),58RememberState,59RestoreState,60ArgsSize(u32),61/// Enables or disables pointer authentication on aarch64 platforms post ARMv8.3. This62/// particular item maps to gimli::ValExpression(RA_SIGN_STATE, lit0/lit1).63Aarch64SetPointerAuth {64return_addresses: bool,65},66}6768impl From<gimli::write::CallFrameInstruction> for CallFrameInstruction {69fn from(cfi: gimli::write::CallFrameInstruction) -> Self {70use gimli::write::CallFrameInstruction;7172match cfi {73CallFrameInstruction::Cfa(reg, offset) => Self::Cfa(reg.0, offset),74CallFrameInstruction::CfaRegister(reg) => Self::CfaRegister(reg.0),75CallFrameInstruction::CfaOffset(offset) => Self::CfaOffset(offset),76CallFrameInstruction::Restore(reg) => Self::Restore(reg.0),77CallFrameInstruction::Undefined(reg) => Self::Undefined(reg.0),78CallFrameInstruction::SameValue(reg) => Self::SameValue(reg.0),79CallFrameInstruction::Offset(reg, offset) => Self::Offset(reg.0, offset),80CallFrameInstruction::ValOffset(reg, offset) => Self::ValOffset(reg.0, offset),81CallFrameInstruction::Register(reg1, reg2) => Self::Register(reg1.0, reg2.0),82CallFrameInstruction::RememberState => Self::RememberState,83CallFrameInstruction::RestoreState => Self::RestoreState,84CallFrameInstruction::ArgsSize(size) => Self::ArgsSize(size),85_ => {86// Cranelift's unwind support does not generate `CallFrameInstruction`s with87// Expression at this moment, and it is not trivial to88// serialize such instructions.89panic!("CallFrameInstruction with Expression not supported");90}91}92}93}9495impl From<CallFrameInstruction> for gimli::write::CallFrameInstruction {96fn from(cfi: CallFrameInstruction) -> gimli::write::CallFrameInstruction {97use CallFrameInstruction as ClifCfi;98use gimli::{Register, write::CallFrameInstruction as GimliCfi, write::Expression};99100match cfi {101ClifCfi::Cfa(reg, offset) => GimliCfi::Cfa(Register(reg), offset),102ClifCfi::CfaRegister(reg) => GimliCfi::CfaRegister(Register(reg)),103ClifCfi::CfaOffset(offset) => GimliCfi::CfaOffset(offset),104ClifCfi::Restore(reg) => GimliCfi::Restore(Register(reg)),105ClifCfi::Undefined(reg) => GimliCfi::Undefined(Register(reg)),106ClifCfi::SameValue(reg) => GimliCfi::SameValue(Register(reg)),107ClifCfi::Offset(reg, offset) => GimliCfi::Offset(Register(reg), offset),108ClifCfi::ValOffset(reg, offset) => GimliCfi::ValOffset(Register(reg), offset),109ClifCfi::Register(reg1, reg2) => GimliCfi::Register(Register(reg1), Register(reg2)),110ClifCfi::RememberState => GimliCfi::RememberState,111ClifCfi::RestoreState => GimliCfi::RestoreState,112ClifCfi::ArgsSize(size) => GimliCfi::ArgsSize(size),113ClifCfi::Aarch64SetPointerAuth { return_addresses } => {114// To enable pointer authentication for return addresses in dwarf directives, we115// use a small dwarf expression that sets the value of the pseudo-register116// RA_SIGN_STATE (RA stands for return address) to 0 or 1. This behavior is117// documented in118// https://github.com/ARM-software/abi-aa/blob/master/aadwarf64/aadwarf64.rst#41dwarf-register-names.119let mut expr = Expression::new();120expr.op(if return_addresses {121gimli::DW_OP_lit1122} else {123gimli::DW_OP_lit0124});125const RA_SIGN_STATE: Register = Register(34);126GimliCfi::ValExpression(RA_SIGN_STATE, expr)127}128}129}130}131132/// Maps UnwindInfo register to gimli's index space.133pub(crate) trait RegisterMapper<Reg> {134/// Maps Reg.135fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;136/// Gets the frame pointer register, if any.137fn fp(&self) -> Option<Register> {138None139}140/// Gets the link register, if any.141fn lr(&self) -> Option<Register> {142None143}144/// What is the offset from saved FP to saved LR?145fn lr_offset(&self) -> Option<u32> {146None147}148}149150/// Represents unwind information for a single System V ABI function.151///152/// This representation is not ISA specific.153#[derive(Clone, Debug, PartialEq, Eq)]154#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]155pub struct UnwindInfo {156instructions: Vec<(u32, CallFrameInstruction)>,157len: u32,158}159160/// Offset from the caller's SP to CFA as we define it.161pub(crate) fn caller_sp_to_cfa_offset() -> u32 {162// Currently we define them to always be equal.1630164}165166pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<Reg>>(167insts: &[(CodeOffset, UnwindInst)],168code_len: usize,169mr: &MR,170) -> CodegenResult<UnwindInfo> {171let mut instructions = vec![];172173let mut cfa_offset = 0;174let mut clobber_offset_to_cfa = 0;175for &(instruction_offset, ref inst) in insts {176match inst {177&UnwindInst::PushFrameRegs {178offset_upward_to_caller_sp,179} => {180// Define CFA in terms of current SP (SP changed and we haven't181// set FP yet).182instructions.push((183instruction_offset,184CallFrameInstruction::CfaOffset(offset_upward_to_caller_sp as i32),185));186// Note that we saved the old FP value on the stack. Use of this187// operation implies that the target defines a FP register.188instructions.push((189instruction_offset,190CallFrameInstruction::Offset(191mr.fp().unwrap(),192-(offset_upward_to_caller_sp as i32),193),194));195// If there is a link register on this architecture, note that196// we saved it as well.197if let Some(lr) = mr.lr() {198instructions.push((199instruction_offset,200CallFrameInstruction::Offset(201lr,202-(offset_upward_to_caller_sp as i32)203+ mr.lr_offset().expect("LR offset not provided") as i32,204),205));206}207}208&UnwindInst::DefineNewFrame {209offset_upward_to_caller_sp,210offset_downward_to_clobbers,211} => {212// Define CFA in terms of FP. Note that we assume it was already213// defined correctly in terms of the current SP, and FP has just214// been set to the current SP, so we do not need to change the215// offset, only the register. (This is done only if the target216// defines a frame pointer register.)217if let Some(fp) = mr.fp() {218instructions.push((instruction_offset, CallFrameInstruction::CfaRegister(fp)));219}220// Record initial CFA offset. This will be used with later221// StackAlloc calls if we do not have a frame pointer.222cfa_offset = offset_upward_to_caller_sp;223// Record distance from CFA downward to clobber area so we can224// express clobber offsets later in terms of CFA.225clobber_offset_to_cfa = offset_upward_to_caller_sp + offset_downward_to_clobbers;226}227&UnwindInst::StackAlloc { size } => {228// If we do not use a frame pointer, we need to update the229// CFA offset whenever the stack pointer changes.230if mr.fp().is_none() {231cfa_offset += size;232instructions.push((233instruction_offset,234CallFrameInstruction::CfaOffset(cfa_offset as i32),235));236}237}238&UnwindInst::SaveReg {239clobber_offset,240reg,241} => {242let reg = mr243.map(reg.into())244.map_err(|e| CodegenError::RegisterMappingError(e))?;245let off = (clobber_offset as i32) - (clobber_offset_to_cfa as i32);246instructions.push((instruction_offset, CallFrameInstruction::Offset(reg, off)));247}248&UnwindInst::RegStackOffset {249clobber_offset,250reg,251} => {252let reg = mr253.map(reg.into())254.map_err(|e| CodegenError::RegisterMappingError(e))?;255let off = (clobber_offset as i32) - (clobber_offset_to_cfa as i32);256instructions.push((257instruction_offset,258CallFrameInstruction::ValOffset(reg, off),259));260}261&UnwindInst::Aarch64SetPointerAuth { return_addresses } => {262instructions.push((263instruction_offset,264CallFrameInstruction::Aarch64SetPointerAuth { return_addresses },265));266}267}268}269270Ok(UnwindInfo {271instructions,272len: code_len as u32,273})274}275276impl UnwindInfo {277/// Converts the unwind information into a `FrameDescriptionEntry`.278pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry {279let mut fde = FrameDescriptionEntry::new(address, self.len);280281for (offset, inst) in &self.instructions {282fde.add_instruction(*offset, inst.clone().into());283}284285fde286}287}288289290