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