diff --git a/src/cmd/dwarf.rs b/src/cmd/dwarf.rs index 545cc83..23539be 100644 --- a/src/cmd/dwarf.rs +++ b/src/cmd/dwarf.rs @@ -15,8 +15,8 @@ use syntect::{ use crate::util::{ dwarf::{ - process_root_tag, read_debug_section, should_skip_tag, tag_type_string, AttributeKind, - TagKind, + process_cu_tag, process_root_tag, read_debug_section, should_skip_tag, tag_type_string, + AttributeKind, TagKind, }, file::{buf_writer, map_file}, }; @@ -163,81 +163,76 @@ where let mut units = Vec::::new(); if let Some((_, mut tag)) = info.tags.first_key_value() { loop { - match tag.kind { - TagKind::CompileUnit => { - let unit = tag - .string_attribute(AttributeKind::Name) - .ok_or_else(|| anyhow!("CompileUnit without name {:?}", tag))?; - if units.contains(unit) { - // log::warn!("Duplicate unit '{}'", unit); - } else { - units.push(unit.clone()); + let unit = process_root_tag(tag)?; + if units.contains(&unit.name) { + // log::warn!("Duplicate unit '{}'", unit.name); + } else { + units.push(unit.name.clone()); + } + writeln!(w, "\n/*\n Compile unit: {}", unit.name)?; + if let Some(producer) = unit.producer { + writeln!(w, " Producer: {}", producer)?; + } + if let Some(language) = unit.language { + writeln!(w, " Language: {}", language)?; + } + if let (Some(start), Some(end)) = (unit.start_address, unit.end_address) { + writeln!(w, " Code range: {:#010X} -> {:#010X}", start, end)?; + } + writeln!(w, "*/")?; + + let children = tag.children(&info.tags); + let mut typedefs = BTreeMap::>::new(); + for child in children { + let tag_type = match process_cu_tag(&info, child) { + Ok(tag_type) => tag_type, + Err(e) => { + log::error!( + "Failed to process tag {} (unit {}): {}", + child.key, + unit.name, + e + ); + writeln!( + w, + "// ERROR: Failed to process tag {} ({:?})", + child.key, child.kind + )?; + continue; } - writeln!(w, "\n// Compile unit: {}", unit)?; - - let children = tag.children(&info.tags); - let mut typedefs = BTreeMap::>::new(); - for child in children { - let tag_type = match process_root_tag(&info, child) { - Ok(tag_type) => tag_type, - Err(e) => { - log::error!( - "Failed to process tag {} (unit {}): {}", - child.key, - unit, - e - ); - writeln!( - w, - "// ERROR: Failed to process tag {} ({:?})", - child.key, child.kind - )?; - continue; - } - }; - if should_skip_tag(&tag_type) { - continue; - } - match tag_type_string(&info, &typedefs, &tag_type) { - Ok(s) => writeln!(w, "{}", s)?, - Err(e) => { - log::error!( - "Failed to emit tag {} (unit {}): {}", - child.key, - unit, - e - ); - writeln!( - w, - "// ERROR: Failed to emit tag {} ({:?})", - child.key, child.kind - )?; - continue; - } - } - - if let TagKind::Typedef = child.kind { - // TODO fundamental typedefs? - if let Some(ud_type_ref) = - child.reference_attribute(AttributeKind::UserDefType) - { - match typedefs.entry(ud_type_ref) { - btree_map::Entry::Vacant(e) => { - e.insert(vec![child.key]); - } - btree_map::Entry::Occupied(e) => { - e.into_mut().push(child.key); - } - } - } - } + }; + if should_skip_tag(&tag_type) { + continue; + } + match tag_type_string(&info, &typedefs, &tag_type) { + Ok(s) => writeln!(w, "{}", s)?, + Err(e) => { + log::error!("Failed to emit tag {} (unit {}): {}", child.key, unit.name, e); + writeln!( + w, + "// ERROR: Failed to emit tag {} ({:?})", + child.key, child.kind + )?; + continue; } } - _ => { - log::warn!("Expected CompileUnit, got {:?}", tag.kind); - break; + + if let TagKind::Typedef = child.kind { + // TODO fundamental typedefs? + if let Some(ud_type_ref) = child.reference_attribute(AttributeKind::UserDefType) + { + match typedefs.entry(ud_type_ref) { + btree_map::Entry::Vacant(e) => { + e.insert(vec![child.key]); + } + btree_map::Entry::Occupied(e) => { + e.into_mut().push(child.key); + } + } + } } } + if let Some(next) = tag.next_sibling(&info.tags) { tag = next; } else { diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 0f31215..d870936 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -716,6 +716,8 @@ pub struct SubroutineType { pub labels: Vec, pub blocks: Vec, pub inlines: Vec, + pub start_address: Option, + pub end_address: Option, } #[derive(Debug, Clone)] @@ -756,6 +758,47 @@ pub enum TagType { UserDefined(UserDefinedType), } +#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] +#[repr(u32)] +pub enum Language { + C89 = 0x1, + C = 0x2, + Ada83 = 0x3, + CPlusPlus = 0x4, + Cobol74 = 0x5, + Cobol85 = 0x6, + Fortran77 = 0x7, + Fortran90 = 0x8, + Pascal83 = 0x9, + Modula2 = 0xa, +} + +impl Display for Language { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Language::C89 => write!(f, "C89"), + Language::C => write!(f, "C"), + Language::Ada83 => write!(f, "Ada83"), + Language::CPlusPlus => write!(f, "C++"), + Language::Cobol74 => write!(f, "Cobol74"), + Language::Cobol85 => write!(f, "Cobol85"), + Language::Fortran77 => write!(f, "Fortran77"), + Language::Fortran90 => write!(f, "Fortran90"), + Language::Pascal83 => write!(f, "Pascal83"), + Language::Modula2 => write!(f, "Modula2"), + } + } +} + +#[derive(Debug, Clone)] +pub struct CompileUnit { + pub name: String, + pub producer: Option, + pub language: Option, + pub start_address: Option, + pub end_address: Option, +} + impl UserDefinedType { pub fn name(&self) -> Option { match self { @@ -1171,8 +1214,14 @@ pub fn subroutine_def_string( typedefs: &TypedefMap, t: &SubroutineType, ) -> Result { + let mut out = String::new(); + if let (Some(start), Some(end)) = (t.start_address, t.end_address) { + writeln!(out, "// Range: {:#X} -> {:#X}", start, end)?; + } let rt = type_string(info, typedefs, &t.return_type, true)?; - let mut out = if t.local { "static ".to_string() } else { String::new() }; + if t.local { + out.push_str("static "); + } if t.inline { out.push_str("inline "); } @@ -1852,6 +1901,8 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result let mut references = Vec::new(); let mut member_of = None; let mut inline = false; + let mut start_address = None; + let mut end_address = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} @@ -1865,8 +1916,11 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result _, ) => return_type = Some(process_type(attr, info.e)?), (AttributeKind::Prototyped, _) => prototyped = true, - (AttributeKind::LowPc, _) | (AttributeKind::HighPc, _) => { - // TODO? + (AttributeKind::LowPc, &AttributeValue::Address(addr)) => { + start_address = Some(addr); + } + (AttributeKind::HighPc, &AttributeValue::Address(addr)) => { + end_address = Some(addr); } (AttributeKind::MwGlobalRef, &AttributeValue::Reference(key)) => { references.push(key); @@ -1988,6 +2042,8 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result labels, blocks, inlines, + start_address, + end_address, }) } @@ -2287,7 +2343,38 @@ pub fn process_type(attr: &Attribute, e: Endian) -> Result { } } -pub fn process_root_tag(info: &DwarfInfo, tag: &Tag) -> Result { +pub fn process_root_tag(tag: &Tag) -> Result { + ensure!(tag.kind == TagKind::CompileUnit, "{:?} is not a CompileUnit tag", tag.kind); + + let mut name = None; + let mut producer = None; + let mut language = None; + let mut start_address = None; + let mut end_address = None; + for attr in &tag.attributes { + match (attr.kind, &attr.value) { + (AttributeKind::Sibling, _) => {} + (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), + (AttributeKind::Producer, AttributeValue::String(s)) => producer = Some(s.clone()), + (AttributeKind::Language, &AttributeValue::Data4(value)) => { + language = Some(Language::try_from_primitive(value)?) + } + (AttributeKind::LowPc, &AttributeValue::Address(addr)) => start_address = Some(addr), + (AttributeKind::HighPc, &AttributeValue::Address(addr)) => end_address = Some(addr), + (AttributeKind::StmtList, AttributeValue::Data4(_)) => { + // TODO .line support + } + _ => { + bail!("Unhandled CompileUnit attribute {:?}", attr); + } + } + } + + let name = name.ok_or_else(|| anyhow!("CompileUnit without Name: {:?}", tag))?; + Ok(CompileUnit { name, producer, language, start_address, end_address }) +} + +pub fn process_cu_tag(info: &DwarfInfo, tag: &Tag) -> Result { match tag.kind { TagKind::Typedef => Ok(TagType::Typedef(process_typedef_tag(info, tag)?)), TagKind::GlobalVariable | TagKind::LocalVariable => { @@ -2352,14 +2439,13 @@ fn variable_string( out.push(' '); out.push_str(variable.name.as_deref().unwrap_or("[unknown]")); out.push_str(&ts.suffix); - match &variable.address { - Some(addr) => out.push_str(&format!(" : {:#010X}", addr)), - None => {} - } out.push(';'); if include_extra { let size = variable.kind.size(info)?; out.push_str(&format!(" // size: {:#X}", size)); + if let Some(addr) = variable.address { + out.push_str(&format!(", address: {:#X}", addr)); + } } Ok(out) }