cranelift_codegen/isa/unwind/
systemv.rs1use crate::isa::unwind::UnwindInst;
4use crate::machinst::Reg;
5use crate::result::CodegenResult;
6use crate::{binemit::CodeOffset, CodegenError};
7use alloc::vec::Vec;
8use gimli::write::{Address, FrameDescriptionEntry};
9
10#[cfg(feature = "enable-serde")]
11use serde::{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 Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
97 fn into(self) -> gimli::write::CallFrameInstruction {
98 use gimli::{write::CallFrameInstruction, write::Expression, Register};
99
100 match self {
101 Self::Cfa(reg, offset) => CallFrameInstruction::Cfa(Register(reg), offset),
102 Self::CfaRegister(reg) => CallFrameInstruction::CfaRegister(Register(reg)),
103 Self::CfaOffset(offset) => CallFrameInstruction::CfaOffset(offset),
104 Self::Restore(reg) => CallFrameInstruction::Restore(Register(reg)),
105 Self::Undefined(reg) => CallFrameInstruction::Undefined(Register(reg)),
106 Self::SameValue(reg) => CallFrameInstruction::SameValue(Register(reg)),
107 Self::Offset(reg, offset) => CallFrameInstruction::Offset(Register(reg), offset),
108 Self::ValOffset(reg, offset) => CallFrameInstruction::ValOffset(Register(reg), offset),
109 Self::Register(reg1, reg2) => {
110 CallFrameInstruction::Register(Register(reg1), Register(reg2))
111 }
112 Self::RememberState => CallFrameInstruction::RememberState,
113 Self::RestoreState => CallFrameInstruction::RestoreState,
114 Self::ArgsSize(size) => CallFrameInstruction::ArgsSize(size),
115 Self::Aarch64SetPointerAuth { return_addresses } => {
116 let mut expr = Expression::new();
122 expr.op(if return_addresses {
123 gimli::DW_OP_lit1
124 } else {
125 gimli::DW_OP_lit0
126 });
127 const RA_SIGN_STATE: Register = Register(34);
128 CallFrameInstruction::ValExpression(RA_SIGN_STATE, expr)
129 }
130 }
131 }
132}
133
134pub(crate) trait RegisterMapper<Reg> {
136 fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
138 fn sp(&self) -> Register;
140 fn fp(&self) -> Option<Register> {
142 None
143 }
144 fn lr(&self) -> Option<Register> {
146 None
147 }
148 fn lr_offset(&self) -> Option<u32> {
150 None
151 }
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
158#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
159pub struct UnwindInfo {
160 instructions: Vec<(u32, CallFrameInstruction)>,
161 len: u32,
162}
163
164pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<Reg>>(
165 insts: &[(CodeOffset, UnwindInst)],
166 code_len: usize,
167 mr: &MR,
168) -> CodegenResult<UnwindInfo> {
169 let mut instructions = vec![];
170
171 let mut cfa_offset = 0;
172 let mut clobber_offset_to_cfa = 0;
173 for &(instruction_offset, ref inst) in insts {
174 match inst {
175 &UnwindInst::PushFrameRegs {
176 offset_upward_to_caller_sp,
177 } => {
178 instructions.push((
181 instruction_offset,
182 CallFrameInstruction::CfaOffset(offset_upward_to_caller_sp as i32),
183 ));
184 instructions.push((
187 instruction_offset,
188 CallFrameInstruction::Offset(
189 mr.fp().unwrap(),
190 -(offset_upward_to_caller_sp as i32),
191 ),
192 ));
193 if let Some(lr) = mr.lr() {
196 instructions.push((
197 instruction_offset,
198 CallFrameInstruction::Offset(
199 lr,
200 -(offset_upward_to_caller_sp as i32)
201 + mr.lr_offset().expect("LR offset not provided") as i32,
202 ),
203 ));
204 }
205 }
206 &UnwindInst::DefineNewFrame {
207 offset_upward_to_caller_sp,
208 offset_downward_to_clobbers,
209 } => {
210 if let Some(fp) = mr.fp() {
216 instructions.push((instruction_offset, CallFrameInstruction::CfaRegister(fp)));
217 }
218 cfa_offset = offset_upward_to_caller_sp;
221 clobber_offset_to_cfa = offset_upward_to_caller_sp + offset_downward_to_clobbers;
224 }
225 &UnwindInst::StackAlloc { size } => {
226 if mr.fp().is_none() {
229 cfa_offset += size;
230 instructions.push((
231 instruction_offset,
232 CallFrameInstruction::CfaOffset(cfa_offset as i32),
233 ));
234 }
235 }
236 &UnwindInst::SaveReg {
237 clobber_offset,
238 reg,
239 } => {
240 let reg = mr
241 .map(reg.into())
242 .map_err(|e| CodegenError::RegisterMappingError(e))?;
243 let off = (clobber_offset as i32) - (clobber_offset_to_cfa as i32);
244 instructions.push((instruction_offset, CallFrameInstruction::Offset(reg, off)));
245 }
246 &UnwindInst::Aarch64SetPointerAuth { return_addresses } => {
247 instructions.push((
248 instruction_offset,
249 CallFrameInstruction::Aarch64SetPointerAuth { return_addresses },
250 ));
251 }
252 }
253 }
254
255 Ok(UnwindInfo {
256 instructions,
257 len: code_len as u32,
258 })
259}
260
261impl UnwindInfo {
262 pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry {
264 let mut fde = FrameDescriptionEntry::new(address, self.len);
265
266 for (offset, inst) in &self.instructions {
267 fde.add_instruction(*offset, inst.clone().into());
268 }
269
270 fde
271 }
272}