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 pc = device_memory.register(gimli::Arm::PC)?.as_u64();
238
239 let mut context_frames = addr2line_context.find_frames(pc).skip_all_loads()?;
241
242 let unit_ref = addr2line_context
244 .find_dwarf_and_unit(pc)
245 .skip_all_loads()
246 .ok_or(TraceError::DwarfUnitNotFound { pc })?;
247
248 let abbreviations = unit_ref.dwarf.abbreviations(&unit_ref.header)?;
250
251 let mut added_frames = 0;
253 while let Some(context_frame) = context_frames.next()? {
254 let (file, line, column) = context_frame
255 .location
256 .map(|l| {
257 (
258 l.file.map(|f| f.to_string()),
259 l.line.map(|line| line as _),
260 l.column.map(|column| column as _),
261 )
262 })
263 .unwrap_or_default();
264
265 let mut variables = Vec::new();
266
267 if let Some(die_offset) = context_frame.dw_die_offset {
268 let mut entries = match unit_ref
269 .header
270 .entries_tree(&abbreviations, Some(die_offset))
271 {
272 Ok(entries) => entries,
273 Err(_) => {
274 continue;
275 }
276 };
277
278 if let Ok(entry_root) = entries.root() {
279 variables = crate::variables::find_variables_in_function(
280 unit_ref.dwarf,
281 unit_ref.unit,
282 &abbreviations,
283 device_memory,
284 entry_root,
285 type_cache,
286 )?;
287 }
288 }
289
290 frames.push(Frame {
291 function: context_frame
292 .function
293 .and_then(|f| f.demangle().ok().map(|f| f.into_owned()))
294 .unwrap_or_else(|| "UNKNOWN".into()),
295 location: crate::Location { file, line, column },
296 frame_type: FrameType::InlineFunction,
297 variables,
298 });
299
300 added_frames += 1;
301 }
302
303 if added_frames > 0 {
304 frames.last_mut().unwrap().frame_type = FrameType::Function;
306 }
307
308 Ok(())
309}