From 1ce2c6837a7baf97e6b48f5a76df949f83010e98 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Fri, 22 Dec 2023 13:31:59 +0200 Subject: [PATCH] Implement "exported" variables allowing to pull data from firmware. --- teleprobe-meta/teleprobe.x | 6 +++- teleprobe/src/run.rs | 59 +++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/teleprobe-meta/teleprobe.x b/teleprobe-meta/teleprobe.x index 119306a..e01dd76 100644 --- a/teleprobe-meta/teleprobe.x +++ b/teleprobe-meta/teleprobe.x @@ -8,4 +8,8 @@ SECTIONS { KEEP(*(.teleprobe.timeout)); } -} \ No newline at end of file + .teleprobe.export (INFO) : + { + KEEP(*(.teleprobe.export)); + } +} diff --git a/teleprobe/src/run.rs b/teleprobe/src/run.rs index edae9d2..2cf4b0d 100644 --- a/teleprobe/src/run.rs +++ b/teleprobe/src/run.rs @@ -56,6 +56,8 @@ struct Runner { defmt_stream: Box, di: DebugInfo, + + exports: BTreeMap, } unsafe fn fuck_it<'a, 'b, T>(wtf: &'a T) -> &'b T { @@ -117,6 +119,36 @@ impl Runner { let vector_table = vector_table.ok_or_else(|| anyhow!("`.vector_table` section is missing"))?; log::debug!("vector table: {:x?}", vector_table); + // Build a map of exported variables, based on addresses found in the + // '.teleprobe.export' section. + let mut export_map = BTreeMap::new(); + + if let Some(section) = elf.section_by_name(".teleprobe.export") { + let ptrs = section + .data()? + .chunks_exact(4) + .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap())) + .collect::>(); + + if ptrs.len() > 0 { + info!("Found {} exported variables: {:#0x?}", ptrs.len(), ptrs); + + // TODO: Is there a better way? + for symbol in elf.symbols() { + if ptrs.contains(&(symbol.address() as u32)) { + let addr = symbol.address() as u32; + let name = String::from(symbol.name().unwrap()); + // Filter out private symbols, otherwise we get some false-positives + if name.starts_with("__") { + continue; + } + export_map.insert(String::from(symbol.name().unwrap()), addr); + info!("Found match for addr {:?} -> {:?}", &addr, &name); + } + } + } + } + // reset ALL cores other than the main one. // This is needed for rp2040 core1. for (i, _) in sess.list_cores() { @@ -244,6 +276,7 @@ impl Runner { defmt, defmt_stream, di, + exports: export_map, }) } @@ -342,6 +375,30 @@ impl Runner { bail!("Firmware crashed"); } + // Look up exported symbols + for (symbol, addr) in &self.exports { + let mut buf = [0; 64]; + /* + // XXX: Ideally we should use demangling information to + // either handle references ie bytestrings are stored as + // pointers to data. But for now, we only support hardcoded + // byte arrays of length 64. + let ptr = core.read_word_32(*addr as u64).unwrap(); + let _ = core.read(ptr as u64, &mut buf).unwrap(); + */ + // TODO: Error handling? + let _ = core.read(*addr as u64, &mut buf).unwrap(); + match String::from_utf8(buf.to_vec()) { + Ok(s) => { + info!("Found: {} -> {}", symbol, s); + } + Err(_) => { + warn!("Data for {} not found!", symbol); + } + } + } + // TODO: Export our wanted symbols? + Ok(()) } @@ -439,7 +496,7 @@ impl Runner { fn dump_state(&mut self, core: &mut Core, force: bool) -> anyhow::Result { core.halt(TIMEOUT)?; - // determine if the target is handling an interupt + // determine if the target is handling an interrupt let xpsr: u32 = core.read_core_reg(XPSR)?; let exception_number = xpsr & 0xff; match exception_number {