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_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 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 fp(&self) -> Option<Register> {
140 None
141 }
142 fn lr(&self) -> Option<Register> {
144 None
145 }
146 fn lr_offset(&self) -> Option<u32> {
148 None
149 }
150}
151
152#[derive(Clone, Debug, PartialEq, Eq)]
156#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
157pub struct UnwindInfo {
158 instructions: Vec<(u32, CallFrameInstruction)>,
159 len: u32,
160}
161
162pub(crate) fn caller_sp_to_cfa_offset() -> u32 {
164 0
166}
167
168pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<Reg>>(
169 insts: &[(CodeOffset, UnwindInst)],
170 code_len: usize,
171 mr: &MR,
172) -> CodegenResult<UnwindInfo> {
173 let mut instructions = vec![];
174
175 let mut cfa_offset = 0;
176 let mut clobber_offset_to_cfa = 0;
177 for &(instruction_offset, ref inst) in insts {
178 match inst {
179 &UnwindInst::PushFrameRegs {
180 offset_upward_to_caller_sp,
181 } => {
182 instructions.push((
185 instruction_offset,
186 CallFrameInstruction::CfaOffset(offset_upward_to_caller_sp as i32),
187 ));
188 instructions.push((
191 instruction_offset,
192 CallFrameInstruction::Offset(
193 mr.fp().unwrap(),
194 -(offset_upward_to_caller_sp as i32),
195 ),
196 ));
197 if let Some(lr) = mr.lr() {
200 instructions.push((
201 instruction_offset,
202 CallFrameInstruction::Offset(
203 lr,
204 -(offset_upward_to_caller_sp as i32)
205 + mr.lr_offset().expect("LR offset not provided") as i32,
206 ),
207 ));
208 }
209 }
210 &UnwindInst::DefineNewFrame {
211 offset_upward_to_caller_sp,
212 offset_downward_to_clobbers,
213 } => {
214 if let Some(fp) = mr.fp() {
220 instructions.push((instruction_offset, CallFrameInstruction::CfaRegister(fp)));
221 }
222 cfa_offset = offset_upward_to_caller_sp;
225 clobber_offset_to_cfa = offset_upward_to_caller_sp + offset_downward_to_clobbers;
228 }
229 &UnwindInst::StackAlloc { size } => {
230 if mr.fp().is_none() {
233 cfa_offset += size;
234 instructions.push((
235 instruction_offset,
236 CallFrameInstruction::CfaOffset(cfa_offset as i32),
237 ));
238 }
239 }
240 &UnwindInst::SaveReg {
241 clobber_offset,
242 reg,
243 } => {
244 let reg = mr
245 .map(reg.into())
246 .map_err(|e| CodegenError::RegisterMappingError(e))?;
247 let off = (clobber_offset as i32) - (clobber_offset_to_cfa as i32);
248 instructions.push((instruction_offset, CallFrameInstruction::Offset(reg, off)));
249 }
250 &UnwindInst::RegStackOffset {
251 clobber_offset,
252 reg,
253 } => {
254 let reg = mr
255 .map(reg.into())
256 .map_err(|e| CodegenError::RegisterMappingError(e))?;
257 let off = (clobber_offset as i32) - (clobber_offset_to_cfa as i32);
258 instructions.push((
259 instruction_offset,
260 CallFrameInstruction::ValOffset(reg, off),
261 ));
262 }
263 &UnwindInst::Aarch64SetPointerAuth { return_addresses } => {
264 instructions.push((
265 instruction_offset,
266 CallFrameInstruction::Aarch64SetPointerAuth { return_addresses },
267 ));
268 }
269 }
270 }
271
272 Ok(UnwindInfo {
273 instructions,
274 len: code_len as u32,
275 })
276}
277
278impl UnwindInfo {
279 pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry {
281 let mut fde = FrameDescriptionEntry::new(address, self.len);
282
283 for (offset, inst) in &self.instructions {
284 fde.add_instruction(*offset, inst.clone().into());
285 }
286
287 fde
288 }
289}