1 //! System V ABI unwind information.
2
3 use crate::binemit::CodeOffset;
4 use crate::isa::unwind::input;
5 use crate::isa::unwind::UnwindInst;
6 use crate::result::{CodegenError, CodegenResult};
7 use alloc::vec::Vec;
8 use gimli::write::{Address, FrameDescriptionEntry};
9
10 #[cfg(feature = "enable-serde")]
11 use serde::{Deserialize, Serialize};
12
13 type Register = u16;
14
15 /// Enumerate the errors possible in mapping Cranelift registers to their DWARF equivalent.
16 #[allow(missing_docs)]
17 #[derive(Debug, PartialEq, Eq)]
18 pub enum RegisterMappingError {
19 MissingBank,
20 UnsupportedArchitecture,
21 UnsupportedRegisterBank(&'static str),
22 }
23
24 // This is manually implementing Error and Display instead of using thiserror to reduce the amount
25 // of dependencies used by Cranelift.
26 impl std::error::Error for RegisterMappingError {}
27
28 impl std::fmt::Display for RegisterMappingError {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result29 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 // This mirrors gimli's CallFrameInstruction, but is serializable
44 // This excludes CfaExpression, Expression, ValExpression due to
45 // https://github.com/gimli-rs/gimli/issues/513.
46 // TODO: if gimli ever adds serialization support, remove this type
47 #[derive(Clone, Debug, PartialEq, Eq)]
48 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
49 pub(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 /// Enables or disables pointer authentication on aarch64 platforms post ARMv8.3. This
63 /// particular item maps to gimli::ValExpression(RA_SIGN_STATE, lit0/lit1).
64 Aarch64SetPointerAuth {
65 return_addresses: bool,
66 },
67 }
68
69 impl From<gimli::write::CallFrameInstruction> for CallFrameInstruction {
from(cfi: gimli::write::CallFrameInstruction) -> Self70 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 // Cranelift's unwind support does not generate `CallFrameInstruction`s with
88 // Expression at this moment, and it is not trivial to
89 // serialize such instructions.
90 panic!("CallFrameInstruction with Expression not supported");
91 }
92 }
93 }
94 }
95
96 impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
into(self) -> gimli::write::CallFrameInstruction97 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 // To enable pointer authentication for return addresses in dwarf directives, we
117 // use a small dwarf expression that sets the value of the pseudo-register
118 // RA_SIGN_STATE (RA stands for return address) to 0 or 1. This behavior is
119 // documented in
120 // https://github.com/ARM-software/abi-aa/blob/master/aadwarf64/aadwarf64.rst#41dwarf-register-names.
121 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
134 /// Maps UnwindInfo register to gimli's index space.
135 pub(crate) trait RegisterMapper<Reg> {
136 /// Maps Reg.
map(&self, reg: Reg) -> Result<Register, RegisterMappingError>137 fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
138 /// Gets stack pointer register.
sp(&self) -> Register139 fn sp(&self) -> Register;
140 /// Gets the frame pointer register, if any.
fp(&self) -> Option<Register>141 fn fp(&self) -> Option<Register> {
142 None
143 }
144 /// Gets the link register, if any.
lr(&self) -> Option<Register>145 fn lr(&self) -> Option<Register> {
146 None
147 }
148 /// What is the offset from saved FP to saved LR?
lr_offset(&self) -> Option<u32>149 fn lr_offset(&self) -> Option<u32> {
150 None
151 }
152 }
153
154 /// Represents unwind information for a single System V ABI function.
155 ///
156 /// This representation is not ISA specific.
157 #[derive(Clone, Debug, PartialEq, Eq)]
158 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
159 pub struct UnwindInfo {
160 instructions: Vec<(u32, CallFrameInstruction)>,
161 len: u32,
162 }
163
create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>( insts: &[(CodeOffset, UnwindInst)], code_len: usize, mr: &MR, ) -> CodegenResult<UnwindInfo>164 pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::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 // Define CFA in terms of current SP (SP changed and we haven't
179 // set FP yet).
180 instructions.push((
181 instruction_offset,
182 CallFrameInstruction::CfaOffset(offset_upward_to_caller_sp as i32),
183 ));
184 // Note that we saved the old FP value on the stack. Use of this
185 // operation implies that the target defines a FP register.
186 instructions.push((
187 instruction_offset,
188 CallFrameInstruction::Offset(
189 mr.fp().unwrap(),
190 -(offset_upward_to_caller_sp as i32),
191 ),
192 ));
193 // If there is a link register on this architecture, note that
194 // we saved it as well.
195 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 // Define CFA in terms of FP. Note that we assume it was already
211 // defined correctly in terms of the current SP, and FP has just
212 // been set to the current SP, so we do not need to change the
213 // offset, only the register. (This is done only if the target
214 // defines a frame pointer register.)
215 if let Some(fp) = mr.fp() {
216 instructions.push((instruction_offset, CallFrameInstruction::CfaRegister(fp)));
217 }
218 // Record initial CFA offset. This will be used with later
219 // StackAlloc calls if we do not have a frame pointer.
220 cfa_offset = offset_upward_to_caller_sp;
221 // Record distance from CFA downward to clobber area so we can
222 // express clobber offsets later in terms of CFA.
223 clobber_offset_to_cfa = offset_upward_to_caller_sp + offset_downward_to_clobbers;
224 }
225 &UnwindInst::StackAlloc { size } => {
226 // If we do not use a frame pointer, we need to update the
227 // CFA offset whenever the stack pointer changes.
228 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.to_reg())
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
261 impl UnwindInfo {
262 // TODO: remove `build()` below when old backend is removed. The new backend uses a simpler
263 // approach in `create_unwind_info_from_insts()` above.
264
build<'b, Reg: PartialEq + Copy>( unwind: input::UnwindInfo<Reg>, map_reg: &'b dyn RegisterMapper<Reg>, ) -> CodegenResult<Self>265 pub(crate) fn build<'b, Reg: PartialEq + Copy>(
266 unwind: input::UnwindInfo<Reg>,
267 map_reg: &'b dyn RegisterMapper<Reg>,
268 ) -> CodegenResult<Self> {
269 use input::UnwindCode;
270 let mut builder = InstructionBuilder::new(unwind.initial_sp_offset, map_reg);
271
272 for (offset, c) in unwind.prologue_unwind_codes.iter().chain(
273 unwind
274 .epilogues_unwind_codes
275 .iter()
276 .map(|c| c.iter())
277 .flatten(),
278 ) {
279 match c {
280 UnwindCode::SaveRegister { reg, stack_offset } => {
281 builder
282 .save_reg(*offset, *reg, *stack_offset)
283 .map_err(CodegenError::RegisterMappingError)?;
284 }
285 UnwindCode::StackAlloc { size } => {
286 builder.adjust_sp_down_imm(*offset, *size as i64);
287 }
288 UnwindCode::StackDealloc { size } => {
289 builder.adjust_sp_up_imm(*offset, *size as i64);
290 }
291 UnwindCode::RestoreRegister { reg } => {
292 builder
293 .restore_reg(*offset, *reg)
294 .map_err(CodegenError::RegisterMappingError)?;
295 }
296 UnwindCode::SetFramePointer { reg } => {
297 builder
298 .set_cfa_reg(*offset, *reg)
299 .map_err(CodegenError::RegisterMappingError)?;
300 }
301 UnwindCode::RestoreFramePointer => {
302 builder.restore_cfa(*offset);
303 }
304 UnwindCode::RememberState => {
305 builder.remember_state(*offset);
306 }
307 UnwindCode::RestoreState => {
308 builder.restore_state(*offset);
309 }
310 UnwindCode::Aarch64SetPointerAuth { return_addresses } => {
311 builder.set_aarch64_pauth(*offset, *return_addresses);
312 }
313 }
314 }
315
316 let instructions = builder.instructions;
317 let len = unwind.function_size;
318
319 Ok(Self { instructions, len })
320 }
321
322 /// Converts the unwind information into a `FrameDescriptionEntry`.
to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry323 pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry {
324 let mut fde = FrameDescriptionEntry::new(address, self.len);
325
326 for (offset, inst) in &self.instructions {
327 fde.add_instruction(*offset, inst.clone().into());
328 }
329
330 fde
331 }
332 }
333
334 // TODO: delete the builder below when the old backend is removed.
335
336 struct InstructionBuilder<'a, Reg: PartialEq + Copy> {
337 sp_offset: i32,
338 frame_register: Option<Reg>,
339 saved_state: Option<(i32, Option<Reg>)>,
340 map_reg: &'a dyn RegisterMapper<Reg>,
341 instructions: Vec<(u32, CallFrameInstruction)>,
342 }
343
344 impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
new(sp_offset: u8, map_reg: &'a (dyn RegisterMapper<Reg> + 'a)) -> Self345 fn new(sp_offset: u8, map_reg: &'a (dyn RegisterMapper<Reg> + 'a)) -> Self {
346 Self {
347 sp_offset: sp_offset as i32, // CFA offset starts at the specified offset to account for the return address on stack
348 saved_state: None,
349 frame_register: None,
350 map_reg,
351 instructions: Vec::new(),
352 }
353 }
354
save_reg( &mut self, offset: u32, reg: Reg, stack_offset: u32, ) -> Result<(), RegisterMappingError>355 fn save_reg(
356 &mut self,
357 offset: u32,
358 reg: Reg,
359 stack_offset: u32,
360 ) -> Result<(), RegisterMappingError> {
361 // Pushes in the prologue are register saves, so record an offset of the save
362 self.instructions.push((
363 offset,
364 CallFrameInstruction::Offset(
365 self.map_reg.map(reg)?,
366 stack_offset as i32 - self.sp_offset,
367 ),
368 ));
369
370 Ok(())
371 }
372
adjust_sp_down_imm(&mut self, offset: u32, imm: i64)373 fn adjust_sp_down_imm(&mut self, offset: u32, imm: i64) {
374 assert!(imm <= core::u32::MAX as i64);
375
376 self.sp_offset += imm as i32;
377
378 // Don't adjust the CFA if we're using a frame pointer
379 if self.frame_register.is_some() {
380 return;
381 }
382
383 self.instructions
384 .push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
385 }
386
adjust_sp_up_imm(&mut self, offset: u32, imm: i64)387 fn adjust_sp_up_imm(&mut self, offset: u32, imm: i64) {
388 assert!(imm <= core::u32::MAX as i64);
389
390 self.sp_offset -= imm as i32;
391
392 // Don't adjust the CFA if we're using a frame pointer
393 if self.frame_register.is_some() {
394 return;
395 }
396
397 let cfa_inst_ofs = {
398 // Scan to find and merge with CFA instruction with the same offset.
399 let mut it = self.instructions.iter_mut();
400 loop {
401 match it.next_back() {
402 Some((i_offset, i)) if *i_offset == offset => {
403 if let CallFrameInstruction::Cfa(_, o) = i {
404 break Some(o);
405 }
406 }
407 _ => {
408 break None;
409 }
410 }
411 }
412 };
413
414 if let Some(o) = cfa_inst_ofs {
415 // Update previous CFA instruction.
416 *o = self.sp_offset;
417 } else {
418 // Add just CFA offset instruction.
419 self.instructions
420 .push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
421 }
422 }
423
set_cfa_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError>424 fn set_cfa_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> {
425 self.instructions.push((
426 offset,
427 CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?),
428 ));
429 self.frame_register = Some(reg);
430 Ok(())
431 }
432
restore_cfa(&mut self, offset: u32)433 fn restore_cfa(&mut self, offset: u32) {
434 // Restore SP and its offset.
435 self.instructions.push((
436 offset,
437 CallFrameInstruction::Cfa(self.map_reg.sp(), self.sp_offset),
438 ));
439 self.frame_register = None;
440 }
441
restore_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError>442 fn restore_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> {
443 // Pops in the epilogue are register restores, so record a "same value" for the register
444 self.instructions.push((
445 offset,
446 CallFrameInstruction::SameValue(self.map_reg.map(reg)?),
447 ));
448
449 Ok(())
450 }
451
remember_state(&mut self, offset: u32)452 fn remember_state(&mut self, offset: u32) {
453 self.saved_state = Some((self.sp_offset, self.frame_register));
454
455 self.instructions
456 .push((offset, CallFrameInstruction::RememberState));
457 }
458
restore_state(&mut self, offset: u32)459 fn restore_state(&mut self, offset: u32) {
460 let (sp_offset, frame_register) = self.saved_state.take().unwrap();
461 self.sp_offset = sp_offset;
462 self.frame_register = frame_register;
463
464 self.instructions
465 .push((offset, CallFrameInstruction::RestoreState));
466 }
467
set_aarch64_pauth(&mut self, offset: u32, return_addresses: bool)468 fn set_aarch64_pauth(&mut self, offset: u32, return_addresses: bool) {
469 self.instructions.push((
470 offset,
471 CallFrameInstruction::Aarch64SetPointerAuth { return_addresses },
472 ));
473 }
474 }
475