Skip to content

Commit 8aa91ef

Browse files
authored
Check chip using metadata (#816)
* Verify chip from build info Move metadata handling out of Symbols Clippy * Undo visibility change
1 parent 74bf802 commit 8aa91ef

File tree

9 files changed

+116
-39
lines changed

9 files changed

+116
-39
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- `espflash` can detect the log format automatically from ESP-HAL metadata. Reqires `esp-println` 0.14 (presumably, yet to be released) (#809)
2121
- Add `--output-format` option to monitor (#818)
2222
- Added chip detection based on security info, where supported (#814)
23+
- `espflash` can detect the chip from ESP-HAL metadata to prevent flashing firmware built for a different device. Reqires `esp-hal` 1.0.0-beta.0 (presumably, yet to be released) (#816)
2324

2425
### Changed
2526

cargo-espflash/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> {
313313
let elf_data = fs::read(build_ctx.artifact_path.clone()).into_diagnostic()?;
314314

315315
print_board_info(&mut flasher)?;
316+
ensure_chip_compatibility(chip, Some(elf_data.as_slice()))?;
316317

317318
let mut flash_config = args.build_args.flash_config_args;
318319
flash_config.flash_size = flash_config

espflash/src/bin/espflash.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> {
226226
let elf_data = fs::read(&args.image).into_diagnostic()?;
227227

228228
print_board_info(&mut flasher)?;
229+
ensure_chip_compatibility(chip, Some(elf_data.as_slice()))?;
229230

230231
let mut flash_config = args.flash_config_args;
231232
flash_config.flash_size = flash_config

espflash/src/cli/mod.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::{
1818
path::{Path, PathBuf},
1919
};
2020

21-
use clap::Args;
21+
use clap::{Args, ValueEnum};
2222
use clap_complete::Shell;
2323
use comfy_table::{modifiers, presets::UTF8_FULL, Attribute, Cell, Color, Table};
2424
use esp_idf_part::{DataType, Partition, PartitionTable};
@@ -44,6 +44,7 @@ use crate::{
4444
ProgressCallbacks,
4545
FLASH_SECTOR_SIZE,
4646
},
47+
image_format::Metadata,
4748
targets::{Chip, XtalFrequency},
4849
};
4950

@@ -579,6 +580,9 @@ pub fn serial_monitor(args: MonitorArgs, config: &Config) -> Result<()> {
579580
};
580581

581582
let chip = flasher.chip();
583+
584+
ensure_chip_compatibility(chip, elf.as_deref())?;
585+
582586
let target = chip.into_target();
583587

584588
let mut monitor_args = args.monitor_args;
@@ -1023,7 +1027,7 @@ pub fn write_bin(args: WriteBinArgs, config: &Config) -> Result<()> {
10231027
}
10241028
let mut buffer = Vec::with_capacity(size.try_into().into_diagnostic()?);
10251029
f.read_to_end(&mut buffer).into_diagnostic()?;
1026-
buffer.extend(std::iter::repeat(0xFF).take(padded_bytes as usize));
1030+
buffer.extend(std::iter::repeat_n(0xFF, padded_bytes as usize));
10271031

10281032
flasher.write_bin_to_flash(
10291033
args.address,
@@ -1066,6 +1070,23 @@ pub fn hold_in_reset(args: ConnectArgs, config: &Config) -> Result<()> {
10661070
Ok(())
10671071
}
10681072

1073+
pub fn ensure_chip_compatibility(chip: Chip, elf: Option<&[u8]>) -> Result<()> {
1074+
let metadata = Metadata::from_bytes(elf);
1075+
let Some(elf_chip) = metadata.chip_name() else {
1076+
// No chip name in the ELF, assume compatible
1077+
return Ok(());
1078+
};
1079+
1080+
match Chip::from_str(elf_chip, false) {
1081+
Ok(elf_chip) if chip == elf_chip => Ok(()),
1082+
_ => Err(Error::FirmwareChipMismatch {
1083+
elf: elf_chip.to_string(),
1084+
detected: chip,
1085+
})
1086+
.into_diagnostic(),
1087+
}
1088+
}
1089+
10691090
mod test {
10701091
use clap::Parser;
10711092

espflash/src/cli/monitor/mod.rs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,11 @@ use strum::{Display, EnumIter, EnumString, VariantNames};
2828

2929
use crate::{
3030
cli::{
31-
monitor::{
32-
parser::{InputParser, ResolvingPrinter},
33-
symbols::Symbols,
34-
},
31+
monitor::parser::{InputParser, ResolvingPrinter},
3532
MonitorConfigArgs,
3633
},
3734
connection::{reset::reset_after_flash, Port},
35+
image_format::Metadata,
3836
};
3937

4038
pub mod external_processors;
@@ -192,28 +190,16 @@ fn key_event() -> std::io::Result<Option<KeyEvent>> {
192190
}
193191

194192
fn deduce_log_format(elf: Option<&[u8]>) -> LogFormat {
195-
let Some(elf) = elf else {
193+
let metadata = Metadata::from_bytes(elf);
194+
let Some(log_format) = metadata.log_format() else {
196195
return LogFormat::Serial;
197196
};
198197

199-
let Ok(symbols) = Symbols::try_from(elf) else {
200-
return LogFormat::Serial;
201-
};
202-
203-
let Some(format_symbol) =
204-
symbols.symbol_data(Some(".espressif.metadata"), b"espflash.LOG_FORMAT")
205-
else {
206-
return LogFormat::Serial;
207-
};
208-
209-
match format_symbol {
210-
b"defmt-espflash" => LogFormat::Defmt,
211-
b"serial" => LogFormat::Serial,
198+
match log_format {
199+
"defmt-espflash" => LogFormat::Defmt,
200+
"serial" => LogFormat::Serial,
212201
other => {
213-
warn!(
214-
"Unknown log format symbol: {}. Defaulting to serial.",
215-
String::from_utf8_lossy(other),
216-
);
202+
warn!("Unknown log format symbol: {other}. Defaulting to serial.");
217203
LogFormat::Serial
218204
}
219205
}

espflash/src/cli/monitor/symbols.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,4 @@ impl<'sym> Symbols<'sym> {
9898
}
9999
})?
100100
}
101-
102-
pub(crate) fn symbol_data(
103-
&self,
104-
section_name: Option<&str>,
105-
name_bytes: &[u8],
106-
) -> Option<&'sym [u8]> {
107-
let sym = self.object.symbol_by_name_bytes(name_bytes)?;
108-
109-
let section = match section_name {
110-
Some(section_name) => self.object.section_by_name(section_name)?,
111-
None => self.object.section_by_index(sym.section_index()?).ok()?,
112-
};
113-
section.data_range(sym.address(), sym.size()).ok().flatten()
114-
}
115101
}

espflash/src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,13 @@ pub enum Error {
221221
help("`address` and `size` must be multiples of 0x1000 (4096)")
222222
)]
223223
InvalidEraseRegionArgument { address: u32, size: u32 },
224+
225+
#[error("The firmware was built for {elf}, but the detected chip is {detected}")]
226+
#[diagnostic(
227+
code(espflash::chip_mismatch),
228+
help("Ensure that the device is connected and your host recognizes the serial adapter")
229+
)]
230+
FirmwareChipMismatch { elf: String, detected: Chip },
224231
}
225232

226233
#[cfg(feature = "serialport")]

espflash/src/image_format/metadata.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use std::{collections::HashMap, error::Error};
2+
3+
use object::{File, Object, ObjectSection, ObjectSymbol};
4+
5+
#[derive(Debug, Clone)]
6+
pub struct Metadata {
7+
symbols: HashMap<String, Vec<u8>>,
8+
}
9+
10+
impl Metadata {
11+
fn empty() -> Self {
12+
Self {
13+
symbols: HashMap::new(),
14+
}
15+
}
16+
17+
pub fn from_bytes(bytes: Option<&[u8]>) -> Self {
18+
match Self::try_from(bytes) {
19+
Ok(metadata) => metadata,
20+
Err(_) => Self::empty(),
21+
}
22+
}
23+
24+
pub fn try_from(bytes: Option<&[u8]>) -> Result<Self, Box<dyn Error>> {
25+
const METADATA_SECTION: &str = ".espressif.metadata";
26+
27+
let Some(bytes) = bytes else {
28+
return Ok(Self::empty());
29+
};
30+
31+
let object = File::parse(bytes)?;
32+
if object.section_by_name(METADATA_SECTION).is_none() {
33+
return Ok(Self::empty());
34+
}
35+
36+
let mut this = Self::empty();
37+
for symbol in object.symbols() {
38+
let Some(sym_section_idx) = symbol.section_index() else {
39+
continue;
40+
};
41+
let sym_section = object.section_by_index(sym_section_idx)?;
42+
if sym_section.name().ok() != Some(METADATA_SECTION) {
43+
// Skip symbols that are not in the metadata section.
44+
continue;
45+
}
46+
47+
let name = symbol.name()?.to_string();
48+
let data = sym_section
49+
.data_range(symbol.address(), symbol.size())?
50+
.map(|b| b.to_vec());
51+
52+
if let Some(data) = data {
53+
this.symbols.insert(name, data);
54+
}
55+
}
56+
57+
Ok(this)
58+
}
59+
60+
fn read_string<'f>(&'f self, name: &str) -> Option<&'f str> {
61+
self.symbols
62+
.get(name)
63+
.and_then(|data| std::str::from_utf8(data).ok())
64+
}
65+
66+
pub fn chip_name(&self) -> Option<&str> {
67+
self.read_string("build_info.CHIP_NAME")
68+
}
69+
70+
pub fn log_format(&self) -> Option<&str> {
71+
self.read_string("espflash.LOG_FORMAT")
72+
}
73+
}

espflash/src/image_format/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ use object::{
1616
ObjectSection as _,
1717
};
1818

19-
pub use self::esp_idf::IdfBootloaderFormat;
19+
pub use self::{esp_idf::IdfBootloaderFormat, metadata::Metadata};
2020
use crate::targets::Chip;
2121

2222
mod esp_idf;
23+
mod metadata;
2324

2425
/// A segment of code from the source ELF
2526
#[derive(Default, Clone, Eq)]

0 commit comments

Comments
 (0)