stackdump_trace/platform/cortex_m/
mod.rs

1//! Trace implementation for the cortex m target
2
3use super::{Platform, UnwindResult};
4use crate::error::TraceError;
5use crate::{Frame, FrameType};
6use addr2line::object::{Object, ObjectSection, ObjectSymbol};
7use core::ops::Range;
8use gimli::{
9    BaseAddresses, CfaRule, DebugFrame, EndianSlice, LittleEndian, RegisterRule, RunTimeEndian,
10    UnwindContext, UnwindSection, UnwindTableRow,
11};
12use stackdump_core::device_memory::DeviceMemory;
13
14const THUMB_BIT: u32 = 1;
15const EXC_RETURN_MARKER: u32 = 0xFF00_0000;
16const EXC_RETURN_FTYPE_MASK: u32 = 1 << 4;
17
18pub struct CortexMPlatform<'data> {
19    debug_frame: DebugFrame<EndianSlice<'data, LittleEndian>>,
20    reset_vector_address_range: Range<u32>,
21    text_address_range: Range<u32>,
22    bases: BaseAddresses,
23    unwind_context: UnwindContext<EndianSlice<'data, LittleEndian>>,
24}
25
26impl<'data> CortexMPlatform<'data> {
27    fn apply_unwind_info(
28        device_memory: &mut DeviceMemory<<Self as Platform<'data>>::Word>,
29        unwind_info: UnwindTableRow<EndianSlice<LittleEndian>>,
30    ) -> Result<bool, TraceError> {
31        let updated = match unwind_info.cfa() {
32            CfaRule::RegisterAndOffset { register, offset } => {
33                let new_cfa = (device_memory.register(*register)? as i64 + *offset) as u32;
34                let old_cfa = device_memory.register(gimli::Arm::SP)?;
35                let changed = new_cfa != old_cfa;
36                *device_memory.register_mut(gimli::Arm::SP)? = new_cfa;
37                changed
38            }
39            CfaRule::Expression(_) => todo!("CfaRule::Expression"),
40        };
41
42        for (reg, rule) in unwind_info.registers() {
43            match rule {
44                RegisterRule::Undefined => unreachable!(),
45                RegisterRule::Offset(offset) => {
46                    let cfa = device_memory.register(gimli::Arm::SP)?;
47                    let addr = (i64::from(cfa) + offset) as u64;
48                    let new_value = device_memory
49                        .read_u32(addr, RunTimeEndian::Little)?
50                        .ok_or(TraceError::MissingMemory(addr))?;
51                    *device_memory.register_mut(*reg)? = new_value;
52                }
53                _ => unimplemented!(),
54            }
55        }
56
57        Ok(updated)
58    }
59
60    fn is_last_frame(
61        &self,
62        device_memory: &DeviceMemory<<Self as Platform<'data>>::Word>,
63    ) -> Result<bool, TraceError> {
64        Ok(device_memory.register(gimli::Arm::LR)? == 0
65            || (!self
66                .text_address_range
67                .contains(device_memory.register_ref(gimli::Arm::PC)?)
68                && device_memory.register(gimli::Arm::LR)? < EXC_RETURN_MARKER))
69    }
70
71    /// Assumes we are at an exception point in the stack unwinding.
72    /// Reads the registers that were stored on the stack and updates our current register representation with it.
73    ///
74    /// Returns Ok if everything went fine or an error with an address if the stack could not be read
75    fn update_registers_with_exception_stack(
76        device_memory: &mut DeviceMemory<<Self as Platform<'data>>::Word>,
77        fpu: bool,
78    ) -> Result<(), TraceError> {
79        let current_sp = device_memory.register(gimli::Arm::SP)?;
80
81        fn read_stack_var(
82            device_memory: &DeviceMemory<u32>,
83            starting_sp: u32,
84            index: usize,
85        ) -> Result<u32, TraceError> {
86            device_memory
87                .read_u32(starting_sp as u64 + index as u64 * 4, RunTimeEndian::Little)?
88                .ok_or(TraceError::MissingMemory(
89                    starting_sp as u64 + index as u64 * 4,
90                ))
91        }
92
93        *device_memory.register_mut(gimli::Arm::R0)? =
94            read_stack_var(device_memory, current_sp, 0)?;
95        *device_memory.register_mut(gimli::Arm::R1)? =
96            read_stack_var(device_memory, current_sp, 1)?;
97        *device_memory.register_mut(gimli::Arm::R2)? =
98            read_stack_var(device_memory, current_sp, 2)?;
99        *device_memory.register_mut(gimli::Arm::R3)? =
100            read_stack_var(device_memory, current_sp, 3)?;
101        *device_memory.register_mut(gimli::Arm::R12)? =
102            read_stack_var(device_memory, current_sp, 4)?;
103        *device_memory.register_mut(gimli::Arm::LR)? =
104            read_stack_var(device_memory, current_sp, 5)?;
105        *device_memory.register_mut(gimli::Arm::PC)? =
106            read_stack_var(device_memory, current_sp, 6)?;
107        // At stack place 7 is the PSR register, but we don't need that, so we skip it
108
109        // Adjust the sp with the size of what we've read
110        *device_memory.register_mut(gimli::Arm::SP)? = device_memory.register(gimli::Arm::SP)?
111            + 8 * std::mem::size_of::<<Self as Platform>::Word>() as <Self as Platform>::Word;
112
113        if fpu {
114            *device_memory.register_mut(gimli::Arm::D0)? =
115                read_stack_var(device_memory, current_sp, 8)?;
116            *device_memory.register_mut(gimli::Arm::D1)? =
117                read_stack_var(device_memory, current_sp, 9)?;
118            *device_memory.register_mut(gimli::Arm::D2)? =
119                read_stack_var(device_memory, current_sp, 10)?;
120            *device_memory.register_mut(gimli::Arm::D3)? =
121                read_stack_var(device_memory, current_sp, 11)?;
122            *device_memory.register_mut(gimli::Arm::D4)? =
123                read_stack_var(device_memory, current_sp, 12)?;
124            *device_memory.register_mut(gimli::Arm::D5)? =
125                read_stack_var(device_memory, current_sp, 13)?;
126            *device_memory.register_mut(gimli::Arm::D6)? =
127                read_stack_var(device_memory, current_sp, 14)?;
128            *device_memory.register_mut(gimli::Arm::D7)? =
129                read_stack_var(device_memory, current_sp, 15)?;
130            *device_memory.register_mut(gimli::Arm::D8)? =
131                read_stack_var(device_memory, current_sp, 16)?;
132            *device_memory.register_mut(gimli::Arm::D9)? =
133                read_stack_var(device_memory, current_sp, 17)?;
134            *device_memory.register_mut(gimli::Arm::D10)? =
135                read_stack_var(device_memory, current_sp, 18)?;
136            *device_memory.register_mut(gimli::Arm::D11)? =
137                read_stack_var(device_memory, current_sp, 19)?;
138            *device_memory.register_mut(gimli::Arm::D12)? =
139                read_stack_var(device_memory, current_sp, 20)?;
140            *device_memory.register_mut(gimli::Arm::D13)? =
141                read_stack_var(device_memory, current_sp, 21)?;
142            *device_memory.register_mut(gimli::Arm::D14)? =
143                read_stack_var(device_memory, current_sp, 22)?;
144            *device_memory.register_mut(gimli::Arm::D15)? =
145                read_stack_var(device_memory, current_sp, 23)?;
146            // At stack place 24 is the fpscr register, but we don't need that, so we skip it
147
148            // Adjust the sp with the size of what we've read
149            *device_memory.register_mut(gimli::Arm::SP)? =
150                device_memory.register(gimli::Arm::SP)? + 17 * std::mem::size_of::<u32>() as u32;
151        }
152
153        Ok(())
154    }
155}
156
157impl<'data> Platform<'data> for CortexMPlatform<'data> {
158    type Word = u32;
159
160    fn create_context(elf: &addr2line::object::File<'data, &'data [u8]>) -> Result<Self, TraceError>
161    where
162        Self: Sized,
163    {
164        let debug_info_sector_data = elf
165            .section_by_name(".debug_frame")
166            .ok_or_else(|| TraceError::MissingElfSection(".debug_frame".into()))?
167            .data()?;
168        let mut debug_frame =
169            addr2line::gimli::DebugFrame::new(debug_info_sector_data, LittleEndian);
170        debug_frame.set_address_size(std::mem::size_of::<Self::Word>() as u8);
171
172        let vector_table_section = elf
173            .section_by_name(".vector_table")
174            .ok_or_else(|| TraceError::MissingElfSection(".vector_table".into()))?;
175        let vector_table = vector_table_section
176            .data()?
177            .chunks_exact(4)
178            .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
179            .collect::<Vec<_>>();
180        let reset_vector_address = vector_table[1];
181        let reset_vector_address_range = elf
182            .symbols()
183            .find(|sym| sym.address() as u32 == reset_vector_address)
184            .map(|reset_vector_symbol| {
185                reset_vector_symbol.address() as u32
186                    ..reset_vector_symbol.address() as u32 + reset_vector_symbol.size() as u32
187            })
188            .unwrap_or(reset_vector_address..reset_vector_address);
189        let text_section = elf
190            .section_by_name(".text")
191            .ok_or_else(|| TraceError::MissingElfSection(".text".into()))?;
192        let text_address_range = (text_section.address() as u32)
193            ..(text_section.address() as u32 + text_section.size() as u32);
194
195        let bases = BaseAddresses::default();
196        let unwind_context = UnwindContext::new();
197
198        Ok(Self {
199            debug_frame,
200            reset_vector_address_range,
201            text_address_range,
202            bases,
203            unwind_context,
204        })
205    }
206
207    fn unwind(
208        &mut self,
209        device_memory: &mut DeviceMemory<Self::Word>,
210        previous_frame: Option<&mut Frame<Self::Word>>,
211    ) -> Result<super::UnwindResult<Self::Word>, TraceError> {
212        let unwind_info = self.debug_frame.unwind_info_for_address(
213            &self.bases,
214            &mut self.unwind_context,
215            device_memory.register(gimli::Arm::PC)? as u64,
216            DebugFrame::cie_from_offset,
217        );
218
219        let unwind_info = match unwind_info {
220            Ok(unwind_info) => unwind_info.clone(),
221            Err(_e) => {
222                return Ok(UnwindResult::Corrupted {error_frame: Some(Frame { function: "Unknown".into(), location: crate::Location { file: None, line: None, column: None }, frame_type: FrameType::Corrupted(format!("debug information for address {:#x} is missing. Likely fixes:
223                1. compile the Rust code with `debug = 1` or higher. This is configured in the `profile.{{release,bench}}` sections of Cargo.toml (`profile.{{dev,test}}` default to `debug = 2`)
224                2. use a recent version of the `cortex-m` crates (e.g. cortex-m 0.6.3 or newer). Check versions in Cargo.lock
225                3. if linking to C code, compile the C code with the `-g` flag", device_memory.register(gimli::Arm::PC)?)),
226                    variables: Vec::new(), }) });
227            }
228        };
229
230        // We can update the stackpointer and other registers to the previous frame by applying the unwind info
231        let stack_pointer_changed = match Self::apply_unwind_info(device_memory, unwind_info) {
232            Ok(stack_pointer_changed) => stack_pointer_changed,
233            Err(e) => {
234                return Ok(UnwindResult::Corrupted {
235                    error_frame: Some(Frame {
236                        function: "Unknown".into(),
237                        location: crate::Location {
238                            file: None,
239                            line: None,
240                            column: None,
241                        },
242                        frame_type: FrameType::Corrupted(e.to_string()),
243                        variables: Vec::new(),
244                    }),
245                });
246            }
247        };
248
249        // We're not at the last frame. What's the reason?
250
251        // Do we have a corrupted stack?
252        if !stack_pointer_changed
253            && device_memory.register(gimli::Arm::LR)? & !THUMB_BIT
254                == device_memory.register(gimli::Arm::PC)? & !THUMB_BIT
255        {
256            // The stack pointer didn't change and our LR points to our current PC
257            // If we unwound further we'd get the same frame again so we better stop
258
259            return Ok(UnwindResult::Corrupted {
260                error_frame: Some(Frame {
261                    function: "Unknown".into(),
262                    location: crate::Location {
263                        file: None,
264                        line: None,
265                        column: None,
266                    },
267                    frame_type: FrameType::Corrupted(
268                        "CFA did not change and LR and PC are equal".into(),
269                    ),
270                    variables: Vec::new(),
271                }),
272            });
273        }
274
275        // Stack is not corrupted, but unwinding is not done
276        // Are we returning from an exception? (EXC_RETURN)
277        if device_memory.register(gimli::Arm::LR)? >= EXC_RETURN_MARKER {
278            // Yes, so the registers were pushed to the stack and we need to get them back
279
280            // Check the value to know if there are fpu registers to read
281            let fpu = device_memory.register(gimli::Arm::LR)? & EXC_RETURN_FTYPE_MASK > 0;
282
283            if let Some(previous_frame) = previous_frame {
284                previous_frame.frame_type = FrameType::Exception;
285            }
286
287            match Self::update_registers_with_exception_stack(device_memory, fpu) {
288                Ok(()) => {}
289                Err(TraceError::MissingMemory(address)) => {
290                    return Ok(UnwindResult::Corrupted {
291                        error_frame: Some(Frame {
292                            function: "Unknown".into(),
293                            location: crate::Location {
294                                file: None,
295                                line: None,
296                                column: None,
297                            },
298                            frame_type: FrameType::Corrupted(format!(
299                                "Could not read address {:#10X} from the stack",
300                                address
301                            )),
302                            variables: Vec::new(),
303                        }),
304                    });
305                }
306                Err(e) => return Err(e),
307            }
308        } else {
309            // No exception, so follow the LR back
310            *device_memory.register_mut(gimli::Arm::PC)? = device_memory.register(gimli::Arm::LR)?
311        }
312
313        // Have we reached the reset vector?
314        if self
315            .reset_vector_address_range
316            .contains(device_memory.register_ref(gimli::Arm::PC)?)
317        {
318            // Yes, let's make that a frame as well
319            // We'll also make an assumption that there's no frames before reset
320            return Ok(UnwindResult::Finished);
321        }
322
323        if self.is_last_frame(device_memory)? {
324            Ok(UnwindResult::Finished)
325        } else {
326            // Is our stack pointer in a weird place?
327            if device_memory
328                .read_u32(
329                    device_memory.register(gimli::Arm::SP)? as u64,
330                    RunTimeEndian::Little,
331                )?
332                .is_none()
333            {
334                Ok(UnwindResult::Corrupted {error_frame:Some(Frame {
335                    function: "Unknown".into(),
336                    location: crate::Location { file: None, line: None, column: None },
337                    frame_type: FrameType::Corrupted(
338                        format!("The stack pointer ({:#08X}) is corrupted or the dump does not contain the full stack", device_memory
339                        .register(gimli::Arm::SP)?),
340                    ),
341                    variables: Vec::new(),
342                })})
343            } else {
344                Ok(UnwindResult::Proceeded)
345            }
346        }
347    }
348}