diff --git a/decoder/src/elf2table/mod.rs b/decoder/src/elf2table/mod.rs index 6d0893218..af55d4d33 100644 --- a/decoder/src/elf2table/mod.rs +++ b/decoder/src/elf2table/mod.rs @@ -13,7 +13,7 @@ use std::{ path::{Path, PathBuf}, }; -use crate::{BitflagsKey, StringEntry, Table, TableEntry, Tag, DEFMT_VERSION}; +use crate::{BitflagsKey, Encoding, StringEntry, Table, TableEntry, Tag, DEFMT_VERSION}; use anyhow::{anyhow, bail, ensure}; use object::{Object, ObjectSection, ObjectSymbol}; @@ -21,9 +21,29 @@ pub fn parse_impl(elf: &[u8], check_version: bool) -> Result, anyh let elf = object::File::parse(elf)?; // first pass to extract the `_defmt_version` let mut version = None; - let is_defmt_version = |name: &str| { - name.starts_with("\"_defmt_version_ = ") || name.starts_with("_defmt_version_ = ") + let mut encoding = None; + + // Note that we check for a quoted and unquoted version symbol, since LLD has a bug that + // makes it keep the quotes from the linker script. + let try_get_version = |name: &str| { + if name.starts_with("\"_defmt_version_ = ") || name.starts_with("_defmt_version_ = ") { + Some( + name.trim_start_matches("\"_defmt_version_ = ") + .trim_start_matches("_defmt_version_ = ") + .trim_end_matches('"') + .to_string(), + ) + } else { + None + } + }; + + // No need to remove quotes for `_defmt_encoding_`, since it's + let try_get_encoding = |name: &str| { + name.strip_prefix("_defmt_encoding_ = ") + .map(ToString::to_string) }; + for entry in elf.symbols() { let name = match entry.name() { Ok(name) => name, @@ -32,13 +52,7 @@ pub fn parse_impl(elf: &[u8], check_version: bool) -> Result, anyh // Not in the `.defmt` section because it's not tied to the address of any symbol // in `.defmt`. - // Note that we check for a quoted and unquoted version symbol, since LLD has a bug that - // makes it keep the quotes from the linker script. - if is_defmt_version(name) { - let new_version = name - .trim_start_matches("\"_defmt_version_ = ") - .trim_start_matches("_defmt_version_ = ") - .trim_end_matches('"'); + if let Some(new_version) = try_get_version(name) { if let Some(version) = version { return Err(anyhow!( "multiple defmt versions in use: {} and {} (only one is supported)", @@ -48,6 +62,17 @@ pub fn parse_impl(elf: &[u8], check_version: bool) -> Result, anyh } version = Some(new_version); } + + if let Some(new_encoding) = try_get_encoding(name) { + if let Some(encoding) = encoding { + return Err(anyhow!( + "multiple defmt encodings in use: {} and {} (only one is supported)", + encoding, + new_encoding + )); + } + encoding = Some(new_encoding); + } } // NOTE: We need to make sure to return `Ok(None)`, not `Err`, when defmt is not in use. @@ -69,9 +94,18 @@ pub fn parse_impl(elf: &[u8], check_version: bool) -> Result, anyh }; if check_version { - self::check_version(version).map_err(anyhow::Error::msg)?; + self::check_version(&version).map_err(anyhow::Error::msg)?; } + let encoding = match encoding { + Some(e) => match &e[..] { + "raw" => Encoding::Raw, + "rzcobs" => Encoding::Rzcobs, + _ => bail!("Unknown defmt encoding '{}' specified. This is a bug.", e), + }, + None => bail!("No defmt encoding specified. This is a bug."), + }; + // second pass to demangle symbols let mut map = BTreeMap::new(); let mut bitflags_map = HashMap::new(); @@ -84,7 +118,7 @@ pub fn parse_impl(elf: &[u8], check_version: bool) -> Result, anyh _ => continue, }; - if is_defmt_version(name) || name.starts_with("__DEFMT_MARKER") { + if name.starts_with("_defmt") || name.starts_with("__DEFMT_MARKER") { // `_defmt_version_` is not a JSON encoded `defmt` symbol / log-message; skip it // LLD and GNU LD behave differently here. LLD doesn't include `_defmt_version_` // (defined in a linker script) in the `.defmt` section but GNU LD does. @@ -157,12 +191,12 @@ pub fn parse_impl(elf: &[u8], check_version: bool) -> Result, anyh } } - let mut table = Table::new(map); - if let Some(ts) = timestamp { - table.set_timestamp_entry(ts); - } - table.bitflags = bitflags_map; - Ok(Some(table)) + Ok(Some(Table { + entries: map, + timestamp, + bitflags: bitflags_map, + encoding, + })) } /// Checks if the version encoded in the symbol table is compatible with this version of the `decoder` crate diff --git a/decoder/src/lib.rs b/decoder/src/lib.rs index d7c5888b0..81a8353a5 100644 --- a/decoder/src/lib.rs +++ b/decoder/src/lib.rs @@ -116,24 +116,22 @@ struct BitflagsKey { disambig: String, } +#[derive(Copy, Clone, Debug, PartialEq)] +enum Encoding { + Raw, + Rzcobs, +} + /// Internal table that holds log levels and maps format strings to indices #[derive(Debug, PartialEq)] pub struct Table { timestamp: Option, entries: BTreeMap, bitflags: HashMap>, + encoding: Encoding, } impl Table { - /// NOTE caller must verify that defmt symbols are compatible with this version of the `decoder` crate using the `check_version` function - pub fn new(entries: BTreeMap) -> Self { - Self { - entries, - timestamp: None, - bitflags: Default::default(), - } - } - /// Parses an ELF file and returns the decoded `defmt` table. /// /// This function returns `None` if the ELF file contains no `.defmt` section. @@ -236,12 +234,10 @@ impl Table { } pub fn new_stream_decoder(&self) -> Box { - Box::new(stream::Rzcobs::new(self)) - } - - // temporary, will remove - pub fn new_stream_decoder_raw(&self) -> Box { - Box::new(stream::Raw::new(self)) + match self.encoding { + Encoding::Raw => Box::new(stream::Raw::new(self)), + Encoding::Rzcobs => Box::new(stream::Rzcobs::new(self)), + } } } @@ -326,6 +322,7 @@ mod tests { timestamp: None, entries: entries.into_iter().enumerate().collect(), bitflags: Default::default(), + encoding: Encoding::Raw, } } @@ -340,6 +337,7 @@ mod tests { )), entries: entries.into_iter().enumerate().collect(), bitflags: Default::default(), + encoding: Encoding::Raw, } } @@ -362,6 +360,7 @@ mod tests { "{=u8:us}".to_owned(), )), bitflags: Default::default(), + encoding: Encoding::Raw, }; let frame = table.decode(&bytes).unwrap().0; @@ -1021,6 +1020,7 @@ mod tests { "{=u8:us}".to_owned(), )), bitflags: Default::default(), + encoding: Encoding::Raw, }; let bytes = [ diff --git a/src/lib.rs b/src/lib.rs index 6b9ad76c3..036077f23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,18 @@ #[cfg(feature = "alloc")] extern crate alloc; +// This must be in the root lib.rs, otherwise it doesn't appear in the final binary. +#[used] +#[link_section = ".defmt.end"] +#[cfg_attr(feature = "encoding-raw", export_name = "_defmt_encoding_ = raw")] +#[cfg_attr( + not(feature = "encoding-raw"), + export_name = "_defmt_encoding_ = rzcobs" +)] +#[allow(missing_docs)] +#[doc(hidden)] +pub static DEFMT_ENCODING: u8 = 0; + mod encoding; #[doc(hidden)] pub mod export;