diff --git a/CHANGELOG.md b/CHANGELOG.md index 1857a3f10..54f3b557e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- A new CLI argument `rom-elf` was added which will be used for backtraces (#963) + ### Changed ### Fixed diff --git a/cargo-espflash/src/main.rs b/cargo-espflash/src/main.rs index 41b1c7e9a..967b293fc 100644 --- a/cargo-espflash/src/main.rs +++ b/cargo-espflash/src/main.rs @@ -413,9 +413,11 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> { monitor_args.elf = Some(build_ctx.artifact_path); + let elfs = vec![elf_data.as_ref()]; + monitor( flasher.into(), - Some(&elf_data), + elfs, pid, monitor_args, args.connect_args.non_interactive, diff --git a/espflash/resources/roms/esp32_rev0_rom.elf b/espflash/resources/roms/esp32_rev0_rom.elf new file mode 100644 index 000000000..f457c04cb Binary files /dev/null and b/espflash/resources/roms/esp32_rev0_rom.elf differ diff --git a/espflash/resources/roms/esp32_rev300_rom.elf b/espflash/resources/roms/esp32_rev300_rom.elf new file mode 100644 index 000000000..3498acb0c Binary files /dev/null and b/espflash/resources/roms/esp32_rev300_rom.elf differ diff --git a/espflash/resources/roms/esp32c2_rev100_rom.elf b/espflash/resources/roms/esp32c2_rev100_rom.elf new file mode 100644 index 000000000..aea32f2ff Binary files /dev/null and b/espflash/resources/roms/esp32c2_rev100_rom.elf differ diff --git a/espflash/resources/roms/esp32c3_rev0_rom.elf b/espflash/resources/roms/esp32c3_rev0_rom.elf new file mode 100644 index 000000000..863b44fc6 Binary files /dev/null and b/espflash/resources/roms/esp32c3_rev0_rom.elf differ diff --git a/espflash/resources/roms/esp32c3_rev101_rom.elf b/espflash/resources/roms/esp32c3_rev101_rom.elf new file mode 100644 index 000000000..ba59424f5 Binary files /dev/null and b/espflash/resources/roms/esp32c3_rev101_rom.elf differ diff --git a/espflash/resources/roms/esp32c3_rev3_rom.elf b/espflash/resources/roms/esp32c3_rev3_rom.elf new file mode 100644 index 000000000..73da11867 Binary files /dev/null and b/espflash/resources/roms/esp32c3_rev3_rom.elf differ diff --git a/espflash/resources/roms/esp32c6_rev0_rom.elf b/espflash/resources/roms/esp32c6_rev0_rom.elf new file mode 100644 index 000000000..de12387b3 Binary files /dev/null and b/espflash/resources/roms/esp32c6_rev0_rom.elf differ diff --git a/espflash/resources/roms/esp32h2_rev0_rom.elf b/espflash/resources/roms/esp32h2_rev0_rom.elf new file mode 100644 index 000000000..e47754d26 Binary files /dev/null and b/espflash/resources/roms/esp32h2_rev0_rom.elf differ diff --git a/espflash/resources/roms/esp32p4_rev0_rom.elf b/espflash/resources/roms/esp32p4_rev0_rom.elf new file mode 100644 index 000000000..a8a486f69 Binary files /dev/null and b/espflash/resources/roms/esp32p4_rev0_rom.elf differ diff --git a/espflash/resources/roms/esp32s2_rev0_rom.elf b/espflash/resources/roms/esp32s2_rev0_rom.elf new file mode 100644 index 000000000..0f6586229 Binary files /dev/null and b/espflash/resources/roms/esp32s2_rev0_rom.elf differ diff --git a/espflash/resources/roms/esp32s3_rev0_rom.elf b/espflash/resources/roms/esp32s3_rev0_rom.elf new file mode 100644 index 000000000..3448c5dfd Binary files /dev/null and b/espflash/resources/roms/esp32s3_rev0_rom.elf differ diff --git a/espflash/src/bin/espflash.rs b/espflash/src/bin/espflash.rs index bde23e8d8..0a18f864b 100644 --- a/espflash/src/bin/espflash.rs +++ b/espflash/src/bin/espflash.rs @@ -267,7 +267,7 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> { check_idf_bootloader(&elf_data)?; } - print_board_info(&mut flasher)?; + let dev_info = print_board_info(&mut flasher)?; ensure_chip_compatibility(chip, Some(elf_data.as_slice()))?; let mut flash_config = args.flash_config_args; @@ -326,9 +326,21 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> { monitor_args.elf = Some(args.image); + let mut elfs = Vec::new(); + elfs.push(elf_data.as_ref()); + + let rom_elf; + if let Some(rom) = &monitor_args.rom_elf { + rom_elf = std::fs::read(rom).unwrap(); + elfs.push(rom_elf.as_ref()); + } else if let Some(rom) = dev_info.rom() { + rom_elf = rom; + elfs.push(rom_elf.as_ref()) + } + monitor( flasher.into(), - Some(&elf_data), + elfs, pid, monitor_args, args.connect_args.non_interactive, diff --git a/espflash/src/cli/mod.rs b/espflash/src/cli/mod.rs index 8627f1c4b..ec833a2c2 100644 --- a/espflash/src/cli/mod.rs +++ b/espflash/src/cli/mod.rs @@ -42,6 +42,7 @@ use crate::{ }, error::{Error, MissingPartition, MissingPartitionTable}, flasher::{ + DeviceInfo, FLASH_SECTOR_SIZE, FlashData, FlashFrequency, @@ -303,6 +304,10 @@ pub struct MonitorConfigArgs { /// ELF image to load the symbols from #[arg(long, value_name = "FILE")] pub elf: Option, + /// Optional ELF image to load ROM symbols from - if not given espflash will + /// try to use a matching built-in ROM + #[arg(long, value_name = "FILE")] + pub rom_elf: Option, /// Avoids restarting the device before monitoring /// /// Only valid when `non_interactive` is also set. @@ -607,7 +612,7 @@ pub fn parse_chip_rev(chip_rev: &str) -> Result { } /// Print information about a chip -pub fn print_board_info(flasher: &mut Flasher) -> Result<()> { +pub fn print_board_info(flasher: &mut Flasher) -> Result { let info = flasher.device_info()?; print!("Chip type: {}", info.chip); @@ -621,11 +626,11 @@ pub fn print_board_info(flasher: &mut Flasher) -> Result<()> { println!("Flash size: {}", info.flash_size); println!("Features: {}", info.features.join(", ")); - if let Some(mac) = info.mac_address { + if let Some(ref mac) = info.mac_address { println!("MAC address: {mac}"); } - Ok(()) + Ok(info) } /// Open a serial monitor @@ -633,7 +638,7 @@ pub fn serial_monitor(args: MonitorArgs, config: &Config) -> Result<()> { let mut flasher = connect(&args.connect_args, config, true, true)?; let pid = flasher.connection().usb_pid(); - let elf = if let Some(elf_path) = args.monitor_args.elf.clone() { + let firmware_elf = if let Some(elf_path) = args.monitor_args.elf.clone() { let path = fs::canonicalize(elf_path).into_diagnostic()?; let data = fs::read(path).into_diagnostic()?; @@ -643,8 +648,9 @@ pub fn serial_monitor(args: MonitorArgs, config: &Config) -> Result<()> { }; let chip = flasher.chip(); + let dev_info = flasher.device_info()?; - ensure_chip_compatibility(chip, elf.as_deref())?; + ensure_chip_compatibility(chip, firmware_elf.as_deref())?; let mut monitor_args = args.monitor_args; @@ -657,9 +663,23 @@ pub fn serial_monitor(args: MonitorArgs, config: &Config) -> Result<()> { monitor_args.monitor_baud = 74_880; } + let mut elfs: Vec<&[u8]> = Vec::new(); + if let Some(firmware_elf) = firmware_elf.as_ref() { + elfs.push(firmware_elf); + } + + let rom_elf; + if let Some(ref rom) = monitor_args.rom_elf { + rom_elf = std::fs::read(rom).unwrap(); + elfs.push(rom_elf.as_ref()); + } else if let Some(rom) = dev_info.rom() { + rom_elf = rom; + elfs.push(rom_elf.as_ref()) + } + monitor( flasher.into(), - elf.as_deref(), + elfs, pid, monitor_args, args.connect_args.non_interactive, @@ -1162,7 +1182,7 @@ pub fn write_bin(args: WriteBinArgs, config: &Config) -> Result<()> { } monitor( flasher.into(), - None, + Vec::new(), pid, monitor_args, args.connect_args.non_interactive, diff --git a/espflash/src/cli/monitor/mod.rs b/espflash/src/cli/monitor/mod.rs index 286fbe4d0..7b7da2c93 100644 --- a/espflash/src/cli/monitor/mod.rs +++ b/espflash/src/cli/monitor/mod.rs @@ -77,7 +77,7 @@ impl Drop for RawModeGuard { /// parser. pub fn monitor( mut serial: Port, - elf: Option<&[u8]>, + elfs: Vec<&[u8]>, pid: u16, monitor_args: MonitorConfigArgs, non_interactive: bool, @@ -104,19 +104,20 @@ pub fn monitor( // We are in raw mode until `_raw_mode` is dropped (ie. this function returns). let _raw_mode = RawModeGuard::new(); + let firmware_elf = elfs.first().map(|v| &**v); let stdout = stdout(); let mut stdout = if monitor_args.no_addresses { - ResolvingPrinter::new_no_addresses(elf, stdout.lock()) + ResolvingPrinter::new_no_addresses(firmware_elf, stdout.lock()) } else { - ResolvingPrinter::new(elf, stdout.lock()) + ResolvingPrinter::new(elfs, stdout.lock()) }; let mut parser: Box = match monitor_args .log_format - .unwrap_or_else(|| deduce_log_format(elf)) + .unwrap_or_else(|| deduce_log_format(firmware_elf)) { LogFormat::Defmt => Box::new(parser::esp_defmt::EspDefmt::new( - elf, + firmware_elf, monitor_args.output_format, )?), LogFormat::Serial => { diff --git a/espflash/src/cli/monitor/parser/mod.rs b/espflash/src/cli/monitor/parser/mod.rs index 083cbdcc1..affa53eb9 100644 --- a/espflash/src/cli/monitor/parser/mod.rs +++ b/espflash/src/cli/monitor/parser/mod.rs @@ -108,8 +108,8 @@ impl Utf8Merger { #[allow(missing_debug_implementations)] pub struct ResolvingPrinter<'ctx, W: Write> { writer: W, - symbols: Option>, - elf: Option<&'ctx [u8]>, + symbols: Vec>, + elfs: Vec<&'ctx [u8]>, merger: Utf8Merger, line_fragment: String, disable_address_resolution: bool, @@ -117,11 +117,14 @@ pub struct ResolvingPrinter<'ctx, W: Write> { impl<'ctx, W: Write> ResolvingPrinter<'ctx, W> { /// Creates a new `ResolvingPrinter` with the given ELF file and writer. - pub fn new(elf: Option<&'ctx [u8]>, writer: W) -> Self { + pub fn new(elf: Vec<&'ctx [u8]>, writer: W) -> Self { Self { writer, - symbols: elf.and_then(|elf| Symbols::try_from(elf).ok()), - elf, + symbols: elf + .iter() + .filter_map(|elf| Symbols::try_from(elf).ok()) + .collect(), + elfs: elf, merger: Utf8Merger::new(), line_fragment: String::new(), disable_address_resolution: false, @@ -132,8 +135,8 @@ impl<'ctx, W: Write> ResolvingPrinter<'ctx, W> { pub fn new_no_addresses(_elf: Option<&'ctx [u8]>, writer: W) -> Self { Self { writer, - symbols: None, // Don't load symbols when address resolution is disabled - elf: None, + symbols: Vec::new(), // Don't load symbols when address resolution is disabled + elfs: Vec::new(), merger: Utf8Merger::new(), line_fragment: String::new(), disable_address_resolution: true, @@ -176,24 +179,21 @@ impl Write for ResolvingPrinter<'_, W> { // If we have loaded some symbols and address resolution is not disabled... if !self.disable_address_resolution { - if let Some(symbols) = self.symbols.as_ref() { + for symbols in &self.symbols { // Try to print the names of addresses in the current line. resolve_addresses(symbols, &line, &mut self.writer)?; } - if line.starts_with(stack_dump::MARKER) { - if let Some(symbols) = self.symbols.as_ref() { - if stack_dump::backtrace_from_stack_dump( - &line, - &mut self.writer, - self.elf, - symbols, - ) - .is_err() - { - self.writer.queue(Print("\nUnable to decode stack-dump. Double check `-Cforce-unwind-tables` is used.\n"))?; - } - } + if line.starts_with(stack_dump::MARKER) + && stack_dump::backtrace_from_stack_dump( + &line, + &mut self.writer, + &self.elfs, + &self.symbols, + ) + .is_err() + { + self.writer.queue(Print("\nUnable to decode stack-dump. Double check `-Cforce-unwind-tables` is used.\n"))?; } } } diff --git a/espflash/src/cli/monitor/stack_dump.rs b/espflash/src/cli/monitor/stack_dump.rs index 2024fcd15..5529b583d 100644 --- a/espflash/src/cli/monitor/stack_dump.rs +++ b/espflash/src/cli/monitor/stack_dump.rs @@ -10,51 +10,52 @@ pub(crate) const MARKER: &str = "STACKDUMP: "; pub(crate) fn backtrace_from_stack_dump( line: &str, out: &mut dyn Write, - elf: Option<&[u8]>, - symbols: &Symbols<'_>, + elfs: &Vec<&[u8]>, + symbols: &Vec>, ) -> std::io::Result<()> { - if let Some(elf) = elf { - if let Some(remaining) = line.to_string().strip_prefix(MARKER) { - let mut split = remaining.split(" "); - let (address, stack) = { - let first = split.next(); - let second = split.next(); - - (first, second) - }; - - if let Some(address) = address { - if let Some(stack) = stack { - if stack.len() % 2 != 0 { - return Ok(()); - } + if let Some(remaining) = line.to_string().strip_prefix(MARKER) { + let mut split = remaining.split(" "); + let (address, stack) = { + let first = split.next(); + let second = split.next(); + + (first, second) + }; + + if let Some(address) = address { + if let Some(stack) = stack { + if stack.len() % 2 != 0 { + return Ok(()); + } - let mut pc = u32::from_str_radix(address, 16).unwrap_or_default(); - let mut stack_bytes = Vec::new(); - for byte_chars in stack.chars().collect::>().chunks(2) { - if byte_chars.len() == 2 { - stack_bytes.push( - u8::from_str_radix( - &format!("{}{}", byte_chars[0], byte_chars[1]), - 16, - ) + let mut pc = u32::from_str_radix(address, 16).unwrap_or_default(); + let mut stack_bytes = Vec::new(); + for byte_chars in stack.chars().collect::>().chunks(2) { + if byte_chars.len() == 2 { + stack_bytes.push( + u8::from_str_radix(&format!("{}{}", byte_chars[0], byte_chars[1]), 16) .unwrap_or_default(), - ); - } + ); } + } - let func_info = get_func_info(elf)?; + let mut func_info = Vec::new(); + for elf in elfs { + func_info.append(&mut get_func_info(elf)?); + } - writeln!(out).ok(); - let mut index = 0; - loop { - let func = func_info.iter().find(|f| f.start <= pc && f.end >= pc); - if let Some(func) = func { - if func.stack_frame_size == 0 { - break; - } + writeln!(out).ok(); + let mut index = 0; + loop { + let func = func_info.iter().find(|f| f.start <= pc && f.end >= pc); + if let Some(func) = func { + if func.stack_frame_size == 0 { + break; + } - let lookup_pc = pc as u64 - 4; + let lookup_pc = pc as u64 - 4; + + for symbols in symbols { let name = symbols.name(lookup_pc); let location = symbols.location(lookup_pc); if let Some(name) = name { @@ -64,25 +65,25 @@ pub(crate) fn backtrace_from_stack_dump( writeln!(out, "{name}\r\n at ??:??\r\n").ok(); } } + } - if index + func.stack_frame_size as usize > stack_bytes.len() { - break; - } - - let next_pc_pos = index + (func.stack_frame_size as usize - 4); - - pc = u32::from_le_bytes( - stack_bytes[next_pc_pos..][..4] - .try_into() - .unwrap_or_default(), - ); - index += func.stack_frame_size as usize; - } else { + if index + func.stack_frame_size as usize > stack_bytes.len() { break; } + + let next_pc_pos = index + (func.stack_frame_size as usize - 4); + + pc = u32::from_le_bytes( + stack_bytes[next_pc_pos..][..4] + .try_into() + .unwrap_or_default(), + ); + index += func.stack_frame_size as usize; + } else { + break; } - writeln!(out).ok(); } + writeln!(out).ok(); } } } diff --git a/espflash/src/flasher/mod.rs b/espflash/src/flasher/mod.rs index d53a9159d..a301ec350 100644 --- a/espflash/src/flasher/mod.rs +++ b/espflash/src/flasher/mod.rs @@ -458,6 +458,55 @@ pub struct DeviceInfo { pub mac_address: Option, } +impl DeviceInfo { + #[doc(hidden)] + pub fn rom(&self) -> Option> { + match self.chip { + Chip::Esp32 => { + if let Some((major, _)) = self.revision { + if major >= 3 { + Some(include_bytes!("../../resources/roms/esp32_rev300_rom.elf").into()) + } else { + Some(include_bytes!("../../resources/roms/esp32_rev0_rom.elf").into()) + } + } else { + None + } + } + Chip::Esp32c2 => { + Some(include_bytes!("../../resources/roms/esp32c2_rev100_rom.elf").into()) + } + Chip::Esp32c3 => { + if let Some((major, _)) = self.revision { + if major >= 3 { + Some(include_bytes!("../../resources/roms/esp32c3_rev3_rom.elf").into()) + } else { + Some(include_bytes!("../../resources/roms/esp32c3_rev0_rom.elf").into()) + } + } else { + None + } + } + Chip::Esp32c5 => None, + Chip::Esp32c6 => { + Some(include_bytes!("../../resources/roms/esp32c6_rev0_rom.elf").into()) + } + Chip::Esp32h2 => { + Some(include_bytes!("../../resources/roms/esp32h2_rev0_rom.elf").into()) + } + Chip::Esp32p4 => { + Some(include_bytes!("../../resources/roms/esp32p4_rev0_rom.elf").into()) + } + Chip::Esp32s2 => { + Some(include_bytes!("../../resources/roms/esp32s2_rev0_rom.elf").into()) + } + Chip::Esp32s3 => { + Some(include_bytes!("../../resources/roms/esp32s3_rev0_rom.elf").into()) + } + } + } +} + /// Connect to and flash a target device #[cfg(feature = "serialport")] #[derive(Debug)]