use std::{ cmp::max, collections::BTreeMap, convert::TryFrom, fmt::{Display, Formatter, Write}, io::{BufRead, Cursor, Seek, SeekFrom}, num::NonZeroU32, }; use anyhow::{anyhow, bail, ensure, Context, Result}; use indent::indent_all_by; use num_enum::{IntoPrimitive, TryFromPrimitive}; use crate::{ array_ref, util::reader::{Endian, FromBytes, FromReader}, }; #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[repr(u16)] pub enum TagKind { Padding = 0x0000, ArrayType = 0x0001, ClassType = 0x0002, EntryPoint = 0x0003, EnumerationType = 0x0004, FormalParameter = 0x0005, GlobalSubroutine = 0x0006, GlobalVariable = 0x0007, Label = 0x000a, LexicalBlock = 0x000b, LocalVariable = 0x000c, Member = 0x000d, PointerType = 0x000f, ReferenceType = 0x0010, // aka SourceFile CompileUnit = 0x0011, StringType = 0x0012, StructureType = 0x0013, Subroutine = 0x0014, SubroutineType = 0x0015, Typedef = 0x0016, UnionType = 0x0017, UnspecifiedParameters = 0x0018, Variant = 0x0019, CommonBlock = 0x001a, CommonInclusion = 0x001b, Inheritance = 0x001c, InlinedSubroutine = 0x001d, Module = 0x001e, PtrToMemberType = 0x001f, SetType = 0x0020, SubrangeType = 0x0021, WithStmt = 0x0022, // User types MwOverlayBranch = 0x4080, } #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[repr(u16)] pub enum FundType { WideChar = 0x0000, // Likely an MW bug Char = 0x0001, SignedChar = 0x0002, UnsignedChar = 0x0003, Short = 0x0004, SignedShort = 0x0005, UnsignedShort = 0x0006, Integer = 0x0007, SignedInteger = 0x0008, UnsignedInteger = 0x0009, Long = 0x000a, SignedLong = 0x000b, UnsignedLong = 0x000c, Pointer = 0x000d, Float = 0x000e, DblPrecFloat = 0x000f, ExtPrecFloat = 0x0010, Complex = 0x0011, DblPrecComplex = 0x0012, Void = 0x0014, Boolean = 0x0015, ExtPrecComplex = 0x0016, Label = 0x0017, // User types LongLong = 0x8008, SignedLongLong = 0x8108, UnsignedLongLong = 0x8208, Int128 = 0xa510, Vec2x32Float = 0xac00, } impl FundType { pub fn size(self) -> Result { Ok(match self { FundType::Char | FundType::SignedChar | FundType::UnsignedChar | FundType::Boolean => 1, FundType::WideChar | FundType::Short | FundType::SignedShort | FundType::UnsignedShort => 2, FundType::Integer | FundType::SignedInteger | FundType::UnsignedInteger => 4, FundType::Long | FundType::SignedLong | FundType::UnsignedLong | FundType::Pointer | FundType::Float => 4, FundType::DblPrecFloat | FundType::LongLong | FundType::SignedLongLong | FundType::UnsignedLongLong | FundType::Vec2x32Float => 8, FundType::Int128 => 16, FundType::Void => 0, FundType::ExtPrecFloat | FundType::Complex | FundType::DblPrecComplex | FundType::ExtPrecComplex | FundType::Label => bail!("Unhandled fundamental type {self:?}"), }) } pub fn name(self) -> Result<&'static str> { Ok(match self { FundType::WideChar => "wchar_t", FundType::Char => "char", FundType::SignedChar => "signed char", FundType::UnsignedChar => "unsigned char", FundType::Short => "short", FundType::SignedShort => "signed short", FundType::UnsignedShort => "unsigned short", FundType::Integer => "int", FundType::SignedInteger => "signed int", FundType::UnsignedInteger => "unsigned int", FundType::Long => "long", FundType::SignedLong => "signed long", FundType::UnsignedLong => "unsigned long", FundType::Pointer => "void *", FundType::Float => "float", FundType::DblPrecFloat => "double", FundType::ExtPrecFloat => "long double", FundType::Void => "void", FundType::Boolean => "bool", FundType::Complex | FundType::DblPrecComplex | FundType::ExtPrecComplex | FundType::Label => bail!("Unhandled fundamental type {self:?}"), FundType::LongLong => "long long", FundType::SignedLongLong => "signed long long", FundType::UnsignedLongLong => "unsigned long long", FundType::Int128 => "__int128", FundType::Vec2x32Float => "__vec2x32float__", }) } } #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum Modifier { PointerTo = 0x01, ReferenceTo = 0x02, Const = 0x03, Volatile = 0x04, // User types } #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum SubscriptFormat { FundTypeConstConst = 0x0, FundTypeConstLocation = 0x1, FundTypeLocationConst = 0x2, FundTypeLocationLocation = 0x3, UserTypeConstConst = 0x4, UserTypeConstLocation = 0x5, UserTypeLocationConst = 0x6, UserTypeLocationLocation = 0x7, ElementType = 0x8, } #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum LocationOp { Register = 0x01, BaseRegister = 0x02, Address = 0x03, Const = 0x04, Deref2 = 0x05, Deref4 = 0x06, Add = 0x07, // User types MwFpReg = 0x80, MwFpDReg = 0x81, MwDRef8 = 0x82, } const FORM_MASK: u16 = 0xF; #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[repr(u16)] enum FormKind { Addr = 0x1, Ref = 0x2, Block2 = 0x3, Block4 = 0x4, Data2 = 0x5, Data4 = 0x6, Data8 = 0x7, String = 0x8, } #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[repr(u16)] pub enum AttributeKind { Sibling = 0x0010 | (FormKind::Ref as u16), Location = 0x0020 | (FormKind::Block2 as u16), Name = 0x0030 | (FormKind::String as u16), FundType = 0x0050 | (FormKind::Data2 as u16), ModFundType = 0x0060 | (FormKind::Block2 as u16), UserDefType = 0x0070 | (FormKind::Ref as u16), ModUDType = 0x0080 | (FormKind::Block2 as u16), Ordering = 0x0090 | (FormKind::Data2 as u16), SubscrData = 0x00a0 | (FormKind::Block2 as u16), ByteSize = 0x00b0 | (FormKind::Data4 as u16), BitOffset = 0x00c0 | (FormKind::Data2 as u16), BitSize = 0x00d0 | (FormKind::Data4 as u16), ElementList = 0x00f0 | (FormKind::Block4 as u16), StmtList = 0x0100 | (FormKind::Data4 as u16), LowPc = 0x0110 | (FormKind::Addr as u16), HighPc = 0x0120 | (FormKind::Addr as u16), Language = 0x0130 | (FormKind::Data4 as u16), Member = 0x0140 | (FormKind::Ref as u16), Discr = 0x0150 | (FormKind::Ref as u16), DiscrValue = 0x0160 | (FormKind::Block2 as u16), StringLength = 0x0190 | (FormKind::Block2 as u16), CommonReference = 0x01a0 | (FormKind::Ref as u16), CompDir = 0x01b0 | (FormKind::String as u16), ConstValueString = 0x01c0 | (FormKind::String as u16), ConstValueData2 = 0x01c0 | (FormKind::Data2 as u16), ConstValueData4 = 0x01c0 | (FormKind::Data4 as u16), ConstValueData8 = 0x01c0 | (FormKind::Data8 as u16), ConstValueBlock2 = 0x01c0 | (FormKind::Block2 as u16), ConstValueBlock4 = 0x01c0 | (FormKind::Block4 as u16), ContainingType = 0x01d0 | (FormKind::Ref as u16), DefaultValueAddr = 0x01e0 | (FormKind::Addr as u16), DefaultValueData2 = 0x01e0 | (FormKind::Data2 as u16), DefaultValueData8 = 0x01e0 | (FormKind::Data8 as u16), DefaultValueString = 0x01e0 | (FormKind::String as u16), Friends = 0x01f0 | (FormKind::Block2 as u16), Inline = 0x0200 | (FormKind::String as u16), IsOptional = 0x0210 | (FormKind::String as u16), LowerBoundRef = 0x0220 | (FormKind::Ref as u16), LowerBoundData2 = 0x0220 | (FormKind::Data2 as u16), LowerBoundData4 = 0x0220 | (FormKind::Data4 as u16), LowerBoundData8 = 0x0220 | (FormKind::Data8 as u16), Program = 0x0230 | (FormKind::String as u16), Private = 0x0240 | (FormKind::String as u16), Producer = 0x0250 | (FormKind::String as u16), Protected = 0x0260 | (FormKind::String as u16), Prototyped = 0x0270 | (FormKind::String as u16), Public = 0x0280 | (FormKind::String as u16), PureVirtual = 0x0290 | (FormKind::String as u16), ReturnAddr = 0x02a0 | (FormKind::Block2 as u16), Specification = 0x02b0 | (FormKind::Ref as u16), StartScope = 0x02c0 | (FormKind::Data4 as u16), StrideSize = 0x02e0 | (FormKind::Data4 as u16), UpperBoundRef = 0x02f0 | (FormKind::Ref as u16), UpperBoundData2 = 0x02f0 | (FormKind::Data2 as u16), UpperBoundData4 = 0x02f0 | (FormKind::Data4 as u16), UpperBoundData8 = 0x02f0 | (FormKind::Data8 as u16), Virtual = 0x0300 | (FormKind::String as u16), LoUser = 0x2000, HiUser = 0x3ff0, // User types MwMangled = 0x2000 | (FormKind::String as u16), MwRestoreSp = 0x2010 | (FormKind::Block2 as u16), MwGlobalRef = 0x2020 | (FormKind::Ref as u16), MwGlobalRefByName = 0x2030 | (FormKind::String as u16), MwRestoreS0 = 0x2040 | (FormKind::Block2 as u16), MwRestoreS1 = 0x2050 | (FormKind::Block2 as u16), MwRestoreS2 = 0x2060 | (FormKind::Block2 as u16), MwRestoreS3 = 0x2070 | (FormKind::Block2 as u16), MwRestoreS4 = 0x2080 | (FormKind::Block2 as u16), MwRestoreS5 = 0x2090 | (FormKind::Block2 as u16), MwRestoreS6 = 0x20A0 | (FormKind::Block2 as u16), MwRestoreS7 = 0x20B0 | (FormKind::Block2 as u16), MwRestoreS8 = 0x20C0 | (FormKind::Block2 as u16), MwRestoreF20 = 0x20D0 | (FormKind::Block2 as u16), MwRestoreF21 = 0x20E0 | (FormKind::Block2 as u16), MwRestoreF22 = 0x20F0 | (FormKind::Block2 as u16), MwRestoreF23 = 0x2100 | (FormKind::Block2 as u16), MwRestoreF24 = 0x2110 | (FormKind::Block2 as u16), MwRestoreF25 = 0x2120 | (FormKind::Block2 as u16), MwRestoreF26 = 0x2130 | (FormKind::Block2 as u16), MwRestoreF27 = 0x2140 | (FormKind::Block2 as u16), MwRestoreF28 = 0x2150 | (FormKind::Block2 as u16), MwRestoreF29 = 0x2160 | (FormKind::Block2 as u16), MwRestoreF30 = 0x2170 | (FormKind::Block2 as u16), MwRestoreD20 = 0x2180 | (FormKind::Block2 as u16), MwRestoreD21 = 0x2190 | (FormKind::Block2 as u16), MwRestoreD22 = 0x21A0 | (FormKind::Block2 as u16), MwRestoreD23 = 0x21B0 | (FormKind::Block2 as u16), MwRestoreD24 = 0x21C0 | (FormKind::Block2 as u16), MwRestoreD25 = 0x21D0 | (FormKind::Block2 as u16), MwRestoreD26 = 0x2240 | (FormKind::Block2 as u16), MwRestoreD27 = 0x2250 | (FormKind::Block2 as u16), MwRestoreD28 = 0x2260 | (FormKind::Block2 as u16), MwRestoreD29 = 0x2270 | (FormKind::Block2 as u16), MwRestoreD30 = 0x2280 | (FormKind::Block2 as u16), MwOverlayId = 0x2290 | (FormKind::Data4 as u16), MwOverlayName = 0x22A0 | (FormKind::String as u16), MwGlobalRefsBlock = 0x2300 | (FormKind::Block2 as u16), MwLocalSpoffset = 0x2310 | (FormKind::Block4 as u16), MwMips16 = 0x2330 | (FormKind::String as u16), MwDwarf2Location = 0x2340 | (FormKind::Block2 as u16), Unknown800 = 0x8000 | (FormKind::Data4 as u16), Unknown801 = 0x8010 | (FormKind::Data4 as u16), MwPrologueEnd = 0x8040 | (FormKind::Addr as u16), MwEpilogueStart = 0x8050 | (FormKind::Addr as u16), } #[derive(Debug, Clone)] pub enum AttributeValue { Address(u32), Reference(u32), Data2(u16), Data4(u32), Data8(u64), Block(Vec), String(String), } #[derive(Debug, Clone)] pub struct Attribute { pub kind: AttributeKind, pub value: AttributeValue, } #[derive(Debug, Clone)] pub struct Tag { pub key: u32, pub kind: TagKind, pub attributes: Vec, } pub type TagMap = BTreeMap; pub type TypedefMap = BTreeMap>; #[derive(Debug, Clone)] pub struct DwarfInfo { pub e: Endian, pub tags: TagMap, } impl Tag { #[inline] pub fn attribute(&self, kind: AttributeKind) -> Option<&Attribute> { self.attributes.iter().find(|attr| attr.kind == kind) } #[inline] pub fn address_attribute(&self, kind: AttributeKind) -> Option { match self.attribute(kind) { Some(Attribute { value: AttributeValue::Address(addr), .. }) => Some(*addr), _ => None, } } #[inline] pub fn reference_attribute(&self, kind: AttributeKind) -> Option { match self.attribute(kind) { Some(Attribute { value: AttributeValue::Reference(addr), .. }) => Some(*addr), _ => None, } } #[inline] pub fn string_attribute(&self, kind: AttributeKind) -> Option<&String> { match self.attribute(kind) { Some(Attribute { value: AttributeValue::String(str), .. }) => Some(str), _ => None, } } #[inline] pub fn block_attribute(&self, kind: AttributeKind) -> Option<&[u8]> { match self.attribute(kind) { Some(Attribute { value: AttributeValue::Block(vec), .. }) => Some(vec), _ => None, } } #[inline] pub fn data4_attribute(&self, kind: AttributeKind) -> Option { match self.attribute(kind) { Some(Attribute { value: AttributeValue::Data4(value), .. }) => Some(*value), _ => None, } } #[inline] pub fn data2_attribute(&self, kind: AttributeKind) -> Option { match self.attribute(kind) { Some(Attribute { value: AttributeValue::Data2(value), .. }) => Some(*value), _ => None, } } #[inline] pub fn type_attribute(&self) -> Option<&Attribute> { self.attributes.iter().find(|attr| { matches!( attr.kind, AttributeKind::FundType | AttributeKind::ModFundType | AttributeKind::UserDefType | AttributeKind::ModUDType ) }) } pub fn children<'a>(&self, tags: &'a TagMap) -> Vec<&'a Tag> { let sibling = self.next_sibling(tags); let mut children = Vec::new(); let mut child = match self.next_tag(tags) { Some(child) => child, None => return children, }; loop { if let Some(end) = sibling { if child.key == end.key { break; } } if child.kind != TagKind::Padding { children.push(child); } match child.next_sibling(tags) { Some(next) => child = next, None => break, } } children } /// Returns the next sibling tag, if any pub fn next_sibling<'a>(&self, tags: &'a TagMap) -> Option<&'a Tag> { if let Some(key) = self.reference_attribute(AttributeKind::Sibling) { tags.get(&key) } else { self.next_tag(tags) } } /// Returns the next tag sequentially, if any pub fn next_tag<'a>(&self, tags: &'a TagMap) -> Option<&'a Tag> { tags.range(self.key + 1..).next().map(|(_, tag)| tag) } } pub fn read_debug_section(reader: &mut R, e: Endian) -> Result where R: BufRead + Seek + ?Sized { let len = { let old_pos = reader.stream_position()?; let len = reader.seek(SeekFrom::End(0))?; reader.seek(SeekFrom::Start(old_pos))?; len }; let mut info = DwarfInfo { e, tags: BTreeMap::new() }; loop { let position = reader.stream_position()?; if position >= len { break; } let tag = read_tag(reader, e)?; info.tags.insert(position as u32, tag); } Ok(info) } #[allow(unused)] pub fn read_aranges_section(reader: &mut R, e: Endian) -> Result<()> where R: BufRead + Seek + ?Sized { let len = { let old_pos = reader.stream_position()?; let len = reader.seek(SeekFrom::End(0))?; reader.seek(SeekFrom::Start(old_pos))?; len }; // let mut tags = BTreeMap::new(); loop { let position = reader.stream_position()?; if position >= len { break; } let size = u32::from_reader(reader, e)?; let version = u8::from_reader(reader, e)?; ensure!(version == 1, "Expected version 1, got {version}"); let _debug_offs = u32::from_reader(reader, e)?; let _debug_size = u32::from_reader(reader, e)?; while reader.stream_position()? < position + size as u64 { let _address = u32::from_reader(reader, e)?; let _length = u32::from_reader(reader, e)?; } } Ok(()) } fn read_tag(reader: &mut R, e: Endian) -> Result where R: BufRead + Seek + ?Sized { let position = reader.stream_position()?; let size = u32::from_reader(reader, e)?; if size < 8 { // Null entry if size > 4 { reader.seek(SeekFrom::Current(size as i64 - 4))?; } return Ok(Tag { key: position as u32, kind: TagKind::Padding, attributes: vec![] }); } let tag_num = u16::from_reader(reader, e)?; let tag = TagKind::try_from(tag_num).context("Unknown DWARF tag type")?; let mut attributes = Vec::new(); if tag == TagKind::Padding { reader.seek(SeekFrom::Start(position + size as u64))?; // Skip padding } else { while reader.stream_position()? < position + size as u64 { attributes.push(read_attribute(reader, e)?); } } Ok(Tag { key: position as u32, kind: tag, attributes }) } // TODO Shift-JIS? fn read_string(reader: &mut R) -> Result where R: BufRead + ?Sized { let mut str = String::new(); let mut buf = [0u8; 1]; loop { reader.read_exact(&mut buf)?; if buf[0] == 0 { break; } str.push(buf[0] as char); } Ok(str) } fn read_attribute(reader: &mut R, e: Endian) -> Result where R: BufRead + Seek + ?Sized { let attr_type = u16::from_reader(reader, e)?; let attr = AttributeKind::try_from(attr_type).context("Unknown DWARF attribute type")?; let form = FormKind::try_from(attr_type & FORM_MASK).context("Unknown DWARF form type")?; let value = match form { FormKind::Addr => AttributeValue::Address(u32::from_reader(reader, e)?), FormKind::Ref => AttributeValue::Reference(u32::from_reader(reader, e)?), FormKind::Block2 => { let size = u16::from_reader(reader, e)?; let mut data = vec![0u8; size as usize]; reader.read_exact(&mut data)?; AttributeValue::Block(data) } FormKind::Block4 => { let size = u32::from_reader(reader, e)?; let mut data = vec![0u8; size as usize]; reader.read_exact(&mut data)?; AttributeValue::Block(data) } FormKind::Data2 => AttributeValue::Data2(u16::from_reader(reader, e)?), FormKind::Data4 => AttributeValue::Data4(u32::from_reader(reader, e)?), FormKind::Data8 => AttributeValue::Data8(u64::from_reader(reader, e)?), FormKind::String => AttributeValue::String(read_string(reader)?), }; Ok(Attribute { kind: attr, value }) } #[derive(Debug, Clone)] pub struct ArrayDimension { pub index_type: Type, pub size: Option, } #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[repr(u16)] pub enum ArrayOrdering { RowMajor = 0, // ORD_row_major ColMajor = 1, // ORD_col_major } #[derive(Debug, Clone)] pub struct ArrayType { pub element_type: Box, pub dimensions: Vec, } #[derive(Debug, Clone)] pub struct BitData { pub bit_size: u32, pub bit_offset: u16, } #[derive(Debug, Clone)] pub struct StructureMember { pub name: String, pub kind: Type, pub offset: u32, pub bit: Option, pub visibility: Visibility, pub byte_size: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum StructureKind { Struct, Class, } #[derive(Debug, Clone)] pub struct StructureType { pub kind: StructureKind, pub name: Option, pub byte_size: Option, pub members: Vec, pub bases: Vec, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Visibility { Private, Protected, Public, } #[derive(Debug, Clone)] pub struct StructureBase { pub name: Option, pub base_type: Type, pub offset: u32, pub visibility: Visibility, pub virtual_base: bool, } #[derive(Debug, Clone)] pub struct EnumerationMember { pub name: String, pub value: i32, } #[derive(Debug, Clone)] pub struct EnumerationType { pub name: Option, pub byte_size: u32, pub members: Vec, } #[derive(Debug, Clone)] pub struct UnionType { pub name: Option, pub byte_size: u32, pub members: Vec, } #[derive(Debug, Clone)] pub struct SubroutineParameter { pub name: Option, pub kind: Type, pub location: Option, } #[derive(Debug, Clone)] pub struct SubroutineVariable { pub name: Option, pub mangled_name: Option, pub kind: Type, pub location: Option, } #[derive(Debug, Clone)] pub struct SubroutineLabel { pub name: String, pub address: u32, } #[derive(Debug, Clone)] pub struct SubroutineBlock { pub name: Option, pub start_address: u32, pub end_address: u32, pub variables: Vec, pub blocks: Vec, } #[derive(Debug, Clone)] pub struct SubroutineInline { pub specification: u32, pub start_address: u32, pub end_address: u32, } #[derive(Debug, Clone)] pub struct SubroutineType { pub name: Option, pub mangled_name: Option, pub return_type: Type, pub parameters: Vec, pub var_args: bool, pub prototyped: bool, pub references: Vec, pub member_of: Option, pub variables: Vec, pub inline: bool, pub local: bool, pub labels: Vec, pub blocks: Vec, pub inlines: Vec, pub start_address: Option, pub end_address: Option, } #[derive(Debug, Clone)] pub struct PtrToMemberType { pub kind: Type, pub containing_type: u32, } #[derive(Debug, Clone)] pub enum UserDefinedType { Array(ArrayType), Structure(StructureType), Enumeration(EnumerationType), Union(UnionType), Subroutine(SubroutineType), PtrToMember(PtrToMemberType), } #[derive(Debug, Clone)] pub struct VariableTag { pub name: Option, pub mangled_name: Option, pub kind: Type, pub address: Option, pub local: bool, } #[derive(Debug, Clone)] pub struct TypedefTag { pub name: String, pub kind: Type, } #[derive(Debug, Clone)] pub enum TagType { Variable(VariableTag), Typedef(TypedefTag), 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 { UserDefinedType::Array(_) | UserDefinedType::PtrToMember(_) => None, UserDefinedType::Structure(t) => t.name.clone(), UserDefinedType::Enumeration(t) => t.name.clone(), UserDefinedType::Union(t) => t.name.clone(), UserDefinedType::Subroutine(t) => t.name.clone(), } } pub fn is_definition(&self) -> bool { match self { UserDefinedType::Array(_) | UserDefinedType::PtrToMember(_) => false, UserDefinedType::Structure(t) => t.name.is_some(), UserDefinedType::Enumeration(t) => t.name.is_some(), UserDefinedType::Union(t) => t.name.is_some(), UserDefinedType::Subroutine(t) => t.name.is_some(), } } pub fn size(&self, info: &DwarfInfo) -> Result { Ok(match self { UserDefinedType::Array(t) => { let mut size = t.element_type.size(info)?; for dim in &t.dimensions { size *= dim.size.map(|u| u.get()).unwrap_or_default(); } size } UserDefinedType::Structure(t) => match t.byte_size { Some(byte_size) => byte_size, None => { let mut max_end = 0; for member in &t.members { let size = match member.byte_size { Some(byte_size) => byte_size, None => member.kind.size(info)?, }; max_end = max(max_end, member.offset + size); } max_end } }, UserDefinedType::Enumeration(t) => t.byte_size, UserDefinedType::Union(t) => t.byte_size, UserDefinedType::Subroutine(_) => 0, UserDefinedType::PtrToMember(_) => 4, }) } } #[derive(Debug, Copy, Clone)] pub enum TypeKind { Fundamental(FundType), UserDefined(u32), } #[derive(Debug, Clone)] pub struct Type { pub kind: TypeKind, pub modifiers: Vec, } impl Type { pub fn size(&self, info: &DwarfInfo) -> Result { if self.modifiers.iter().any(|m| matches!(m, Modifier::PointerTo | Modifier::ReferenceTo)) { return Ok(4); } match self.kind { TypeKind::Fundamental(ft) => ft.size(), TypeKind::UserDefined(key) => { let tag = info .tags .get(&key) .ok_or_else(|| anyhow!("Failed to locate user defined type {}", key))?; let ud_type = ud_type(info, tag)?; ud_type.size(info) } } } } pub fn apply_modifiers(mut str: TypeString, modifiers: &[Modifier]) -> Result { let mut has_pointer = false; for &modifier in modifiers.iter().rev() { match modifier { Modifier::PointerTo => { if !has_pointer && !str.suffix.is_empty() { if str.member.is_empty() { str.prefix.push_str(" (*"); } else { write!(str.prefix, " ({}*", str.member)?; } str.suffix.insert(0, ')'); } else { str.prefix.push_str(" *"); } has_pointer = true; } Modifier::ReferenceTo => { if !has_pointer && !str.suffix.is_empty() { str.prefix.push_str(" (&"); str.suffix.insert(0, ')'); } else { str.prefix.push_str(" &"); } has_pointer = true; } Modifier::Const => { if has_pointer { str.prefix.push_str(" const"); } else { str.prefix.insert_str(0, "const "); } } Modifier::Volatile => { if has_pointer { str.prefix.push_str(" volatile"); } else { str.prefix.insert_str(0, "volatile "); } } } } Ok(str) } #[derive(Debug, Default, Clone)] pub struct TypeString { pub prefix: String, pub suffix: String, // TODO: rework this eventually and merge with PTMF handling pub member: String, } impl Display for TypeString { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if self.member.is_empty() { write!(f, "{}{}", self.prefix, self.suffix)?; } else { // TODO member print likely wrong write!(f, "{} {}{}", self.prefix, self.member, self.suffix)?; } Ok(()) } } pub fn type_string( info: &DwarfInfo, typedefs: &TypedefMap, t: &Type, include_anonymous_def: bool, ) -> Result { let str = match t.kind { TypeKind::Fundamental(ft) => { TypeString { prefix: ft.name()?.to_string(), ..Default::default() } } TypeKind::UserDefined(key) => { if let Some(&td_key) = typedefs.get(&key).and_then(|v| v.first()) { let tag = info .tags .get(&td_key) .ok_or_else(|| anyhow!("Failed to locate typedef {}", key))?; let td_name = tag .string_attribute(AttributeKind::Name) .ok_or_else(|| anyhow!("typedef without name"))?; TypeString { prefix: td_name.clone(), ..Default::default() } } else { let tag = info .tags .get(&key) .ok_or_else(|| anyhow!("Failed to locate user defined type {}", key))?; ud_type_string(info, typedefs, &ud_type(info, tag)?, true, include_anonymous_def)? } } }; apply_modifiers(str, &t.modifiers) } fn type_name(info: &DwarfInfo, typedefs: &TypedefMap, t: &Type) -> Result { Ok(match t.kind { TypeKind::Fundamental(ft) => ft.name()?.to_string(), TypeKind::UserDefined(key) => { if let Some(&td_key) = typedefs.get(&key).and_then(|v| v.first()) { info.tags .get(&td_key) .ok_or_else(|| anyhow!("Failed to locate typedef {}", key))? .string_attribute(AttributeKind::Name) .ok_or_else(|| anyhow!("typedef without name"))? .clone() } else { let tag = info .tags .get(&key) .ok_or_else(|| anyhow!("Failed to locate user defined type {}", key))?; let udt = ud_type(info, tag)?; udt.name().ok_or_else(|| anyhow!("User defined type without name"))? } } }) } fn array_type_string( info: &DwarfInfo, typedefs: &TypedefMap, t: &ArrayType, include_anonymous_def: bool, ) -> Result { let mut out = type_string(info, typedefs, t.element_type.as_ref(), include_anonymous_def)?; for dim in &t.dimensions { ensure!( matches!( dim.index_type.kind, TypeKind::Fundamental(FundType::Long | FundType::Integer) ), "Unsupported array index type '{}'", type_string(info, typedefs, &dim.index_type, true)? ); match dim.size { None => out.suffix.insert_str(0, "[]"), Some(size) => out.suffix = format!("[{}]{}", size, out.suffix), }; } Ok(out) } fn structure_type_string( info: &DwarfInfo, typedefs: &TypedefMap, t: &StructureType, include_keyword: bool, include_anonymous_def: bool, ) -> Result { let prefix = if let Some(name) = t.name.as_ref() { if include_keyword { match t.kind { StructureKind::Struct => format!("struct {}", name), StructureKind::Class => format!("class {}", name), } } else { name.clone() } } else if include_anonymous_def { struct_def_string(info, typedefs, t)? } else if include_keyword { match t.kind { StructureKind::Struct => "struct [anonymous]".to_string(), StructureKind::Class => "class [anonymous]".to_string(), } } else { match t.kind { StructureKind::Struct => "[anonymous struct]".to_string(), StructureKind::Class => "[anonymous class]".to_string(), } }; Ok(TypeString { prefix, ..Default::default() }) } fn enumeration_type_string( _info: &DwarfInfo, _typedefs: &TypedefMap, t: &EnumerationType, include_keyword: bool, include_anonymous_def: bool, ) -> Result { let prefix = if let Some(name) = t.name.as_ref() { if include_keyword { format!("enum {}", name) } else { name.clone() } } else if include_anonymous_def { enum_def_string(t)? } else if include_keyword { "enum [anonymous]".to_string() } else { "[anonymous enum]".to_string() }; Ok(TypeString { prefix, ..Default::default() }) } fn union_type_string( info: &DwarfInfo, typedefs: &TypedefMap, t: &UnionType, include_keyword: bool, include_anonymous_def: bool, ) -> Result { let prefix = if let Some(name) = t.name.as_ref() { if include_keyword { format!("union {}", name) } else { name.clone() } } else if include_anonymous_def { union_def_string(info, typedefs, t)? } else if include_keyword { "union [anonymous]".to_string() } else { "[anonymous union]".to_string() }; Ok(TypeString { prefix, ..Default::default() }) } pub fn ud_type_string( info: &DwarfInfo, typedefs: &TypedefMap, t: &UserDefinedType, include_keyword: bool, include_anonymous_def: bool, ) -> Result { Ok(match t { UserDefinedType::Array(t) => array_type_string(info, typedefs, t, include_anonymous_def)?, UserDefinedType::Structure(t) => { structure_type_string(info, typedefs, t, include_keyword, include_anonymous_def)? } UserDefinedType::Enumeration(t) => { enumeration_type_string(info, typedefs, t, include_keyword, include_anonymous_def)? } UserDefinedType::Union(t) => { union_type_string(info, typedefs, t, include_keyword, include_anonymous_def)? } UserDefinedType::Subroutine(t) => subroutine_type_string(info, typedefs, t)?, UserDefinedType::PtrToMember(t) => ptr_to_member_type_string(info, typedefs, t)?, }) } fn ptr_to_member_type_string( info: &DwarfInfo, typedefs: &TypedefMap, t: &PtrToMemberType, ) -> Result { let ts = type_string(info, typedefs, &t.kind, true)?; let containing_type = info .tags .get(&t.containing_type) .ok_or_else(|| anyhow!("Failed to locate containing type {}", t.containing_type))?; let containing_ts = ud_type_string(info, typedefs, &ud_type(info, containing_type)?, false, false)?; Ok(TypeString { prefix: format!("{} ({}::*", ts.prefix, containing_ts.prefix), suffix: format!("{}){}", containing_ts.suffix, ts.suffix), ..Default::default() }) } pub fn ud_type_def(info: &DwarfInfo, typedefs: &TypedefMap, t: &UserDefinedType) -> Result { match t { UserDefinedType::Array(t) => { let ts = array_type_string(info, typedefs, t, false)?; Ok(format!("// Array: {}{}", ts.prefix, ts.suffix)) } UserDefinedType::Subroutine(t) => Ok(subroutine_def_string(info, typedefs, t)?), UserDefinedType::Structure(t) => Ok(struct_def_string(info, typedefs, t)?), UserDefinedType::Enumeration(t) => Ok(enum_def_string(t)?), UserDefinedType::Union(t) => Ok(union_def_string(info, typedefs, t)?), UserDefinedType::PtrToMember(t) => { let ts = ptr_to_member_type_string(info, typedefs, t)?; Ok(format!("// PtrToMember: {}{}", ts.prefix, ts.suffix)) } } } pub fn subroutine_type_string( info: &DwarfInfo, typedefs: &TypedefMap, t: &SubroutineType, ) -> Result { let mut out = type_string(info, typedefs, &t.return_type, true)?; let mut parameters = String::new(); if t.parameters.is_empty() { if t.var_args { parameters = "...".to_string(); } else if t.prototyped { parameters = "void".to_string(); } } else { for (idx, parameter) in t.parameters.iter().enumerate() { if idx > 0 { write!(parameters, ", ")?; } let ts = type_string(info, typedefs, ¶meter.kind, true)?; if let Some(name) = ¶meter.name { write!(parameters, "{} {}{}", ts.prefix, name, ts.suffix)?; } else { write!(parameters, "{}{}", ts.prefix, ts.suffix)?; } if let Some(location) = ¶meter.location { write!(parameters, " /* {} */", location)?; } } if t.var_args { write!(parameters, ", ...")?; } } out.suffix = format!("({}){}", parameters, out.suffix); if let Some(member_of) = t.member_of { let tag = info .tags .get(&member_of) .ok_or_else(|| anyhow!("Failed to locate member_of tag {}", member_of))?; let base_name = tag .string_attribute(AttributeKind::Name) .ok_or_else(|| anyhow!("member_of tag {} has no name attribute", member_of))?; out.member = format!("{}::", base_name); } Ok(out) } pub fn subroutine_def_string( info: &DwarfInfo, 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)?; if t.local { out.push_str("static "); } if t.inline { out.push_str("inline "); } out.push_str(&rt.prefix); out.push(' '); let mut name_written = false; if let Some(member_of) = t.member_of { let tag = info .tags .get(&member_of) .ok_or_else(|| anyhow!("Failed to locate member_of tag {}", member_of))?; let base_name = tag .string_attribute(AttributeKind::Name) .ok_or_else(|| anyhow!("member_of tag {} has no name attribute", member_of))?; write!(out, "{}::", base_name)?; // Handle constructors and destructors if let Some(name) = t.name.as_ref() { if name == "__dt" { write!(out, "~{}", base_name)?; name_written = true; } else if name == "__ct" { write!(out, "{}", base_name)?; name_written = true; } } } if !name_written { if let Some(name) = t.name.as_ref() { out.push_str(name); } } let mut parameters = String::new(); if t.parameters.is_empty() { if t.var_args { parameters = "...".to_string(); } else if t.prototyped { parameters = "void".to_string(); } } else { for (idx, parameter) in t.parameters.iter().enumerate() { if idx > 0 { write!(parameters, ", ")?; } let ts = type_string(info, typedefs, ¶meter.kind, true)?; if let Some(name) = ¶meter.name { write!(parameters, "{} {}{}", ts.prefix, name, ts.suffix)?; } else { write!(parameters, "{}{}", ts.prefix, ts.suffix)?; } if let Some(location) = ¶meter.location { write!(parameters, " /* {} */", location)?; } } if t.var_args { write!(parameters, ", ...")?; } } write!(out, "({}){} {{", parameters, rt.suffix)?; if !t.variables.is_empty() { writeln!(out, "\n // Local variables")?; let mut var_out = String::new(); for variable in &t.variables { let ts = type_string(info, typedefs, &variable.kind, true)?; write!( var_out, "{} {}{};", ts.prefix, variable.name.as_deref().unwrap_or_default(), ts.suffix )?; if let Some(location) = &variable.location { write!(var_out, " // {}", location)?; } writeln!(var_out)?; } write!(out, "{}", indent_all_by(4, var_out))?; } if !t.references.is_empty() { writeln!(out, "\n // References")?; for &reference in &t.references { let tag = info .tags .get(&reference) .ok_or_else(|| anyhow!("Failed to locate reference tag {}", reference))?; if tag.kind == TagKind::Padding { writeln!(out, " // -> ??? ({})", reference)?; continue; } let variable = process_variable_tag(info, tag)?; writeln!(out, " // -> {}", variable_string(info, typedefs, &variable, false)?)?; } } if !t.labels.is_empty() { writeln!(out, "\n // Labels")?; for label in &t.labels { writeln!(out, " {}: // {:#X}", label.name, label.address)?; } } if !t.blocks.is_empty() { writeln!(out, "\n // Blocks")?; for block in &t.blocks { let block_str = subroutine_block_string(info, typedefs, block)?; out.push_str(&indent_all_by(4, block_str)); } } if !t.inlines.is_empty() { writeln!(out, "\n // Inlines")?; for inline in &t.inlines { let spec_tag = info .tags .get(&inline.specification) .ok_or_else(|| anyhow!("Failed to locate inline tag {}", inline.specification))?; let subroutine = process_subroutine_tag(info, spec_tag)?; writeln!( out, " // -> {} ({:#X} - {:#X})", subroutine_type_string(info, typedefs, &subroutine)?, inline.start_address, inline.end_address, )?; } } writeln!(out, "}}")?; Ok(out) } fn subroutine_block_string( info: &DwarfInfo, typedefs: &TypedefMap, block: &SubroutineBlock, ) -> Result { let mut out = String::new(); if let Some(name) = &block.name { write!(out, "{}: ", name)?; } else { out.push_str("/* anonymous block */ "); } writeln!(out, "{{\n // Range: {:#X} -> {:#X}", block.start_address, block.end_address)?; let mut var_out = String::new(); for variable in &block.variables { let ts = type_string(info, typedefs, &variable.kind, true)?; write!( var_out, "{} {}{};", ts.prefix, variable.name.as_deref().unwrap_or_default(), ts.suffix )?; if let Some(location) = &variable.location { write!(var_out, " // {}", location)?; } writeln!(var_out)?; } write!(out, "{}", indent_all_by(4, var_out))?; for block in &block.blocks { let block_str = subroutine_block_string(info, typedefs, block)?; out.push_str(&indent_all_by(4, block_str)); } writeln!(out, "}}")?; Ok(out) } pub fn struct_def_string( info: &DwarfInfo, typedefs: &TypedefMap, t: &StructureType, ) -> Result { let mut out = match t.kind { StructureKind::Struct => "struct".to_string(), StructureKind::Class => "class".to_string(), }; if let Some(name) = t.name.as_ref() { write!(out, " {}", name)?; } let mut wrote_base = false; for base in &t.bases { if !wrote_base { out.push_str(" : "); wrote_base = true; } else { out.push_str(", "); } match base.visibility { Visibility::Private => out.push_str("private "), Visibility::Protected => out.push_str("protected "), Visibility::Public => out.push_str("public "), } if base.virtual_base { out.push_str("virtual "); } if let Some(name) = &base.name { out.push_str(name); } else { out.push_str(&type_name(info, typedefs, &base.base_type)?); } } out.push_str(" {\n"); if let Some(byte_size) = t.byte_size { writeln!(out, " // total size: {:#X}", byte_size)?; } let mut vis = match t.kind { StructureKind::Struct => Visibility::Public, StructureKind::Class => Visibility::Private, }; for member in &t.members { if vis != member.visibility { vis = member.visibility; match member.visibility { Visibility::Private => out.push_str("private:\n"), Visibility::Protected => out.push_str("protected:\n"), Visibility::Public => out.push_str("public:\n"), } } let mut var_out = String::new(); let ts = type_string(info, typedefs, &member.kind, true)?; write!(var_out, "{} {}{}", ts.prefix, member.name, ts.suffix)?; if let Some(bit) = &member.bit { write!(var_out, " : {}", bit.bit_size)?; } let size = if let Some(size) = member.byte_size { size } else { member.kind.size(info)? }; writeln!(var_out, "; // offset {:#X}, size {:#X}", member.offset, size)?; out.push_str(&indent_all_by(4, var_out)); } out.push('}'); Ok(out) } pub fn enum_def_string(t: &EnumerationType) -> Result { let mut out = match t.name.as_ref() { Some(name) => format!("enum {} {{\n", name), None => "enum {\n".to_string(), }; for member in t.members.iter() { writeln!(out, " {} = {},", member.name, member.value)?; } write!(out, "}}")?; Ok(out) } pub fn union_def_string(info: &DwarfInfo, typedefs: &TypedefMap, t: &UnionType) -> Result { let mut out = match t.name.as_ref() { Some(name) => format!("union {} {{\n", name), None => "union {\n".to_string(), }; let mut var_out = String::new(); for member in t.members.iter() { let ts = type_string(info, typedefs, &member.kind, true)?; write!(var_out, "{} {}{};", ts.prefix, member.name, ts.suffix)?; let size = if let Some(size) = member.byte_size { size } else { member.kind.size(info)? }; write!(var_out, " // offset {:#X}, size {:#X}", member.offset, size)?; writeln!(var_out)?; } write!(out, "{}", indent_all_by(4, var_out))?; write!(out, "}}")?; Ok(out) } pub fn process_offset(block: &[u8], e: Endian) -> Result { if block.len() == 6 && block[0] == LocationOp::Const as u8 && block[5] == LocationOp::Add as u8 { Ok(u32::from_bytes(*array_ref!(block, 1, 4), e)) } else { Err(anyhow!("Unhandled location data, expected offset")) } } pub fn process_address(block: &[u8], e: Endian) -> Result { if block.len() == 5 && block[0] == LocationOp::Address as u8 { Ok(u32::from_bytes(*array_ref!(block, 1, 4), e)) } else { Err(anyhow!("Unhandled location data, expected address")) } } pub const REGISTER_NAMES: [&str; 109] = [ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", // 0-7 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", // 8-15 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", // 16-23 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", // 24-31 "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", // 32-39 "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", // 40-47 "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", // 48-55 "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", // 56-63 "mq", "lr", "ctr", "ap", "cr0", "cr1", "cr2", "cr3", // 64-71 "cr4", "cr5", "cr6", "cr7", "xer", "v0", "v1", "v2", // 72-79 "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10", // 80-87 "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", // 88-95 "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", // 96-103 "v27", "v28", "v29", "v30", "v31", // 104-108 ]; pub const fn register_name(reg: u32) -> &'static str { if reg < REGISTER_NAMES.len() as u32 { REGISTER_NAMES[reg as usize] } else { "[invalid]" } } pub fn process_variable_location(block: &[u8], e: Endian) -> Result { if block.len() == 5 && (block[0] == LocationOp::Register as u8 || block[0] == LocationOp::BaseRegister as u8 || block[0] == LocationOp::MwFpReg as u8) { Ok(register_name(u32::from_bytes(*array_ref!(block, 1, 4), e)).to_string()) } else if block.len() == 5 && block[0] == LocationOp::Address as u8 { Ok(format!("@ {:#010X}", u32::from_bytes(*array_ref!(block, 1, 4), e))) } else if block.len() == 11 && block[0] == LocationOp::BaseRegister as u8 && block[5] == LocationOp::Const as u8 && block[10] == LocationOp::Add as u8 { Ok(format!( "{}+{:#X}", register_name(u32::from_bytes(*array_ref!(block, 1, 4), e)), u32::from_bytes(*array_ref!(block, 6, 4), e) )) } else { Err(anyhow!("Unhandled location data {:?}, expected variable loc", block)) } } fn process_inheritance_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::Inheritance, "{:?} is not an Inheritance tag", tag.kind); let mut name = None; let mut base_type = None; let mut offset = None; let mut visibility = None; let mut virtual_base = false; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), ( AttributeKind::FundType | AttributeKind::ModFundType | AttributeKind::UserDefType | AttributeKind::ModUDType, _, ) => base_type = Some(process_type(attr, info.e)?), (AttributeKind::Location, AttributeValue::Block(block)) => { offset = Some(process_offset(block, info.e)?) } (AttributeKind::Private, _) => visibility = Some(Visibility::Private), (AttributeKind::Protected, _) => visibility = Some(Visibility::Protected), (AttributeKind::Public, _) => visibility = Some(Visibility::Public), (AttributeKind::Virtual, _) => virtual_base = true, _ => { bail!("Unhandled Inheritance attribute {:?}", attr); } } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled Inheritance child {:?}", child.kind); } let base_type = base_type.ok_or_else(|| anyhow!("Inheritance without base type: {:?}", tag))?; let offset = offset.ok_or_else(|| anyhow!("Inheritance without offset: {:?}", tag))?; let visibility = visibility.ok_or_else(|| anyhow!("Inheritance without visibility: {:?}", tag))?; Ok(StructureBase { name, base_type, offset, visibility, virtual_base }) } fn process_structure_member_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::Member, "{:?} is not a Member tag", tag.kind); let mut name = None; let mut member_type = None; let mut offset = None; let mut byte_size = None; let mut bit_size = None; let mut bit_offset = None; let mut visibility = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), ( AttributeKind::FundType | AttributeKind::ModFundType | AttributeKind::UserDefType | AttributeKind::ModUDType, _, ) => member_type = Some(process_type(attr, info.e)?), (AttributeKind::Location, AttributeValue::Block(block)) => { offset = Some(process_offset(block, info.e)?) } (AttributeKind::ByteSize, &AttributeValue::Data4(value)) => byte_size = Some(value), (AttributeKind::BitSize, &AttributeValue::Data4(value)) => bit_size = Some(value), (AttributeKind::BitOffset, &AttributeValue::Data2(value)) => bit_offset = Some(value), (AttributeKind::Private, _) => visibility = Some(Visibility::Private), (AttributeKind::Protected, _) => visibility = Some(Visibility::Protected), (AttributeKind::Public, _) => visibility = Some(Visibility::Public), (AttributeKind::Member, &AttributeValue::Reference(_key)) => { // Pointer to parent structure, ignore } _ => { bail!("Unhandled Member attribute {:?}", attr); } } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled Member child {:?}", child.kind); } let name = name.ok_or_else(|| anyhow!("Member without name: {:?}", tag))?; let member_type = member_type.ok_or_else(|| anyhow!("Member without type: {:?}", tag))?; let offset = offset.ok_or_else(|| anyhow!("Member without offset: {:?}", tag))?; let bit = match (bit_size, bit_offset) { (Some(bit_size), Some(bit_offset)) => Some(BitData { bit_size, bit_offset }), (None, None) => None, _ => bail!("Mismatched bit attributes in Member: {tag:?}"), }; let visibility = visibility.unwrap_or(Visibility::Public); Ok(StructureMember { name: name.clone(), kind: member_type, offset, bit, visibility, byte_size, }) } fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!( matches!(tag.kind, TagKind::StructureType | TagKind::ClassType), "{:?} is not a Structure type tag", tag.kind ); let mut name = None; let mut byte_size = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), (AttributeKind::ByteSize, &AttributeValue::Data4(value)) => byte_size = Some(value), (AttributeKind::Member, &AttributeValue::Reference(_key)) => { // Pointer to parent structure, ignore } _ => { bail!("Unhandled structure attribute {:?}", attr); } } } let mut members = Vec::new(); let mut bases = Vec::new(); for child in tag.children(&info.tags) { match child.kind { TagKind::Inheritance => bases.push(process_inheritance_tag(info, child)?), TagKind::Member => members.push(process_structure_member_tag(info, child)?), TagKind::Typedef => { // TODO? // info!("Structure {:?} Typedef: {:?}", name, child); } TagKind::Subroutine | TagKind::GlobalSubroutine => { // TODO } TagKind::StructureType | TagKind::ArrayType | TagKind::EnumerationType | TagKind::UnionType | TagKind::ClassType | TagKind::SubroutineType | TagKind::PtrToMemberType => { // Variable type, ignore } kind => bail!("Unhandled StructureType child {:?}", kind), } } Ok(StructureType { kind: if tag.kind == TagKind::ClassType { StructureKind::Class } else { StructureKind::Struct }, name, byte_size, members, bases, }) } fn process_array_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::ArrayType, "{:?} is not an ArrayType tag", tag.kind); let mut subscr_data = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::SubscrData, AttributeValue::Block(data)) => { subscr_data = Some(process_array_subscript_data(data, info.e).with_context(|| { format!("Failed to process SubscrData for tag: {:?}", tag) })?) } (AttributeKind::Ordering, val) => match val { AttributeValue::Data2(d2) => { let order = ArrayOrdering::try_from_primitive(*d2)?; if order == ArrayOrdering::ColMajor { log::warn!("Column Major Ordering in Tag {}, Cannot guarantee array will be correct if original source is in different programming language.", tag.key); } } _ => bail!("Unhandled ArrayType attribute {:?}", attr), }, _ => { bail!("Unhandled ArrayType attribute {:?}", attr) } } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled ArrayType child {:?}", child.kind); } let (element_type, dimensions) = subscr_data.ok_or_else(|| anyhow!("ArrayType without SubscrData: {:?}", tag))?; Ok(ArrayType { element_type: Box::from(element_type), dimensions }) } fn process_array_subscript_data(data: &[u8], e: Endian) -> Result<(Type, Vec)> { let mut element_type = None; let mut dimensions = Vec::new(); let mut data = data; while !data.is_empty() { let format = SubscriptFormat::try_from( data.first().cloned().ok_or_else(|| anyhow!("Empty SubscrData"))?, ) .context("Unknown array subscript format")?; data = &data[1..]; match format { SubscriptFormat::FundTypeConstConst => { let index_type = FundType::try_from(u16::from_bytes(data[..2].try_into()?, e)) .context("Invalid fundamental type ID")?; let low_bound = u32::from_bytes(data[2..6].try_into()?, e); ensure!(low_bound == 0, "Invalid array low bound {low_bound}, expected 0"); let high_bound = u32::from_bytes(data[6..10].try_into()?, e); data = &data[10..]; dimensions.push(ArrayDimension { index_type: Type { kind: TypeKind::Fundamental(index_type), modifiers: vec![] }, // u32::MAX will wrap to 0, meaning unbounded size: NonZeroU32::new(high_bound.wrapping_add(1)), }); } SubscriptFormat::FundTypeConstLocation => { let index_type = FundType::try_from(u16::from_bytes(*array_ref!(data, 0, 2), e)) .context("Invalid fundamental type ID")?; let low_bound = u32::from_bytes(*array_ref!(data, 2, 4), e); ensure!(low_bound == 0, "Invalid array low bound {low_bound}, expected 0"); let size = u16::from_bytes(*array_ref!(data, 6, 2), e); let (block, remain) = data[8..].split_at(size as usize); let location = if block.is_empty() { 0 } else { process_offset(block, e)? }; data = remain; dimensions.push(ArrayDimension { index_type: Type { kind: TypeKind::Fundamental(index_type), modifiers: vec![] }, size: NonZeroU32::new(location), }); } SubscriptFormat::ElementType => { let mut cursor = Cursor::new(data); let type_attr = read_attribute(&mut cursor, e)?; element_type = Some(process_type(&type_attr, e)?); data = &data[cursor.position() as usize..]; } _ => bail!("Unhandled subscript format type {:?}", format), } } let element_type = element_type.ok_or_else(|| anyhow!("ArrayType without ElementType"))?; Ok((element_type, dimensions)) } fn process_enumeration_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::EnumerationType, "{:?} is not an EnumerationType tag", tag.kind); let mut name = None; let mut byte_size = None; let mut members = Vec::new(); for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), (AttributeKind::ByteSize, &AttributeValue::Data4(value)) => byte_size = Some(value), (AttributeKind::ElementList, AttributeValue::Block(data)) => { let mut cursor = Cursor::new(data); while cursor.position() < data.len() as u64 { let value = i32::from_reader(&mut cursor, info.e)?; let name = read_string(&mut cursor)?; members.push(EnumerationMember { name, value }); } } _ => { bail!("Unhandled EnumerationType attribute {:?}", attr); } } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled EnumerationType child {:?}", child.kind); } let byte_size = byte_size.ok_or_else(|| anyhow!("EnumerationType without ByteSize: {:?}", tag))?; Ok(EnumerationType { name, byte_size, members }) } fn process_union_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::UnionType, "{:?} is not a UnionType tag", tag.kind); let mut name = None; let mut byte_size = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), (AttributeKind::ByteSize, &AttributeValue::Data4(value)) => byte_size = Some(value), (AttributeKind::Member, &AttributeValue::Reference(_key)) => { // Pointer to parent structure, ignore } _ => { bail!("Unhandled UnionType attribute {:?}", attr); } } } let mut members = Vec::new(); for child in tag.children(&info.tags) { match child.kind { TagKind::Member => members.push(process_structure_member_tag(info, child)?), TagKind::StructureType | TagKind::ArrayType | TagKind::EnumerationType | TagKind::UnionType | TagKind::ClassType | TagKind::SubroutineType | TagKind::PtrToMemberType => { // Variable type, ignore } kind => bail!("Unhandled UnionType child {:?}", kind), } } let byte_size = byte_size.ok_or_else(|| anyhow!("UnionType without ByteSize: {:?}", tag))?; Ok(UnionType { name, byte_size, members }) } fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!( matches!( tag.kind, TagKind::SubroutineType | TagKind::GlobalSubroutine | TagKind::Subroutine ), "{:?} is not a Subroutine tag", tag.kind ); let mut name = None; let mut mangled_name = None; let mut return_type = None; let mut prototyped = false; let mut parameters = Vec::new(); let mut var_args = false; 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, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), (AttributeKind::MwMangled, AttributeValue::String(s)) => mangled_name = Some(s.clone()), ( AttributeKind::FundType | AttributeKind::ModFundType | AttributeKind::UserDefType | AttributeKind::ModUDType, _, ) => return_type = Some(process_type(attr, info.e)?), (AttributeKind::Prototyped, _) => prototyped = true, (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); } (AttributeKind::MwGlobalRefsBlock, AttributeValue::Block(_)) => { // Global references block } (AttributeKind::ReturnAddr, AttributeValue::Block(_block)) => { // let location = process_variable_location(block)?; // info!("ReturnAddr: {}", location); } (AttributeKind::Member, &AttributeValue::Reference(key)) => { member_of = Some(key); } (AttributeKind::MwPrologueEnd, &AttributeValue::Address(_addr)) => { // Prologue end } (AttributeKind::MwEpilogueStart, &AttributeValue::Address(_addr)) => { // Epilogue start } ( AttributeKind::MwRestoreSp | AttributeKind::MwRestoreS0 | AttributeKind::MwRestoreS1 | AttributeKind::MwRestoreS2 | AttributeKind::MwRestoreS3 | AttributeKind::MwRestoreS4 | AttributeKind::MwRestoreS5 | AttributeKind::MwRestoreS6 | AttributeKind::MwRestoreS7 | AttributeKind::MwRestoreS8 | AttributeKind::MwRestoreF20 | AttributeKind::MwRestoreF21 | AttributeKind::MwRestoreF22 | AttributeKind::MwRestoreF23 | AttributeKind::MwRestoreF24 | AttributeKind::MwRestoreF25 | AttributeKind::MwRestoreF26 | AttributeKind::MwRestoreF27 | AttributeKind::MwRestoreF28 | AttributeKind::MwRestoreF29 | AttributeKind::MwRestoreF30, AttributeValue::Block(_), ) => { // Restore register } (AttributeKind::Inline, _) => inline = true, (AttributeKind::Specification, &AttributeValue::Reference(key)) => { let spec_tag = info .tags .get(&key) .ok_or_else(|| anyhow!("Failed to locate specification tag {}", key))?; // Merge attributes from specification tag let spec = process_subroutine_tag(info, spec_tag)?; name = name.or(spec.name); mangled_name = mangled_name.or(spec.mangled_name); return_type = return_type.or(Some(spec.return_type)); prototyped = prototyped || spec.prototyped; parameters.extend(spec.parameters); var_args = var_args || spec.var_args; references.extend(spec.references); member_of = member_of.or(spec.member_of); inline = inline || spec.inline; } _ => { bail!("Unhandled SubroutineType attribute {:?}", attr); } } } let mut variables = Vec::new(); let mut labels = Vec::new(); let mut blocks = Vec::new(); let mut inlines = Vec::new(); for child in tag.children(&info.tags) { ensure!(!var_args, "{:?} after UnspecifiedParameters", child.kind); match child.kind { TagKind::FormalParameter => { parameters.push(process_subroutine_parameter_tag(info, child)?) } TagKind::UnspecifiedParameters => var_args = true, TagKind::LocalVariable => variables.push(process_local_variable_tag(info, child)?), TagKind::GlobalVariable => { // TODO GlobalVariable refs? } TagKind::Label => labels.push(process_subroutine_label_tag(info, child)?), TagKind::LexicalBlock => blocks.push(process_subroutine_block_tag(info, child)?), TagKind::InlinedSubroutine => { inlines.push(process_inlined_subroutine_tag(info, child)?) } TagKind::StructureType | TagKind::ArrayType | TagKind::EnumerationType | TagKind::UnionType | TagKind::ClassType | TagKind::SubroutineType | TagKind::PtrToMemberType => { // Variable type, ignore } kind => bail!("Unhandled SubroutineType child {:?}", kind), } } let return_type = return_type .unwrap_or_else(|| Type { kind: TypeKind::Fundamental(FundType::Void), modifiers: vec![] }); let local = tag.kind == TagKind::Subroutine; Ok(SubroutineType { name, mangled_name, return_type, parameters, var_args, prototyped, references, member_of, variables, inline, local, labels, blocks, inlines, start_address, end_address, }) } fn process_subroutine_label_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::Label, "{:?} is not a Label tag", tag.kind); let mut name = None; let mut address = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), (AttributeKind::LowPc, &AttributeValue::Address(addr)) => address = Some(addr), _ => bail!("Unhandled Label attribute {:?}", attr), } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled Label child {:?}", child.kind); } let name = name.ok_or_else(|| anyhow!("Label without name: {:?}", tag))?; let address = address.ok_or_else(|| anyhow!("Label without address: {:?}", tag))?; Ok(SubroutineLabel { name, address }) } fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::LexicalBlock, "{:?} is not a LexicalBlock tag", tag.kind); let mut name = 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::LowPc, &AttributeValue::Address(addr)) => start_address = Some(addr), (AttributeKind::HighPc, &AttributeValue::Address(addr)) => end_address = Some(addr), _ => bail!("Unhandled Label attribute {:?}", attr), } } let mut variables = Vec::new(); let mut blocks = Vec::new(); for child in tag.children(&info.tags) { match child.kind { TagKind::LocalVariable => variables.push(process_local_variable_tag(info, child)?), TagKind::GlobalVariable => { // TODO GlobalVariable refs? } TagKind::LexicalBlock => blocks.push(process_subroutine_block_tag(info, child)?), TagKind::StructureType | TagKind::ArrayType | TagKind::EnumerationType | TagKind::UnionType | TagKind::ClassType | TagKind::SubroutineType | TagKind::PtrToMemberType => { // Variable type, ignore } kind => bail!("Unhandled LexicalBlock child {:?}", kind), } } let start_address = start_address.ok_or_else(|| anyhow!("LexicalBlock without start address: {:?}", tag))?; let end_address = end_address.ok_or_else(|| anyhow!("LexicalBlock without end address: {:?}", tag))?; Ok(SubroutineBlock { name, start_address, end_address, variables, blocks }) } fn process_inlined_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!( tag.kind == TagKind::InlinedSubroutine, "{:?} is not an InlinedSubroutine tag", tag.kind ); let mut specification = None; let mut start_address = None; let mut end_address = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Specification, &AttributeValue::Reference(key)) => { specification = Some(key) } (AttributeKind::LowPc, &AttributeValue::Address(addr)) => start_address = Some(addr), (AttributeKind::HighPc, &AttributeValue::Address(addr)) => end_address = Some(addr), _ => bail!("Unhandled InlinedSubroutine attribute {:?}", attr), } } for child in tag.children(&info.tags) { match child.kind { TagKind::GlobalVariable => { // TODO GlobalVariable refs? } kind => bail!("Unhandled InlinedSubroutine child {:?}", kind), } } let specification = specification .ok_or_else(|| anyhow!("InlinedSubroutine without specification: {:?}", tag))?; let start_address = start_address .ok_or_else(|| anyhow!("InlinedSubroutine without start address: {:?}", tag))?; let end_address = end_address.ok_or_else(|| anyhow!("InlinedSubroutine without end address: {:?}", tag))?; Ok(SubroutineInline { specification, start_address, end_address }) } fn process_subroutine_parameter_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::FormalParameter, "{:?} is not a FormalParameter tag", tag.kind); let mut name = None; let mut kind = None; let mut location = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), ( AttributeKind::FundType | AttributeKind::ModFundType | AttributeKind::UserDefType | AttributeKind::ModUDType, _, ) => kind = Some(process_type(attr, info.e)?), (AttributeKind::Location, AttributeValue::Block(block)) => { location = Some(process_variable_location(block, info.e)?) } (AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => { // TODO? // info!("MwDwarf2Location: {:?} in {:?}", block, tag); } (AttributeKind::Specification, &AttributeValue::Reference(key)) => { let spec_tag = info .tags .get(&key) .ok_or_else(|| anyhow!("Failed to locate specification tag {}", key))?; // Merge attributes from specification tag let spec = process_subroutine_parameter_tag(info, spec_tag)?; name = name.or(spec.name); kind = kind.or(Some(spec.kind)); location = location.or(spec.location); } _ => bail!("Unhandled SubroutineParameter attribute {:?}", attr), } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled SubroutineParameter child {:?}", child.kind); } let kind = kind.ok_or_else(|| anyhow!("SubroutineParameter without type: {:?}", tag))?; Ok(SubroutineParameter { name, kind, location }) } fn process_local_variable_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::LocalVariable, "{:?} is not a LocalVariable tag", tag.kind); let mut mangled_name = None; let mut name = None; let mut kind = None; let mut location = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), (AttributeKind::MwMangled, AttributeValue::String(s)) => mangled_name = Some(s.clone()), ( AttributeKind::FundType | AttributeKind::ModFundType | AttributeKind::UserDefType | AttributeKind::ModUDType, _, ) => kind = Some(process_type(attr, info.e)?), (AttributeKind::Location, AttributeValue::Block(block)) => { if !block.is_empty() { location = Some(process_variable_location(block, info.e)?); } } (AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => { // TODO? // info!("MwDwarf2Location: {:?} in {:?}", block, tag); } (AttributeKind::Specification, &AttributeValue::Reference(key)) => { let spec_tag = info .tags .get(&key) .ok_or_else(|| anyhow!("Failed to locate specification tag {}", key))?; // Merge attributes from specification tag let spec = process_local_variable_tag(info, spec_tag)?; name = name.or(spec.name); kind = kind.or(Some(spec.kind)); location = location.or(spec.location); } _ => { bail!("Unhandled LocalVariable attribute {:?}", attr); } } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled LocalVariable child {:?}", child.kind); } let kind = kind.ok_or_else(|| anyhow!("LocalVariable without type: {:?}", tag))?; Ok(SubroutineVariable { name, mangled_name, kind, location }) } fn process_ptr_to_member_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::PtrToMemberType, "{:?} is not a PtrToMemberType tag", tag.kind); let mut kind = None; let mut containing_type = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} ( AttributeKind::FundType | AttributeKind::ModFundType | AttributeKind::UserDefType | AttributeKind::ModUDType, _, ) => kind = Some(process_type(attr, info.e)?), (AttributeKind::ContainingType, &AttributeValue::Reference(key)) => { containing_type = Some(key) } _ => { bail!("Unhandled PtrToMemberType attribute {:?}", attr); } } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled PtrToMemberType child {:?}", child.kind); } let kind = kind.ok_or_else(|| anyhow!("PtrToMemberType without type: {:?}", tag))?; let containing_type = containing_type .ok_or_else(|| anyhow!("PtrToMemberType without containing type: {:?}", tag))?; Ok(PtrToMemberType { kind, containing_type }) } pub fn ud_type(info: &DwarfInfo, tag: &Tag) -> Result { match tag.kind { TagKind::ArrayType => Ok(UserDefinedType::Array(process_array_tag(info, tag)?)), TagKind::StructureType | TagKind::ClassType => { Ok(UserDefinedType::Structure(process_structure_tag(info, tag)?)) } TagKind::EnumerationType => { Ok(UserDefinedType::Enumeration(process_enumeration_tag(info, tag)?)) } TagKind::UnionType => Ok(UserDefinedType::Union(process_union_tag(info, tag)?)), TagKind::SubroutineType | TagKind::GlobalSubroutine | TagKind::Subroutine => { Ok(UserDefinedType::Subroutine(process_subroutine_tag(info, tag)?)) } TagKind::PtrToMemberType => { Ok(UserDefinedType::PtrToMember(process_ptr_to_member_tag(info, tag)?)) } kind => Err(anyhow!("Unhandled user defined type {kind:?}")), } } pub fn process_modifiers(block: &[u8]) -> Result> { let mut out = Vec::with_capacity(block.len()); for &b in block { out.push(Modifier::try_from(b)?); } Ok(out) } pub fn process_type(attr: &Attribute, e: Endian) -> Result { match (attr.kind, &attr.value) { (AttributeKind::FundType, &AttributeValue::Data2(type_id)) => { let fund_type = FundType::try_from(type_id) .with_context(|| format!("Invalid fundamental type ID '{}'", type_id))?; Ok(Type { kind: TypeKind::Fundamental(fund_type), modifiers: vec![] }) } (AttributeKind::ModFundType, AttributeValue::Block(ops)) => { let type_id = u16::from_bytes(ops[ops.len() - 2..].try_into()?, e); let fund_type = FundType::try_from(type_id) .with_context(|| format!("Invalid fundamental type ID '{}'", type_id))?; let modifiers = process_modifiers(&ops[..ops.len() - 2])?; Ok(Type { kind: TypeKind::Fundamental(fund_type), modifiers }) } (AttributeKind::UserDefType, &AttributeValue::Reference(key)) => { Ok(Type { kind: TypeKind::UserDefined(key), modifiers: vec![] }) } (AttributeKind::ModUDType, AttributeValue::Block(ops)) => { let ud_ref = u32::from_bytes(ops[ops.len() - 4..].try_into()?, e); let modifiers = process_modifiers(&ops[..ops.len() - 4])?; Ok(Type { kind: TypeKind::UserDefined(ud_ref), modifiers }) } _ => Err(anyhow!("Invalid type attribute {:?}", attr)), } } 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 => { Ok(TagType::Variable(process_variable_tag(info, tag)?)) } TagKind::StructureType | TagKind::ArrayType | TagKind::EnumerationType | TagKind::UnionType | TagKind::ClassType | TagKind::SubroutineType | TagKind::GlobalSubroutine | TagKind::Subroutine | TagKind::PtrToMemberType => Ok(TagType::UserDefined(ud_type(info, tag)?)), kind => Err(anyhow!("Unhandled root tag type {:?}", kind)), } } /// Logic to skip uninteresting tags pub fn should_skip_tag(tag_type: &TagType) -> bool { match tag_type { TagType::Variable(_) => false, TagType::Typedef(_) => false, TagType::UserDefined(t) => !t.is_definition(), } } pub fn tag_type_string( info: &DwarfInfo, typedefs: &TypedefMap, tag_type: &TagType, ) -> Result { match tag_type { TagType::Typedef(t) => typedef_string(info, typedefs, t), TagType::Variable(v) => variable_string(info, typedefs, v, true), TagType::UserDefined(ud) => { let ud_str = ud_type_def(info, typedefs, ud)?; match ud { UserDefinedType::Structure(_) | UserDefinedType::Enumeration(_) | UserDefinedType::Union(_) => Ok(format!("{};", ud_str)), _ => Ok(ud_str), } } } } fn typedef_string(info: &DwarfInfo, typedefs: &TypedefMap, typedef: &TypedefTag) -> Result { let ts = type_string(info, typedefs, &typedef.kind, true)?; Ok(format!("typedef {} {}{};", ts.prefix, typedef.name, ts.suffix)) } fn variable_string( info: &DwarfInfo, typedefs: &TypedefMap, variable: &VariableTag, include_extra: bool, ) -> Result { let ts = type_string(info, typedefs, &variable.kind, include_extra)?; let mut out = if variable.local { "static ".to_string() } else { String::new() }; out.push_str(&ts.prefix); out.push(' '); out.push_str(variable.name.as_deref().unwrap_or("[unknown]")); out.push_str(&ts.suffix); 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) } fn process_typedef_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!(tag.kind == TagKind::Typedef, "{:?} is not a typedef tag", tag.kind); let mut name = None; let mut kind = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), ( AttributeKind::FundType | AttributeKind::ModFundType | AttributeKind::UserDefType | AttributeKind::ModUDType, _, ) => kind = Some(process_type(attr, info.e)?), _ => { bail!("Unhandled Typedef attribute {:?}", attr); } } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled Typedef child {:?}", child.kind); } let name = name.ok_or_else(|| anyhow!("Typedef without Name: {:?}", tag))?; let kind = kind.ok_or_else(|| anyhow!("Typedef without Type: {:?}", tag))?; Ok(TypedefTag { name, kind }) } fn process_variable_tag(info: &DwarfInfo, tag: &Tag) -> Result { ensure!( matches!(tag.kind, TagKind::GlobalVariable | TagKind::LocalVariable), "{:?} is not a variable tag", tag.kind ); let mut name = None; let mut mangled_name = None; let mut kind = None; let mut address = None; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} (AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()), (AttributeKind::MwMangled, AttributeValue::String(s)) => mangled_name = Some(s.clone()), ( AttributeKind::FundType | AttributeKind::ModFundType | AttributeKind::UserDefType | AttributeKind::ModUDType, _, ) => kind = Some(process_type(attr, info.e)?), (AttributeKind::Location, AttributeValue::Block(block)) => { address = Some(process_address(block, info.e)?) } _ => { bail!("Unhandled Variable attribute {:?}", attr); } } } if let Some(child) = tag.children(&info.tags).first() { bail!("Unhandled Variable child {:?}", child.kind); } let kind = kind.ok_or_else(|| anyhow!("Variable without Type: {:?}", tag))?; let local = tag.kind == TagKind::LocalVariable; Ok(VariableTag { name, mangled_name, kind, address, local }) }