1use super::compact_unwind::{self, CompactEntry};
7use super::dwarf_cfi;
8use super::frame_pointer;
9use super::registers::RegisterContext;
10use super::{BinaryImageInfo, MemoryReader, UnwindError};
11
12const DEFAULT_MAX_DEPTH: u32 = 512;
14
15const ARM64_PAC_MASK: u64 = 0x0000_007F_FFFF_FFFF;
17
18pub struct FrameCursor<'a> {
20 reader: &'a dyn MemoryReader,
21 regs: RegisterContext,
22 images: &'a mut [BinaryImageInfo],
23 is_64_bit: bool,
24 depth: u32,
25 max_depth: u32,
26}
27
28impl<'a> FrameCursor<'a> {
29 pub fn new(
31 reader: &'a dyn MemoryReader,
32 regs: RegisterContext,
33 images: &'a mut [BinaryImageInfo],
34 is_64_bit: bool,
35 ) -> Self {
36 Self {
37 reader,
38 regs,
39 images,
40 is_64_bit,
41 depth: 0,
42 max_depth: DEFAULT_MAX_DEPTH,
43 }
44 }
45
46 pub fn pc(&self) -> Option<u64> {
48 self.regs.pc()
49 }
50
51 pub fn registers(&self) -> &RegisterContext {
53 &self.regs
54 }
55
56 pub fn step(&mut self) -> Result<bool, UnwindError> {
59 self.depth += 1;
60 if self.depth >= self.max_depth {
61 return Err(UnwindError::MaxDepthExceeded(self.max_depth));
62 }
63
64 let pc = self.regs.pc().ok_or(UnwindError::NullPC)?;
65
66 let pc = if self.regs.cpu_type == crate::types::CpuType::ARM64 {
68 pc & ARM64_PAC_MASK
69 } else {
70 pc
71 };
72
73 if pc == 0 || (self.is_64_bit && pc < 0x1000) || (!self.is_64_bit && pc < 0x100) {
75 return Ok(false);
76 }
77
78 let image_idx = self.find_image(pc);
80
81 if let Some(idx) = image_idx {
82 self.images[idx].resolve_sections(self.reader);
84 let image = &self.images[idx];
85
86 if let Some(ref unwind_info) = image.unwind_info
88 && let Some((encoding, func_base)) = compact_unwind::lookup_encoding(
89 self.reader,
90 unwind_info,
91 pc,
92 image.load_address,
93 )
94 {
95 let entry = compact_unwind::decode_encoding(encoding, self.regs.cpu_type);
96
97 match &entry {
98 CompactEntry::Dwarf { fde_offset } => {
99 if let Some(ref eh_frame) = self.images[idx].eh_frame {
101 let target_addr = eh_frame.vm_addr + *fde_offset as u64;
102 if let Some(fde) = dwarf_cfi::find_fde(
103 self.reader,
104 eh_frame,
105 target_addr,
106 self.is_64_bit,
107 ) {
108 let new_regs = dwarf_cfi::apply_dwarf_unwind(
109 &fde,
110 pc,
111 &self.regs,
112 self.reader,
113 self.is_64_bit,
114 )?;
115 self.regs = new_regs;
116 return Ok(true);
117 }
118 }
119 }
121 CompactEntry::None => {
122 }
124 _ => {
125 let func_start = self.images[idx].load_address + func_base as u64;
127 if compact_unwind::apply_entry(
128 &entry,
129 &mut self.regs,
130 self.reader,
131 func_start,
132 self.is_64_bit,
133 )? {
134 return Ok(true);
135 }
136 }
137 }
138 }
139
140 if let Some(ref eh_frame) = self.images[idx].eh_frame
142 && let Some(fde) = dwarf_cfi::find_fde(self.reader, eh_frame, pc, self.is_64_bit)
143 {
144 let result = dwarf_cfi::apply_dwarf_unwind(
145 &fde,
146 pc,
147 &self.regs,
148 self.reader,
149 self.is_64_bit,
150 );
151 if let Ok(new_regs) = result {
152 self.regs = new_regs;
153 return Ok(true);
154 }
155 }
157 }
158
159 frame_pointer::step_frame_pointer(self.reader, &mut self.regs, self.is_64_bit)
161 }
162
163 fn find_image(&self, pc: u64) -> Option<usize> {
164 self.images.iter().position(|img| img.contains(pc))
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use crate::types::CpuType;
172 use crate::unwind::SliceMemoryReader;
173
174 fn build_fp_chain_test() -> (SliceMemoryReader, RegisterContext, Vec<BinaryImageInfo>) {
176 let base = 0x1_0000_0000u64;
177 let mut data = vec![0u8; 0x10000];
178
179 let f0 = 0x8000usize;
181 data[f0..f0 + 8].copy_from_slice(&(base + 0x9000u64).to_le_bytes()); data[f0 + 8..f0 + 16].copy_from_slice(&(base + 0x2000u64).to_le_bytes()); let f1 = 0x9000usize;
186 data[f1..f1 + 8].copy_from_slice(&(base + 0xA000u64).to_le_bytes()); data[f1 + 8..f1 + 16].copy_from_slice(&(base + 0x3000u64).to_le_bytes()); let f2 = 0xA000usize;
191 data[f2..f2 + 8].copy_from_slice(&0u64.to_le_bytes()); data[f2 + 8..f2 + 16].copy_from_slice(&(base + 0x4000u64).to_le_bytes());
193
194 let reader = SliceMemoryReader {
195 data,
196 base_address: base,
197 };
198
199 let mut regs = RegisterContext::new(CpuType::ARM64);
200 regs.set_fp(base + f0 as u64);
201 regs.set_sp(base + f0 as u64 - 16);
202 regs.set_pc(base + 0x1000); let images = vec![BinaryImageInfo {
205 name: "test".into(),
206 load_address: base,
207 end_address: base + 0x10000,
208 is_64_bit: true,
209 uuid: None,
210 unwind_info: None,
211 eh_frame: None,
212 text_section: None,
213 sections_resolved: true,
214 }];
215
216 (reader, regs, images)
217 }
218
219 #[test]
220 fn fp_fallback_walk() {
221 let (reader, regs, mut images) = build_fp_chain_test();
222 let base = 0x1_0000_0000u64;
223
224 let mut cursor = FrameCursor::new(&reader, regs, &mut images, true);
225
226 assert_eq!(cursor.pc(), Some(base + 0x1000));
228
229 assert!(cursor.step().unwrap());
231 assert_eq!(cursor.pc(), Some(base + 0x2000));
232
233 assert!(cursor.step().unwrap());
235 assert_eq!(cursor.pc(), Some(base + 0x3000));
236
237 assert!(cursor.step().unwrap());
239 assert_eq!(cursor.pc(), Some(base + 0x4000));
240
241 assert!(!cursor.step().unwrap());
243 }
244
245 #[test]
246 fn max_depth_exceeded() {
247 let (reader, regs, mut images) = build_fp_chain_test();
248
249 let mut cursor = FrameCursor::new(&reader, regs, &mut images, true);
250 cursor.max_depth = 2;
251
252 assert!(cursor.step().unwrap());
254 assert!(matches!(
256 cursor.step(),
257 Err(UnwindError::MaxDepthExceeded(2))
258 ));
259 }
260
261 #[test]
262 fn null_pc_stops() {
263 let reader = SliceMemoryReader {
264 data: vec![0u8; 0x100],
265 base_address: 0x1000,
266 };
267
268 let mut regs = RegisterContext::new(CpuType::ARM64);
269 regs.set_pc(0); regs.set_fp(0x1080);
271 regs.set_sp(0x1070);
272
273 let mut images: Vec<BinaryImageInfo> = vec![];
274 let mut cursor = FrameCursor::new(&reader, regs, &mut images, true);
275
276 assert!(!cursor.step().unwrap());
277 }
278}