1use super::unwindregs::UnwindRegsAarch64;
2use crate::add_signed::checked_add_signed;
3use crate::error::Error;
4
5use crate::unwind_rule::UnwindRule;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum UnwindRuleAarch64 {
9 NoOp,
14 NoOpIfFirstFrameOtherwiseFp,
17 OffsetSp { sp_offset_by_16: u16 },
22 OffsetSpIfFirstFrameOtherwiseStackEndsHere { sp_offset_by_16: u16 },
27 OffsetSpAndRestoreLr {
29 sp_offset_by_16: u16,
30 lr_storage_offset_from_sp_by_8: i16,
31 },
32 OffsetSpAndRestoreFpAndLr {
34 sp_offset_by_16: u16,
35 fp_storage_offset_from_sp_by_8: i16,
36 lr_storage_offset_from_sp_by_8: i16,
37 },
38 UseFramePointer,
40 UseFramepointerWithOffsets {
42 sp_offset_from_fp_by_8: u16,
43 fp_storage_offset_from_fp_by_8: i16,
44 lr_storage_offset_from_fp_by_8: i16,
45 },
46}
47
48impl UnwindRule for UnwindRuleAarch64 {
49 type UnwindRegs = UnwindRegsAarch64;
50
51 fn rule_for_stub_functions() -> Self {
52 UnwindRuleAarch64::NoOp
53 }
54 fn rule_for_function_start() -> Self {
55 UnwindRuleAarch64::NoOp
56 }
57 fn fallback_rule() -> Self {
58 UnwindRuleAarch64::UseFramePointer
59 }
60
61 fn exec<F>(
62 self,
63 is_first_frame: bool,
64 regs: &mut UnwindRegsAarch64,
65 read_stack: &mut F,
66 ) -> Result<Option<u64>, Error>
67 where
68 F: FnMut(u64) -> Result<u64, ()>,
69 {
70 let lr = regs.lr();
71 let sp = regs.sp();
72 let fp = regs.fp();
73
74 let (new_lr, new_sp, new_fp) = match self {
75 UnwindRuleAarch64::NoOp => {
76 if !is_first_frame {
77 return Err(Error::DidNotAdvance);
78 }
79 (lr, sp, fp)
80 }
81 UnwindRuleAarch64::NoOpIfFirstFrameOtherwiseFp => {
82 if is_first_frame {
83 (lr, sp, fp)
84 } else {
85 let fp = regs.fp();
86 let new_sp = fp.checked_add(16).ok_or(Error::IntegerOverflow)?;
87 let new_lr =
88 read_stack(fp + 8).map_err(|_| Error::CouldNotReadStack(fp + 8))?;
89 let new_fp = read_stack(fp).map_err(|_| Error::CouldNotReadStack(fp))?;
90 if new_sp <= sp {
91 return Err(Error::FramepointerUnwindingMovedBackwards);
92 }
93 (new_lr, new_sp, new_fp)
94 }
95 }
96 UnwindRuleAarch64::OffsetSpIfFirstFrameOtherwiseStackEndsHere { sp_offset_by_16 } => {
97 if !is_first_frame {
98 return Ok(None);
99 }
100 let sp_offset = u64::from(sp_offset_by_16) * 16;
101 let new_sp = sp.checked_add(sp_offset).ok_or(Error::IntegerOverflow)?;
102 (lr, new_sp, fp)
103 }
104 UnwindRuleAarch64::OffsetSp { sp_offset_by_16 } => {
105 if !is_first_frame {
106 return Err(Error::DidNotAdvance);
107 }
108 let sp_offset = u64::from(sp_offset_by_16) * 16;
109 let new_sp = sp.checked_add(sp_offset).ok_or(Error::IntegerOverflow)?;
110 (lr, new_sp, fp)
111 }
112 UnwindRuleAarch64::OffsetSpAndRestoreLr {
113 sp_offset_by_16,
114 lr_storage_offset_from_sp_by_8,
115 } => {
116 let sp_offset = u64::from(sp_offset_by_16) * 16;
117 let new_sp = sp.checked_add(sp_offset).ok_or(Error::IntegerOverflow)?;
118 let lr_storage_offset = i64::from(lr_storage_offset_from_sp_by_8) * 8;
119 let lr_location =
120 checked_add_signed(sp, lr_storage_offset).ok_or(Error::IntegerOverflow)?;
121 let new_lr =
122 read_stack(lr_location).map_err(|_| Error::CouldNotReadStack(lr_location))?;
123 (new_lr, new_sp, fp)
124 }
125 UnwindRuleAarch64::OffsetSpAndRestoreFpAndLr {
126 sp_offset_by_16,
127 fp_storage_offset_from_sp_by_8,
128 lr_storage_offset_from_sp_by_8,
129 } => {
130 let sp_offset = u64::from(sp_offset_by_16) * 16;
131 let new_sp = sp.checked_add(sp_offset).ok_or(Error::IntegerOverflow)?;
132 let lr_storage_offset = i64::from(lr_storage_offset_from_sp_by_8) * 8;
133 let lr_location =
134 checked_add_signed(sp, lr_storage_offset).ok_or(Error::IntegerOverflow)?;
135 let new_lr =
136 read_stack(lr_location).map_err(|_| Error::CouldNotReadStack(lr_location))?;
137 let fp_storage_offset = i64::from(fp_storage_offset_from_sp_by_8) * 8;
138 let fp_location =
139 checked_add_signed(sp, fp_storage_offset).ok_or(Error::IntegerOverflow)?;
140 let new_fp =
141 read_stack(fp_location).map_err(|_| Error::CouldNotReadStack(fp_location))?;
142 (new_lr, new_sp, new_fp)
143 }
144 UnwindRuleAarch64::UseFramePointer => {
145 let fp = regs.fp();
184 let new_sp = fp.checked_add(16).ok_or(Error::IntegerOverflow)?;
185 let new_lr = read_stack(fp + 8).map_err(|_| Error::CouldNotReadStack(fp + 8))?;
186 let new_fp = read_stack(fp).map_err(|_| Error::CouldNotReadStack(fp))?;
187 if new_fp == 0 {
188 return Ok(None);
189 }
190 if new_fp <= fp || new_sp <= sp {
191 return Err(Error::FramepointerUnwindingMovedBackwards);
192 }
193 (new_lr, new_sp, new_fp)
194 }
195 UnwindRuleAarch64::UseFramepointerWithOffsets {
196 sp_offset_from_fp_by_8,
197 fp_storage_offset_from_fp_by_8,
198 lr_storage_offset_from_fp_by_8,
199 } => {
200 let sp_offset_from_fp = u64::from(sp_offset_from_fp_by_8) * 8;
201 let new_sp = fp
202 .checked_add(sp_offset_from_fp)
203 .ok_or(Error::IntegerOverflow)?;
204 let lr_storage_offset = i64::from(lr_storage_offset_from_fp_by_8) * 8;
205 let lr_location =
206 checked_add_signed(fp, lr_storage_offset).ok_or(Error::IntegerOverflow)?;
207 let new_lr =
208 read_stack(lr_location).map_err(|_| Error::CouldNotReadStack(lr_location))?;
209 let fp_storage_offset = i64::from(fp_storage_offset_from_fp_by_8) * 8;
210 let fp_location =
211 checked_add_signed(fp, fp_storage_offset).ok_or(Error::IntegerOverflow)?;
212 let new_fp =
213 read_stack(fp_location).map_err(|_| Error::CouldNotReadStack(fp_location))?;
214
215 if new_fp == 0 {
216 return Ok(None);
217 }
218 if new_fp <= fp || new_sp <= sp {
219 return Err(Error::FramepointerUnwindingMovedBackwards);
220 }
221 (new_lr, new_sp, new_fp)
222 }
223 };
224 let return_address = regs.lr_mask().strip_ptr_auth(new_lr);
225 if return_address == 0 {
226 return Ok(None);
227 }
228 if !is_first_frame && new_sp == sp {
229 return Err(Error::DidNotAdvance);
230 }
231 regs.set_lr(new_lr);
232 regs.set_sp(new_sp);
233 regs.set_fp(new_fp);
234
235 Ok(Some(return_address))
236 }
237}
238
239#[cfg(test)]
240mod test {
241 use super::*;
242
243 #[test]
244 fn test_basic() {
245 let stack = [
246 1, 2, 3, 4, 0x40, 0x100200, 5, 6, 0x70, 0x100100, 7, 8, 9, 10, 0x0, 0x0,
247 ];
248 let mut read_stack = |addr| Ok(stack[(addr / 8) as usize]);
249 let mut regs = UnwindRegsAarch64::new(0x100300, 0x10, 0x20);
250 let res = UnwindRuleAarch64::NoOp.exec(true, &mut regs, &mut read_stack);
251 assert_eq!(res, Ok(Some(0x100300)));
252 assert_eq!(regs.sp(), 0x10);
253 let res = UnwindRuleAarch64::UseFramePointer.exec(false, &mut regs, &mut read_stack);
254 assert_eq!(res, Ok(Some(0x100200)));
255 assert_eq!(regs.sp(), 0x30);
256 assert_eq!(regs.fp(), 0x40);
257 let res = UnwindRuleAarch64::UseFramePointer.exec(false, &mut regs, &mut read_stack);
258 assert_eq!(res, Ok(Some(0x100100)));
259 assert_eq!(regs.sp(), 0x50);
260 assert_eq!(regs.fp(), 0x70);
261 let res = UnwindRuleAarch64::UseFramePointer.exec(false, &mut regs, &mut read_stack);
262 assert_eq!(res, Ok(None));
263 }
264}