cranelift_codegen/isa/unwind/
systemv.rs1use crate::isa::unwind::UnwindInst;
4use crate::machinst::Reg;
5use crate::result::CodegenResult;
6use crate::{CodegenError, binemit::CodeOffset};
7use alloc::vec::Vec;
8use gimli::write::{Address, FrameDescriptionEntry};
9
10#[cfg(feature = "enable-serde")]
11use serde_derive::{Deserialize, Serialize};
12
13type Register = u16;
14
15#[allow(missing_docs)]
17#[derive(Debug, PartialEq, Eq)]
18pub enum RegisterMappingError {
19 MissingBank,
20 UnsupportedArchitecture,
21 UnsupportedRegisterBank(&'static str),
22}
23
24impl std::error::Error for RegisterMappingError {}
27
28impl std::fmt::Display for RegisterMappingError {
29 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
30 match self {
31 RegisterMappingError::MissingBank => write!(f, "unable to find bank for register info"),
32 RegisterMappingError::UnsupportedArchitecture => write!(
33 f,
34 "register mapping is currently only implemented for x86_64"
35 ),
36 RegisterMappingError::UnsupportedRegisterBank(bank) => {
37 write!(f, "unsupported register bank: {bank}")
38 }
39 }
40 }
41}
42
43#[derive(Clone, Debug, PartialEq, Eq)]
48#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
49pub(crate) enum CallFrameInstruction {
50 Cfa(Register, i32),
51 CfaRegister(Register),
52 CfaOffset(i32),
53 Restore(Register),
54 Undefined(Register),
55 SameValue(Register),
56 Offset(Register, i32),
57 ValOffset(Register, i32),
58 Register(Register, Register),
59 RememberState,
60 RestoreState,
61 ArgsSize(u32),
62 Aarch64SetPointerAuth {
65 return_addresses: bool,
66 },
67}
68
69impl From<gimli::write::CallFrameInstruction> for CallFrameInstruction {
70 fn from(cfi: gimli::write::CallFrameInstruction) -> Self {
71 use gimli::write::CallFrameInstruction;
72
73 match cfi {
74 CallFrameInstruction::Cfa(reg, offset) => Self::Cfa(reg.0, offset),
75 CallFrameInstruction::CfaRegister(reg) => Self::CfaRegister(reg.0),
76 CallFrameInstruction::CfaOffset(offset) => Self::CfaOffset(offset),
77 CallFrameInstruction::Restore(reg) => Self::Restore(reg.0),
78 CallFrameInstruction::Undefined(reg) => Self::Undefined(reg.0),
79 CallFrameInstruction::SameValue(reg) => Self::SameValue(reg.0),
80 CallFrameInstruction::Offset(reg, offset) => Self::Offset(reg.0, offset),
81 CallFrameInstruction::ValOffset(reg, offset) => Self::ValOffset(reg.0, offset),
82 CallFrameInstruction::Register(reg1, reg2) => Self::Register(reg1.0, reg2.0),
83 CallFrameInstruction::RememberState => Self::RememberState,
84 CallFrameInstruction::RestoreState => Self::RestoreState,
85 CallFrameInstruction::ArgsSize(size) => Self::ArgsSize(size),
86 _ => {
87 panic!("CallFrameInstruction with Expression not supported");
91 }
92 }
93 }
94}
95
96impl From<CallFrameInstruction> for gimli::write::CallFrameInstruction {
97 fn from(cfi: CallFrameInstruction) -> gimli::write::CallFrameInstruction {
98 use CallFrameInstruction as ClifCfi;
99 use gimli::{Register, write::CallFrameInstruction as GimliCfi, write::Expression};
100
101 match cfi {
102 ClifCfi::Cfa(reg, offset) => GimliCfi::Cfa(Register(reg), offset),
103 ClifCfi::CfaRegister(reg) => GimliCfi::CfaRegister(Register(reg)),
104 ClifCfi::CfaOffset(offset) => GimliCfi::CfaOffset(offset),
105 ClifCfi::Restore(reg) => GimliCfi::Restore(Register(reg)),
106 ClifCfi::Undefined(reg) => GimliCfi::Undefined(Register(reg)),
107 ClifCfi::SameValue(reg) => GimliCfi::SameValue(Register(reg)),
108 ClifCfi::Offset(reg, offset) => GimliCfi::Offset(Register(reg), offset),
109 ClifCfi::ValOffset(reg, offset) => GimliCfi::ValOffset(Register(reg), offset),
110 ClifCfi::Register(reg1, reg2) => GimliCfi::Register(Register(reg1), Register(reg2)),
111 ClifCfi::RememberState => GimliCfi::RememberState,
112 ClifCfi::RestoreState => GimliCfi::RestoreState,
113 ClifCfi::ArgsSize(size) => GimliCfi::ArgsSize(size),
114 ClifCfi::Aarch64SetPointerAuth { return_addresses } => {
115 let mut expr = Expression::new();
121 expr.op(if return_addresses {
122 gimli::DW_OP_lit1
123 } else {
124 gimli::DW_OP_lit0
125 });
126 const RA_SIGN_STATE: Register = Register(34);
127 GimliCfi::ValExpression(RA_SIGN_STATE, expr)
128 }
129 }
130 }
131}
132
133pub(crate) trait RegisterMapper<Reg> {
135 fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
137 fn fp(&self) -> Option<Register> {
139 None
140 }
141 fn lr(&self) -> Option<Register> {
143 None
144 }
145 fn lr_offset(&self) -> Option<u32> {
147 None
148 }
149}
150
151#[derive(Clone, Debug, PartialEq, Eq)]
155#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
156pub struct UnwindInfo {
157 instructions: Vec<(u32, CallFrameInstruction)>,
158 len: u32,
159}
160
161pub(crate) fn caller_sp_to_cfa_offset() -> u32 {
163 0
165}
166
167pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<Reg>>(
168 insts: &[(CodeOffset, UnwindInst)],
169 code_len: usize,
170 mr: &MR,
171) -> CodegenResult<UnwindInfo> {
172 let mut instructions = vec![];
173
174 let mut cfa_offset = 0;
175 let mut clobber_offset_to_cfa = 0;
176 for &(instruction_offset, ref inst) in insts {
177 match inst {
178 &UnwindInst::PushFrameRegs {
179 offset_upward_to_caller_sp,
180 } => {
181 instructions.push((
184 instruction_offset,
185 CallFrameInstruction::CfaOffset(offset_upward_to_caller_sp as i32),
186 ));
187 instructions.push((
190 instruction_offset,
191 CallFrameInstruction::Offset(
192 mr.fp().unwrap(),
193 -(offset_upward_to_caller_sp as i32),
194 ),
195 ));
196 if let Some(lr) = mr.lr() {
199 instructions.push((
200 instruction_offset,
201 CallFrameInstruction::Offset(
202 lr,
203 -(offset_upward_to_caller_sp as i32)
204 + mr.lr_offset().expect("LR offset not provided") as i32,
205 ),
206 ));
207 }
208 }
209 &UnwindInst::DefineNewFrame {
210 offset_upward_to_caller_sp,
211 offset_downward_to_clobbers,
212 } => {
213 if let Some(fp) = mr.fp() {
219 instructions.push((instruction_offset, CallFrameInstruction::CfaRegister(fp)));
220 }
221 cfa_offset = offset_upward_to_caller_sp;
224 clobber_offset_to_cfa = offset_upward_to_caller_sp + offset_downward_to_clobbers;
227 }
228 &UnwindInst::StackAlloc { size } => {
229 if mr.fp().is_none() {
232 cfa_offset += size;
233 instructions.push((
234 instruction_offset,
235 CallFrameInstruction::CfaOffset(cfa_offset as i32),
236 ));
237 }
238 }
239 &UnwindInst::SaveReg {
240 clobber_offset,
241 reg,
242 } => {
243 let reg = mr
244 .map(reg.into())
245 .map_err(|e| CodegenError::RegisterMappingError(e))?;
246 let off = (clobber_offset as i32) - (clobber_offset_to_cfa as i32);
247 instructions.push((instruction_offset, CallFrameInstruction::Offset(reg, off)));
248 }
249 &UnwindInst::RegStackOffset {
250 clobber_offset,
251 reg,
252 } => {
253 let reg = mr
254 .map(reg.into())
255 .map_err(|e| CodegenError::RegisterMappingError(e))?;
256 let off = (clobber_offset as i32) - (clobber_offset_to_cfa as i32);
257 instructions.push((
258 instruction_offset,
259 CallFrameInstruction::ValOffset(reg, off),
260 ));
261 }
262 &UnwindInst::Aarch64SetPointerAuth { return_addresses } => {
263 instructions.push((
264 instruction_offset,
265 CallFrameInstruction::Aarch64SetPointerAuth { return_addresses },
266 ));
267 }
268 }
269 }
270
271 Ok(UnwindInfo {
272 instructions,
273 len: code_len as u32,
274 })
275}
276
277impl UnwindInfo {
278 pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry {
280 let mut fde = FrameDescriptionEntry::new(address, self.len);
281
282 for (offset, inst) in &self.instructions {
283 fde.add_instruction(*offset, inst.clone().into());
284 }
285
286 fde
287 }
288}