stackdump_trace/platform/
mod.rs1use crate::{error::TraceError, type_value_tree::TypeValueTree, Frame, FrameType, Location};
2use funty::Fundamental;
3use gimli::{DebugInfoOffset, EndianRcSlice, RunTimeEndian};
4use object::{Object, ObjectSection, ObjectSymbol, SectionKind};
5use stackdump_core::{device_memory::DeviceMemory, memory_region::VecMemoryRegion};
6use std::collections::HashMap;
7
8pub mod cortex_m;
9
10pub enum UnwindResult<ADDR: funty::Integral> {
12 Finished,
14 Corrupted {
16 error_frame: Option<Frame<ADDR>>,
18 },
19 Proceeded,
21}
22
23pub trait Platform<'data> {
24 type Word: funty::Integral;
25
26 fn create_context(elf: &object::File<'data, &'data [u8]>) -> Result<Self, TraceError>
27 where
28 Self: Sized;
29
30 fn unwind(
37 &mut self,
38 device_memory: &mut DeviceMemory<Self::Word>,
39 previous_frame: Option<&mut Frame<Self::Word>>,
40 ) -> Result<UnwindResult<Self::Word>, TraceError>;
41}
42
43pub fn trace<'data, P: Platform<'data>>(
53 mut device_memory: DeviceMemory<P::Word>,
54 elf_data: &'data [u8],
55) -> Result<Vec<Frame<P::Word>>, TraceError>
56where
57 <P::Word as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
58{
59 let elf = object::File::parse(elf_data)?;
61
62 for section in elf.sections().filter(|section| {
64 matches!(
65 section.kind(),
66 SectionKind::Text | SectionKind::ReadOnlyData | SectionKind::ReadOnlyString
67 )
68 }) {
69 device_memory.add_memory_region(VecMemoryRegion::new(
70 section.address(),
71 section.uncompressed_data()?.to_vec(),
72 ));
73 }
74
75 let endian = if elf.is_little_endian() {
76 gimli::RunTimeEndian::Little
77 } else {
78 gimli::RunTimeEndian::Big
79 };
80
81 fn load_section<'data: 'file, 'file, O, Endian>(
82 id: gimli::SectionId,
83 file: &'file O,
84 endian: Endian,
85 ) -> Result<gimli::EndianRcSlice<Endian>, TraceError>
86 where
87 O: object::Object<'data>,
88 Endian: gimli::Endianity,
89 {
90 let data = file
91 .section_by_name(id.name())
92 .and_then(|section| section.uncompressed_data().ok())
93 .unwrap_or(std::borrow::Cow::Borrowed(&[]));
94 Ok(gimli::EndianRcSlice::new(std::rc::Rc::from(&*data), endian))
95 }
96
97 let dwarf = gimli::Dwarf::load(|id| load_section(id, &elf, endian))?;
98
99 let mut frames = Vec::new();
101
102 let addr2line_context =
104 addr2line::Context::from_dwarf(gimli::Dwarf::load(|id| load_section(id, &elf, endian))?)?;
105
106 let mut platform_context = P::create_context(&elf)?;
108
109 let mut type_cache = Default::default();
110
111 loop {
113 match add_current_frames::<P>(
115 &device_memory,
116 &addr2line_context,
117 &mut frames,
118 &mut type_cache,
119 ) {
120 Ok(_) => {}
121 Err(e @ TraceError::DwarfUnitNotFound { pc: _ }) => {
122 frames.push(Frame {
123 function: "Unknown".into(),
124 location: Location::default(),
125 frame_type: FrameType::Corrupted(e.to_string()),
126 variables: Vec::default(),
127 });
128 break;
129 }
130 Err(e) => return Err(e),
131 }
132
133 match platform_context.unwind(&mut device_memory, frames.last_mut())? {
135 UnwindResult::Finished => {
136 frames.push(Frame {
137 function: "RESET".into(),
138 location: crate::Location {
139 file: None,
140 line: None,
141 column: None,
142 },
143 frame_type: FrameType::Function,
144 variables: Vec::new(),
145 });
146 break;
147 }
148 UnwindResult::Corrupted {
149 error_frame: Some(error_frame),
150 } => {
151 frames.push(error_frame);
152 break;
153 }
154 UnwindResult::Corrupted { error_frame: None } => {
155 break;
156 }
157 UnwindResult::Proceeded => {
158 continue;
159 }
160 }
161 }
162
163 let mut static_variables =
165 crate::variables::find_static_variables(&dwarf, &device_memory, &mut type_cache)?;
166
167 static_variables.retain(|var| {
169 let Some(linkage_name) = &var.linkage_name else {
170 return true;
173 };
174
175 if let Some(symbol) = elf.symbol_by_name(linkage_name) {
176 if let Some(section_index) = symbol.section_index() {
177 match elf.section_by_index(section_index) {
178 Ok(section) if section.kind() == SectionKind::Other => false,
180 Ok(_section) => true,
181 Err(e) => {
182 log::error!("Could not get section by index: {e}");
183 true
184 }
185 }
186 } else {
187 true
190 }
191 } else {
192 if var.address.is_none() || var.address == Some(0) {
203 false
205 } else {
206 true
208 }
209 }
210 });
211
212 let static_frame = Frame {
213 function: "Static".into(),
214 location: Location {
215 file: None,
216 line: None,
217 column: None,
218 },
219 frame_type: FrameType::Static,
220 variables: static_variables,
221 };
222 frames.push(static_frame);
223
224 Ok(frames)
226}
227
228fn add_current_frames<'a, P: Platform<'a>>(
229 device_memory: &DeviceMemory<P::Word>,
230 addr2line_context: &addr2line::Context<EndianRcSlice<RunTimeEndian>>,
231 frames: &mut Vec<Frame<P::Word>>,
232 type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<P::Word>, TraceError>>,
233) -> Result<(), TraceError>
234where
235 <P::Word as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
236{
237 let mut context_frames = addr2line_context
239 .find_frames(device_memory.register(gimli::Arm::PC)?.as_u64())
240 .skip_all_loads()?;
241
242 let unit_ref = addr2line_context
244 .find_dwarf_and_unit(device_memory.register(gimli::Arm::PC)?.as_u64())
245 .skip_all_loads()
246 .ok_or(TraceError::DwarfUnitNotFound {
247 pc: device_memory.register(gimli::Arm::PC)?.as_u64(),
248 })?;
249
250 let abbreviations = unit_ref.dwarf.abbreviations(&unit_ref.header)?;
252
253 let mut added_frames = 0;
255 while let Some(context_frame) = context_frames.next()? {
256 let (file, line, column) = context_frame
257 .location
258 .map(|l| {
259 (
260 l.file.map(|f| f.to_string()),
261 l.line.map(|line| line as _),
262 l.column.map(|column| column as _),
263 )
264 })
265 .unwrap_or_default();
266
267 let mut variables = Vec::new();
268
269 if let Some(die_offset) = context_frame.dw_die_offset {
270 let mut entries = match unit_ref
271 .header
272 .entries_tree(&abbreviations, Some(die_offset))
273 {
274 Ok(entries) => entries,
275 Err(_) => {
276 continue;
277 }
278 };
279
280 if let Ok(entry_root) = entries.root() {
281 variables = crate::variables::find_variables_in_function(
282 unit_ref.dwarf,
283 unit_ref.unit,
284 &abbreviations,
285 device_memory,
286 entry_root,
287 type_cache,
288 )?;
289 }
290 }
291
292 frames.push(Frame {
293 function: context_frame
294 .function
295 .and_then(|f| f.demangle().ok().map(|f| f.into_owned()))
296 .unwrap_or_else(|| "UNKNOWN".into()),
297 location: crate::Location { file, line, column },
298 frame_type: FrameType::InlineFunction,
299 variables,
300 });
301
302 added_frames += 1;
303 }
304
305 if added_frames > 0 {
306 frames.last_mut().unwrap().frame_type = FrameType::Function;
308 }
309
310 Ok(())
311}