From 85fc17cb8b35dda4e70bc67b84c942610dc79619 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Wed, 17 Dec 2025 20:46:38 +0100 Subject: [PATCH 01/24] Support type definitions inside functions in dwarf dump Detect subroutine parent class and constness Support nested typedefs --- src/util/dwarf.rs | 126 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 32 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index e96123c..d65cb09 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -741,6 +741,8 @@ pub struct StructureType { pub members: Vec, pub bases: Vec, pub inner_types: Vec, + pub member_functions: Vec, // TODO change to reference/id + pub typedefs: Vec, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -827,8 +829,11 @@ pub struct SubroutineType { pub labels: Vec, pub blocks: Vec, pub inlines: Vec, + pub inner_types: Vec, pub start_address: Option, pub end_address: Option, + pub member: bool, + pub is_const: bool, } #[derive(Debug, Clone)] @@ -998,14 +1003,7 @@ impl Type { } 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) - } + TypeKind::UserDefined(key) => get_udt_by_key(info, key)?.size(info), } } } @@ -1096,11 +1094,13 @@ pub fn type_string( .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)? + ud_type_string( + info, + typedefs, + &get_udt_by_key(info, key)?, + true, + include_anonymous_def, + )? } } }; @@ -1119,12 +1119,9 @@ fn type_name(info: &DwarfInfo, typedefs: &TypedefMap, t: &Type) -> Result Visibility::Public, StructureKind::Class => Visibility::Private, @@ -2004,14 +2028,12 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { let mut members = Vec::new(); let mut bases = Vec::new(); let mut inner_types = Vec::new(); + let mut typedefs = 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::Typedef => typedefs.push(process_typedef_tag(info, child)?), TagKind::Subroutine | TagKind::GlobalSubroutine => { // TODO } @@ -2044,6 +2066,8 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { members, bases, inner_types, + member_functions: Vec::new(), + typedefs, }) } @@ -2246,6 +2270,8 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result let mut start_address = None; let mut end_address = None; let mut virtual_ = false; + let mut parent_structure = None; + let mut is_const = false; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} @@ -2340,10 +2366,25 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result let mut labels = Vec::new(); let mut blocks = Vec::new(); let mut inlines = Vec::new(); + let mut inner_types = Vec::new(); for child in tag.children(&info.tags) { match child.kind { TagKind::FormalParameter => { - parameters.push(process_subroutine_parameter_tag(info, child)?) + let param = process_subroutine_parameter_tag(info, child)?; + if member_of.is_some() && parent_structure.is_none() && param.name.as_deref() == Some("this") { + let modifiers = ¶m.kind.modifiers; + // TODO is this a proper check? + if modifiers.len() >= 3 && modifiers[0] == Modifier::Const && modifiers[2] == Modifier::Const { + is_const = true; + } + // This is needed because parent_structure differs from member_of in virtual function overrides + if let TypeKind::UserDefined(key) = param.kind.kind { + if let UserDefinedType::Structure(structure) = get_udt_by_key(info, key)? { + parent_structure = Some(structure); + } + } + } + parameters.push(param); } TagKind::UnspecifiedParameters => var_args = true, TagKind::LocalVariable => variables.push(process_local_variable_tag(info, child)?), @@ -2357,11 +2398,15 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result } } TagKind::InlinedSubroutine => inlines.push(process_subroutine_tag(info, child)?), - TagKind::StructureType - | TagKind::ArrayType - | TagKind::EnumerationType - | TagKind::UnionType - | TagKind::ClassType + TagKind::StructureType | TagKind::ClassType => { + inner_types.push(UserDefinedType::Structure(process_structure_tag(info, child)?)) + } + TagKind::EnumerationType => inner_types + .push(UserDefinedType::Enumeration(process_enumeration_tag(info, child)?)), + TagKind::UnionType => { + inner_types.push(UserDefinedType::Union(process_union_tag(info, child)?)) + } + TagKind::ArrayType | TagKind::SubroutineType | TagKind::PtrToMemberType | TagKind::Typedef => { @@ -2374,7 +2419,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result let return_type = return_type .unwrap_or_else(|| Type { kind: TypeKind::Fundamental(FundType::Void), modifiers: vec![] }); let local = tag.kind == TagKind::Subroutine; - Ok(SubroutineType { + let subroutine = SubroutineType { name, mangled_name, return_type, @@ -2390,9 +2435,14 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result labels, blocks, inlines, + inner_types, start_address, end_address, - }) + member: parent_structure.is_some(), + is_const, + }; + // TODO add a reference to this to parent_structure + Ok(subroutine) } fn process_subroutine_label_tag(info: &DwarfInfo, tag: &Tag) -> Result { @@ -2607,6 +2657,15 @@ fn process_ptr_to_member_tag(info: &DwarfInfo, tag: &Tag) -> Result Result { + let tag = match info.tags.get(&key) { + Some(t) => t, + None => return Err(anyhow!("Failed to locate user defined type {}", key)), + }; + + ud_type(info, tag) +} + pub fn ud_type(info: &DwarfInfo, tag: &Tag) -> Result { match tag.kind { TagKind::ArrayType => Ok(UserDefinedType::Array(process_array_tag(info, tag)?)), @@ -2837,6 +2896,9 @@ fn process_typedef_tag(info: &DwarfInfo, tag: &Tag) -> Result { | AttributeKind::ModUDType, _, ) => kind = Some(process_type(attr, info.e)?), + (AttributeKind::Member, _) => { + // can be ignored for now + }, _ => { bail!("Unhandled Typedef attribute {:?}", attr); } From 864afb49965c884ae70e1ce193d57e9633ffcb2e Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 00:08:54 +0100 Subject: [PATCH 02/24] Omit unnecessary/duplicate dwarf info --- src/util/dwarf.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index d65cb09..58e6f2d 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1698,6 +1698,21 @@ pub fn struct_def_string( writeln!(out, " // total size: {byte_size:#X}")?; } for inner_type in &t.inner_types { + // GCC emits template base classes as a nested struct, which is redundant so we skip them + if let UserDefinedType::Structure(structure) = inner_type { + if let Some(struct_name) = structure.name.as_ref() { + if struct_name.contains("<") && t.bases.iter().any(|base| { + let resolved_name_opt: Option = match &base.name { + Some(name) => Some(name.clone()), + None => type_name(info, typedefs, &base.base_type).ok(), + }; + + structure.name == resolved_name_opt + }) { + continue; + } + } + } writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; } if !t.inner_types.is_empty() { @@ -2033,7 +2048,17 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { match child.kind { TagKind::Inheritance => bases.push(process_inheritance_tag(info, child)?), TagKind::Member => members.push(process_structure_member_tag(info, child)?), - TagKind::Typedef => typedefs.push(process_typedef_tag(info, child)?), + TagKind::Typedef => { + let td = process_typedef_tag(info, child)?; + // GCC generates a typedef in templated structs with the name of the template + // Let's filter it out to not confuse the user + let is_template = name + .as_deref() + .is_some_and(|n| n.starts_with(&format!("{}<", td.name))); + if !is_template { + typedefs.push(td); + } + }, TagKind::Subroutine | TagKind::GlobalSubroutine => { // TODO } From afd2f01d0e8047949583f02410b36fb1ce0b0805 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 00:15:39 +0100 Subject: [PATCH 03/24] Move total size to before struct so that clangd picks it up --- src/util/dwarf.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 58e6f2d..64e1aa9 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1659,9 +1659,13 @@ pub fn struct_def_string( typedefs: &TypedefMap, t: &StructureType, ) -> Result { - let mut out = match t.kind { - StructureKind::Struct => "struct".to_string(), - StructureKind::Class => "class".to_string(), + let mut out = String::new(); + if let Some(byte_size) = t.byte_size { + writeln!(out, "// total size: {byte_size:#X}")?; + } + match t.kind { + StructureKind::Struct => out.push_str("struct"), + StructureKind::Class => out.push_str("class"), }; if let Some(name) = t.name.as_ref() { if name.starts_with('@') { @@ -1694,9 +1698,6 @@ pub fn struct_def_string( } } out.push_str(" {\n"); - if let Some(byte_size) = t.byte_size { - writeln!(out, " // total size: {byte_size:#X}")?; - } for inner_type in &t.inner_types { // GCC emits template base classes as a nested struct, which is redundant so we skip them if let UserDefinedType::Structure(structure) = inner_type { From ef83fed47f629095a24238a7245e0069dc8dcc6a Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 02:07:16 +0100 Subject: [PATCH 04/24] Subroutine typedefs and subroutine block inner types --- src/util/dwarf.rs | 66 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 64e1aa9..342e55c 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -741,7 +741,6 @@ pub struct StructureType { pub members: Vec, pub bases: Vec, pub inner_types: Vec, - pub member_functions: Vec, // TODO change to reference/id pub typedefs: Vec, } @@ -810,6 +809,8 @@ pub struct SubroutineBlock { pub variables: Vec, pub blocks: Vec, pub inlines: Vec, + pub inner_types: Vec, + pub typedefs: Vec, } #[derive(Debug, Clone)] @@ -832,8 +833,9 @@ pub struct SubroutineType { pub inner_types: Vec, pub start_address: Option, pub end_address: Option, - pub member: bool, + pub is_direct_member: bool, pub is_const: bool, + pub typedefs: Vec, } #[derive(Debug, Clone)] @@ -1437,6 +1439,13 @@ pub fn subroutine_def_string( } } + if !t.typedefs.is_empty() { + writeln!(out, "\n // Typedefs")?; + for typedef in &t.typedefs { + writeln!(out, "{}", &indent_all_by(4, &typedef_string(info, typedefs, typedef)?))?; + } + } + if !t.variables.is_empty() { writeln!(out, "\n // Local variables")?; let mut var_out = String::new(); @@ -1531,6 +1540,22 @@ fn subroutine_block_string( writeln!(var_out)?; } write!(out, "{}", indent_all_by(4, var_out))?; + + if !block.inner_types.is_empty() { + writeln!(out, "\n // Inner declarations")?; + + for inner_type in &block.inner_types { + writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; + } + } + + if !block.typedefs.is_empty() { + writeln!(out, "\n // Typedefs")?; + for typedef in &block.typedefs { + writeln!(out, "{}", &indent_all_by(4, &typedef_string(info, typedefs, typedef)?))?; + } + } + if !block.inlines.is_empty() { writeln!(out, "\n // Inlines")?; for inline in &block.inlines { @@ -1720,11 +1745,6 @@ pub fn struct_def_string( out.push_str("\n"); } - for member_function in &t.member_functions { - // TODO - // writeln!(out, "{};", &indent_all_by(4, &subroutine_type_string(info, typedefs, member_function)?))?; - } - for typedef in &t.typedefs { writeln!(out, "{}", &indent_all_by(4, &typedef_string(info, typedefs, typedef)?))?; } @@ -2092,7 +2112,6 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { members, bases, inner_types, - member_functions: Vec::new(), typedefs, }) } @@ -2393,6 +2412,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result let mut blocks = Vec::new(); let mut inlines = Vec::new(); let mut inner_types = Vec::new(); + let mut typedefs = Vec::new(); for child in tag.children(&info.tags) { match child.kind { TagKind::FormalParameter => { @@ -2432,10 +2452,10 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result TagKind::UnionType => { inner_types.push(UserDefinedType::Union(process_union_tag(info, child)?)) } + TagKind::Typedef => typedefs.push(process_typedef_tag(info, child)?), TagKind::ArrayType | TagKind::SubroutineType - | TagKind::PtrToMemberType - | TagKind::Typedef => { + | TagKind::PtrToMemberType => { // Variable type, ignore } kind => bail!("Unhandled SubroutineType child {:?}", kind), @@ -2464,8 +2484,9 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result inner_types, start_address, end_address, - member: parent_structure.is_some(), + is_direct_member: parent_structure.is_some(), is_const, + typedefs }; // TODO add a reference to this to parent_structure Ok(subroutine) @@ -2513,6 +2534,8 @@ fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result variables.push(process_local_variable_tag(info, child)?), @@ -2527,11 +2550,19 @@ fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result { inlines.push(process_subroutine_tag(info, child)?); } - TagKind::StructureType + TagKind::StructureType | TagKind::ClassType => { + inner_types.push(UserDefinedType::Structure(process_structure_tag(info, child)?)) + } + TagKind::EnumerationType => { + inner_types.push(UserDefinedType::Enumeration(process_enumeration_tag(info, child)?)); + } + TagKind::UnionType => { + inner_types.push(UserDefinedType::Union(process_union_tag(info, child)?)) + } + TagKind::Typedef => { + typedefs.push(process_typedef_tag(info, child)?); + } | TagKind::ArrayType - | TagKind::EnumerationType - | TagKind::UnionType - | TagKind::ClassType | TagKind::SubroutineType | TagKind::PtrToMemberType => { // Variable type, ignore @@ -2540,7 +2571,7 @@ fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result Result { @@ -2925,6 +2956,9 @@ fn process_typedef_tag(info: &DwarfInfo, tag: &Tag) -> Result { (AttributeKind::Member, _) => { // can be ignored for now }, + (AttributeKind::Specification, _) => { + // TODO + } _ => { bail!("Unhandled Typedef attribute {:?}", attr); } From 3213c035c52debf784e300ac796d5838e43e4839 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 04:01:19 +0100 Subject: [PATCH 05/24] Handle override, const, volatile, virtual, static function logic --- src/util/dwarf.rs | 112 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 85 insertions(+), 27 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 342e55c..c23fedd 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -823,6 +823,7 @@ pub struct SubroutineType { pub prototyped: bool, pub references: Vec, pub member_of: Option, + pub direct_member_of: Option, pub variables: Vec, pub inline: bool, pub virtual_: bool, @@ -831,11 +832,13 @@ pub struct SubroutineType { pub blocks: Vec, pub inlines: Vec, pub inner_types: Vec, + pub typedefs: Vec, pub start_address: Option, pub end_address: Option, - pub is_direct_member: bool, - pub is_const: bool, - pub typedefs: Vec, + pub const_: bool, + pub static_member: bool, + pub override_: bool, + pub volatile_: bool, } #[derive(Debug, Clone)] @@ -1359,31 +1362,65 @@ pub fn subroutine_def_string( } else if let (Some(start), Some(end)) = (t.start_address, t.end_address) { writeln!(out, "// Range: {start:#X} -> {end:#X}")?; } + + let mut base_name_opt = None; + + 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))?; + + if t.override_ { + writeln!(out, "// Overrides: {}", base_name)?; + } + base_name_opt = Some(base_name); + } + + let is_non_static_member = t.direct_member_of.is_some() && !t.static_member; + if is_non_static_member { + if let Some(param) = t.parameters.get(0) { + if let Some(location) = ¶m.location { + writeln!(out, "// this: {}", location)?; + } + } + } + let rt = type_string(info, typedefs, &t.return_type, true)?; - if t.local { + if t.local || t.static_member { out.push_str("static "); } if t.inline { out.push_str("inline "); } - if t.virtual_ { + if t.virtual_ && !t.override_ { out.push_str("virtual "); } 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))?; + + if t.override_ { + // GCC behavior + if let Some(direct_member_of) = t.direct_member_of { + let tag = info + .tags + .get(&direct_member_of) + .ok_or_else(|| anyhow!("Failed to locate direct_member_of tag {}", direct_member_of))?; + let direct_base_name = tag + .string_attribute(AttributeKind::Name) + .ok_or_else(|| anyhow!("direct_member_of tag {} has no name attribute", direct_member_of))?; + + write!(out, "{direct_base_name}::")?; + } + } else if let Some(base_name) = base_name_opt { write!(out, "{base_name}::")?; - // Handle constructors and destructors + // Handle MWCC constructors and destructors if let Some(name) = t.name.as_ref() { if name == "__dt" { write!(out, "~{base_name}")?; @@ -1407,8 +1444,9 @@ pub fn subroutine_def_string( parameters = "void".to_string(); } } else { - for (idx, parameter) in t.parameters.iter().enumerate() { - if idx > 0 { + let start_index = if is_non_static_member { 1 } else { 0 }; + for (idx, parameter) in t.parameters.iter().enumerate().skip(start_index) { + if idx > start_index { write!(parameters, ", ")?; } let ts = type_string(info, typedefs, ¶meter.kind, true)?; @@ -1426,9 +1464,15 @@ pub fn subroutine_def_string( } } write!(out, "({}){} ", parameters, rt.suffix)?; - if t.is_const { + if t.const_ { write!(out, "const ")?; } + if t.volatile_ { + write!(out, "volatile ")?; + } + if t.override_ { + write!(out, "override ")?; + } write!(out, "{{")?; if !t.inner_types.is_empty() { @@ -1723,6 +1767,7 @@ pub fn struct_def_string( } } out.push_str(" {\n"); + let mut emitted_inner_types = false; for inner_type in &t.inner_types { // GCC emits template base classes as a nested struct, which is redundant so we skip them if let UserDefinedType::Structure(structure) = inner_type { @@ -1739,9 +1784,10 @@ pub fn struct_def_string( } } } + emitted_inner_types = true; writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; } - if !t.inner_types.is_empty() { + if emitted_inner_types { out.push_str("\n"); } @@ -2315,8 +2361,10 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result let mut start_address = None; let mut end_address = None; let mut virtual_ = false; - let mut parent_structure = None; - let mut is_const = false; + let mut direct_base = None; // as opposed to a higher base class whose function is beging overridden + let mut direct_base_key = None; + let mut const_ = false; + let mut volatile_ = false; for attr in &tag.attributes { match (attr.kind, &attr.value) { (AttributeKind::Sibling, _) => {} @@ -2417,16 +2465,20 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result match child.kind { TagKind::FormalParameter => { let param = process_subroutine_parameter_tag(info, child)?; - if member_of.is_some() && parent_structure.is_none() && param.name.as_deref() == Some("this") { + if member_of.is_some() && direct_base.is_none() && param.name.as_deref() == Some("this") { let modifiers = ¶m.kind.modifiers; // TODO is this a proper check? if modifiers.len() >= 3 && modifiers[0] == Modifier::Const && modifiers[2] == Modifier::Const { - is_const = true; + const_ = true; + } + if modifiers.contains(&Modifier::Volatile) { + volatile_ = true; } // This is needed because parent_structure differs from member_of in virtual function overrides if let TypeKind::UserDefined(key) = param.kind.kind { if let UserDefinedType::Structure(structure) = get_udt_by_key(info, key)? { - parent_structure = Some(structure); + direct_base = Some(structure); + direct_base_key = Some(key); } } } @@ -2464,7 +2516,10 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result let return_type = return_type .unwrap_or_else(|| Type { kind: TypeKind::Fundamental(FundType::Void), modifiers: vec![] }); + let direct_member_of = direct_base_key; let local = tag.kind == TagKind::Subroutine; + let static_member = member_of.is_some() && direct_base.is_none(); + let override_ = virtual_ && member_of != direct_base_key; let subroutine = SubroutineType { name, mangled_name, @@ -2474,6 +2529,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result prototyped, references, member_of, + direct_member_of, variables, inline, virtual_, @@ -2482,13 +2538,15 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result blocks, inlines, inner_types, + typedefs, start_address, end_address, - is_direct_member: parent_structure.is_some(), - is_const, - typedefs + const_, + static_member, + override_, + volatile_ }; - // TODO add a reference to this to parent_structure + // TODO save this to a key => SubroutineType map and add a reference to the key in parent_structure Ok(subroutine) } From 02ea35e8789503ce047f34e30be9e673db2ed77a Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 04:26:08 +0100 Subject: [PATCH 06/24] Omit return type of ctors and dtors --- src/util/dwarf.rs | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index c23fedd..3b03556 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1389,7 +1389,6 @@ pub fn subroutine_def_string( } } - let rt = type_string(info, typedefs, &t.return_type, true)?; if t.local || t.static_member { out.push_str("static "); } @@ -1399,10 +1398,11 @@ pub fn subroutine_def_string( if t.virtual_ && !t.override_ { out.push_str("virtual "); } - out.push_str(&rt.prefix); - out.push(' '); let mut name_written = false; + + let mut omit_return_type = false; + let mut full_written_name = String::new(); if t.override_ { // GCC behavior @@ -1415,27 +1415,47 @@ pub fn subroutine_def_string( .string_attribute(AttributeKind::Name) .ok_or_else(|| anyhow!("direct_member_of tag {} has no name attribute", direct_member_of))?; - write!(out, "{direct_base_name}::")?; + write!(full_written_name, "{direct_base_name}::")?; + + if let Some(name) = t.name.as_ref() { + // in GCC the ctor and dtor are called the same, so we need to check the return value + if name == direct_base_name { + if let TypeKind::Fundamental(FundType::Void) = t.return_type.kind { + write!(full_written_name, "~{direct_base_name}")?; + name_written = true; + } + omit_return_type = true; + } + } } } else if let Some(base_name) = base_name_opt { - write!(out, "{base_name}::")?; + write!(full_written_name, "{base_name}::")?; // Handle MWCC constructors and destructors if let Some(name) = t.name.as_ref() { if name == "__dt" { - write!(out, "~{base_name}")?; + write!(full_written_name, "~{base_name}")?; name_written = true; } else if name == "__ct" { - write!(out, "{base_name}")?; + write!(full_written_name, "{base_name}")?; name_written = true; } + omit_return_type = true; } } if !name_written { if let Some(name) = t.name.as_ref() { - out.push_str(name); + full_written_name.push_str(name); } } + let rt = type_string(info, typedefs, &t.return_type, true)?; + if !omit_return_type { + out.push_str(&rt.prefix); + out.push(' '); + } + + out.push_str(&full_written_name); + let mut parameters = String::new(); if t.parameters.is_empty() { if t.var_args { From 177e41d8d1a82888c855ac60e583410b821d1db8 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 04:35:42 +0100 Subject: [PATCH 07/24] Add back the nested template thing because it's actually useful --- src/util/dwarf.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 3b03556..75068cd 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1787,27 +1787,10 @@ pub fn struct_def_string( } } out.push_str(" {\n"); - let mut emitted_inner_types = false; for inner_type in &t.inner_types { - // GCC emits template base classes as a nested struct, which is redundant so we skip them - if let UserDefinedType::Structure(structure) = inner_type { - if let Some(struct_name) = structure.name.as_ref() { - if struct_name.contains("<") && t.bases.iter().any(|base| { - let resolved_name_opt: Option = match &base.name { - Some(name) => Some(name.clone()), - None => type_name(info, typedefs, &base.base_type).ok(), - }; - - structure.name == resolved_name_opt - }) { - continue; - } - } - } - emitted_inner_types = true; writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; } - if emitted_inner_types { + if !t.inner_types.is_empty() { out.push_str("\n"); } From 7c038f7161222efcf999925cf52bd3bf93f15c2d Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 05:04:54 +0100 Subject: [PATCH 08/24] Return type omitting bug fix --- src/util/dwarf.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 75068cd..843223d 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1436,11 +1436,12 @@ pub fn subroutine_def_string( if name == "__dt" { write!(full_written_name, "~{base_name}")?; name_written = true; + omit_return_type = true; } else if name == "__ct" { write!(full_written_name, "{base_name}")?; name_written = true; + omit_return_type = true; } - omit_return_type = true; } } if !name_written { From dfcc295f9c6140aae8ea657aeb95e198feda245a Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 17:36:01 +0100 Subject: [PATCH 09/24] Add static struct members --- src/util/dwarf.rs | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 843223d..5ba9b64 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -739,6 +739,7 @@ pub struct StructureType { pub name: Option, pub byte_size: Option, pub members: Vec, + pub static_members: Vec, pub bases: Vec, pub inner_types: Vec, pub typedefs: Vec, @@ -1165,7 +1166,7 @@ fn structure_type_string( ) -> Result { let prefix = if let Some(name) = t.name.as_ref() { if name.starts_with('@') { - struct_def_string(info, typedefs, t)? + structure_def_string(info, typedefs, t)? } else if include_keyword { match t.kind { StructureKind::Struct => format!("struct {name}"), @@ -1175,7 +1176,7 @@ fn structure_type_string( name.clone() } } else if include_anonymous_def { - struct_def_string(info, typedefs, t)? + structure_def_string(info, typedefs, t)? } else if include_keyword { match t.kind { StructureKind::Struct => "struct [anonymous]".to_string(), @@ -1294,7 +1295,7 @@ pub fn ud_type_def( Ok(format!("// Array: {}{}", ts.prefix, ts.suffix)) } UserDefinedType::Subroutine(t) => Ok(subroutine_def_string(info, typedefs, t, is_erased)?), - UserDefinedType::Structure(t) => Ok(struct_def_string(info, typedefs, t)?), + UserDefinedType::Structure(t) => Ok(structure_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) => { @@ -1744,7 +1745,7 @@ fn get_anon_union_groups(members: &[StructureMember], unions: &[AnonUnion]) -> V groups } -pub fn struct_def_string( +pub fn structure_def_string( info: &DwarfInfo, typedefs: &TypedefMap, t: &StructureType, @@ -1788,19 +1789,31 @@ pub fn struct_def_string( } } out.push_str(" {\n"); - for inner_type in &t.inner_types { - writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; - } if !t.inner_types.is_empty() { + for inner_type in &t.inner_types { + writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; + } out.push_str("\n"); } - for typedef in &t.typedefs { - writeln!(out, "{}", &indent_all_by(4, &typedef_string(info, typedefs, typedef)?))?; - } if !t.typedefs.is_empty() { + for typedef in &t.typedefs { + writeln!(out, "{}", &indent_all_by(4, &typedef_string(info, typedefs, typedef)?))?; + } out.push_str("\n"); } + + if !t.static_members.is_empty() { + for static_member in &t.static_members { + let line = format!( + "static {}", + variable_string(info, typedefs, static_member, true)? + ); + writeln!(out, "{}", indent_all_by(4, &line))?; + } + out.push_str("\n"); + } + let mut vis = match t.kind { StructureKind::Struct => Visibility::Public, StructureKind::Class => Visibility::Private, @@ -2112,6 +2125,7 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { } let mut members = Vec::new(); + let mut static_members = Vec::new(); let mut bases = Vec::new(); let mut inner_types = Vec::new(); let mut typedefs = Vec::new(); @@ -2134,7 +2148,8 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { // TODO } TagKind::GlobalVariable => { - // TODO + // TODO handle visibility + static_members.push(process_variable_tag(info, child)?) } TagKind::StructureType | TagKind::ClassType => { inner_types.push(UserDefinedType::Structure(process_structure_tag(info, child)?)) @@ -2160,6 +2175,7 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { name, byte_size, members, + static_members, bases, inner_types, typedefs, From 102e0f5a0be91e33debd6b3c317f977bc89fe845 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 17:51:56 +0100 Subject: [PATCH 10/24] Fix formatting --- src/util/dwarf.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 5ba9b64..79d9050 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1788,22 +1788,23 @@ pub fn structure_def_string( out.push_str(&type_name(info, typedefs, &base.base_type)?); } } - out.push_str(" {\n"); + out.push_str(" {"); if !t.inner_types.is_empty() { + writeln!(out, "\n // Inner declarations")?; for inner_type in &t.inner_types { writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; } - out.push_str("\n"); } if !t.typedefs.is_empty() { + writeln!(out, "\n // Typedefs")?; for typedef in &t.typedefs { writeln!(out, "{}", &indent_all_by(4, &typedef_string(info, typedefs, typedef)?))?; } - out.push_str("\n"); } if !t.static_members.is_empty() { + writeln!(out, "\n // Static members")?; for static_member in &t.static_members { let line = format!( "static {}", @@ -1811,7 +1812,6 @@ pub fn structure_def_string( ); writeln!(out, "{}", indent_all_by(4, &line))?; } - out.push_str("\n"); } let mut vis = match t.kind { @@ -1823,6 +1823,9 @@ pub fn structure_def_string( let groups = get_anon_union_groups(&t.members, &unions); let mut in_union = 0; let mut in_group = 0; + if !t.members.is_empty() { + writeln!(out, "\n // Members")?; + } for (i, member) in t.members.iter().enumerate() { if vis != member.visibility { vis = member.visibility; From a4ce8ed2c86caeeda11c125a99d2bce0b273853b Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:54:22 +0100 Subject: [PATCH 11/24] Handle typedef specification attribute --- src/util/dwarf.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 79d9050..4f246aa 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -3037,8 +3037,15 @@ fn process_typedef_tag(info: &DwarfInfo, tag: &Tag) -> Result { (AttributeKind::Member, _) => { // can be ignored for now }, - (AttributeKind::Specification, _) => { - // TODO + (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_typedef_tag(info, spec_tag)?; + name = name.or(Some(spec.name)); + kind = kind.or(Some(spec.kind)); } _ => { bail!("Unhandled Typedef attribute {:?}", attr); From daa38c5dfd8e2979e50ee0a02f99bf675dbd5356 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:16:21 +0100 Subject: [PATCH 12/24] Avoid adding duplicate inline params --- src/util/dwarf.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 4f246aa..c6f84ee 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -2505,7 +2505,10 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result } } } - parameters.push(param); + // Avoid applying ones that were already in the specification + if !parameters.iter().any(|p| p.name == param.name) { + parameters.push(param); + } } TagKind::UnspecifiedParameters => var_args = true, TagKind::LocalVariable => variables.push(process_local_variable_tag(info, child)?), From ae9b5460b612ee7a621dc66448a520c731d8fdf5 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:56:57 +0100 Subject: [PATCH 13/24] Handle non-overriden GCC constructors and destructors --- src/util/dwarf.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index c6f84ee..7238142 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1432,7 +1432,7 @@ pub fn subroutine_def_string( } else if let Some(base_name) = base_name_opt { write!(full_written_name, "{base_name}::")?; - // Handle MWCC constructors and destructors + // Handle constructors and destructors if let Some(name) = t.name.as_ref() { if name == "__dt" { write!(full_written_name, "~{base_name}")?; @@ -1442,6 +1442,12 @@ pub fn subroutine_def_string( write!(full_written_name, "{base_name}")?; name_written = true; omit_return_type = true; + } else if name == base_name { + if let TypeKind::Fundamental(FundType::Void) = t.return_type.kind { + write!(full_written_name, "~{base_name}")?; + name_written = true; + } + omit_return_type = true; } } } From b67cd7f8dcdef91d74bcbd9a7a94db39adde9404 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 21:27:58 +0100 Subject: [PATCH 14/24] clippy --- src/util/dwarf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 7238142..c399105 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1383,7 +1383,7 @@ pub fn subroutine_def_string( let is_non_static_member = t.direct_member_of.is_some() && !t.static_member; if is_non_static_member { - if let Some(param) = t.parameters.get(0) { + if let Some(param) = t.parameters.first() { if let Some(location) = ¶m.location { writeln!(out, "// this: {}", location)?; } From c443de77f8577a5fc984fced8781fd25266eecbb Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 21:34:23 +0100 Subject: [PATCH 15/24] Cargo fmt --- src/util/dwarf.rs | 91 ++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index c399105..5329d35 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1374,13 +1374,13 @@ pub fn subroutine_def_string( let base_name = tag .string_attribute(AttributeKind::Name) .ok_or_else(|| anyhow!("member_of tag {} has no name attribute", member_of))?; - + if t.override_ { writeln!(out, "// Overrides: {}", base_name)?; } - base_name_opt = Some(base_name); + base_name_opt = Some(base_name); } - + let is_non_static_member = t.direct_member_of.is_some() && !t.static_member; if is_non_static_member { if let Some(param) = t.parameters.first() { @@ -1401,21 +1401,20 @@ pub fn subroutine_def_string( } let mut name_written = false; - + let mut omit_return_type = false; let mut full_written_name = String::new(); if t.override_ { // GCC behavior if let Some(direct_member_of) = t.direct_member_of { - let tag = info - .tags - .get(&direct_member_of) - .ok_or_else(|| anyhow!("Failed to locate direct_member_of tag {}", direct_member_of))?; - let direct_base_name = tag - .string_attribute(AttributeKind::Name) - .ok_or_else(|| anyhow!("direct_member_of tag {} has no name attribute", direct_member_of))?; - + let tag = info.tags.get(&direct_member_of).ok_or_else(|| { + anyhow!("Failed to locate direct_member_of tag {}", direct_member_of) + })?; + let direct_base_name = tag.string_attribute(AttributeKind::Name).ok_or_else(|| { + anyhow!("direct_member_of tag {} has no name attribute", direct_member_of) + })?; + write!(full_written_name, "{direct_base_name}::")?; if let Some(name) = t.name.as_ref() { @@ -1507,7 +1506,11 @@ pub fn subroutine_def_string( writeln!(out, "\n // Inner declarations")?; for inner_type in &t.inner_types { - writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; + writeln!( + out, + "{};", + &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?) + )?; } } @@ -1617,7 +1620,11 @@ fn subroutine_block_string( writeln!(out, "\n // Inner declarations")?; for inner_type in &block.inner_types { - writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; + writeln!( + out, + "{};", + &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?) + )?; } } @@ -1798,7 +1805,11 @@ pub fn structure_def_string( if !t.inner_types.is_empty() { writeln!(out, "\n // Inner declarations")?; for inner_type in &t.inner_types { - writeln!(out, "{};", &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?))?; + writeln!( + out, + "{};", + &indent_all_by(4, &ud_type_def(info, typedefs, inner_type, false)?) + )?; } } @@ -1812,10 +1823,7 @@ pub fn structure_def_string( if !t.static_members.is_empty() { writeln!(out, "\n // Static members")?; for static_member in &t.static_members { - let line = format!( - "static {}", - variable_string(info, typedefs, static_member, true)? - ); + let line = format!("static {}", variable_string(info, typedefs, static_member, true)?); writeln!(out, "{}", indent_all_by(4, &line))?; } } @@ -2146,13 +2154,12 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { let td = process_typedef_tag(info, child)?; // GCC generates a typedef in templated structs with the name of the template // Let's filter it out to not confuse the user - let is_template = name - .as_deref() - .is_some_and(|n| n.starts_with(&format!("{}<", td.name))); + let is_template = + name.as_deref().is_some_and(|n| n.starts_with(&format!("{}<", td.name))); if !is_template { typedefs.push(td); } - }, + } TagKind::Subroutine | TagKind::GlobalSubroutine => { // TODO } @@ -2494,10 +2501,16 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result match child.kind { TagKind::FormalParameter => { let param = process_subroutine_parameter_tag(info, child)?; - if member_of.is_some() && direct_base.is_none() && param.name.as_deref() == Some("this") { + if member_of.is_some() + && direct_base.is_none() + && param.name.as_deref() == Some("this") + { let modifiers = ¶m.kind.modifiers; // TODO is this a proper check? - if modifiers.len() >= 3 && modifiers[0] == Modifier::Const && modifiers[2] == Modifier::Const { + if modifiers.len() >= 3 + && modifiers[0] == Modifier::Const + && modifiers[2] == Modifier::Const + { const_ = true; } if modifiers.contains(&Modifier::Volatile) { @@ -2537,9 +2550,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result inner_types.push(UserDefinedType::Union(process_union_tag(info, child)?)) } TagKind::Typedef => typedefs.push(process_typedef_tag(info, child)?), - TagKind::ArrayType - | TagKind::SubroutineType - | TagKind::PtrToMemberType => { + TagKind::ArrayType | TagKind::SubroutineType | TagKind::PtrToMemberType => { // Variable type, ignore } kind => bail!("Unhandled SubroutineType child {:?}", kind), @@ -2576,7 +2587,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result const_, static_member, override_, - volatile_ + volatile_, }; // TODO save this to a key => SubroutineType map and add a reference to the key in parent_structure Ok(subroutine) @@ -2644,7 +2655,8 @@ fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result { - inner_types.push(UserDefinedType::Enumeration(process_enumeration_tag(info, child)?)); + inner_types + .push(UserDefinedType::Enumeration(process_enumeration_tag(info, child)?)); } TagKind::UnionType => { inner_types.push(UserDefinedType::Union(process_union_tag(info, child)?)) @@ -2652,16 +2664,23 @@ fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result { typedefs.push(process_typedef_tag(info, child)?); } - | TagKind::ArrayType - | TagKind::SubroutineType - | TagKind::PtrToMemberType => { + TagKind::ArrayType | TagKind::SubroutineType | TagKind::PtrToMemberType => { // Variable type, ignore } kind => bail!("Unhandled LexicalBlock child {:?}", kind), } } - Ok(Some(SubroutineBlock { name, start_address, end_address, variables, blocks, inlines, inner_types, typedefs })) + Ok(Some(SubroutineBlock { + name, + start_address, + end_address, + variables, + blocks, + inlines, + inner_types, + typedefs, + })) } fn process_subroutine_parameter_tag(info: &DwarfInfo, tag: &Tag) -> Result { @@ -3044,8 +3063,8 @@ fn process_typedef_tag(info: &DwarfInfo, tag: &Tag) -> Result { _, ) => kind = Some(process_type(attr, info.e)?), (AttributeKind::Member, _) => { - // can be ignored for now - }, + // can be ignored for now + } (AttributeKind::Specification, &AttributeValue::Reference(key)) => { let spec_tag = info .tags From 1ccc792f875f7514b6425d715dbf7f9f804e00a0 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Thu, 18 Dec 2025 21:39:43 +0100 Subject: [PATCH 16/24] Apply clippy's suggestion --- src/util/dwarf.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 5329d35..7a1f374 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -877,7 +877,7 @@ pub struct TypedefTag { pub enum TagType { Variable(VariableTag), Typedef(TypedefTag), - UserDefined(UserDefinedType), + UserDefined(Box), } #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] @@ -2983,7 +2983,7 @@ pub fn process_cu_tag(info: &DwarfInfo, tag: &Tag) -> Result { | TagKind::SubroutineType | TagKind::GlobalSubroutine | TagKind::Subroutine - | TagKind::PtrToMemberType => Ok(TagType::UserDefined(ud_type(info, tag)?)), + | TagKind::PtrToMemberType => Ok(TagType::UserDefined(Box::new(ud_type(info, tag)?))), kind => Err(anyhow!("Unhandled root tag type {:?}", kind)), } } @@ -3008,7 +3008,7 @@ pub fn tag_type_string( TagType::Variable(v) => variable_string(info, typedefs, v, true), TagType::UserDefined(ud) => { let ud_str = ud_type_def(info, typedefs, ud, is_erased)?; - match ud { + match **ud { UserDefinedType::Structure(_) | UserDefinedType::Enumeration(_) | UserDefinedType::Union(_) => Ok(format!("{ud_str};")), From 0634be05263267ebb1db2c6eb67197d2b8a851a5 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Fri, 19 Dec 2025 00:23:12 +0100 Subject: [PATCH 17/24] Extract producer and correctly handle MWCC static members --- src/cmd/dwarf.rs | 8 ++-- src/util/dwarf.rs | 97 +++++++++++++++++++++++++++++++---------------- 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/src/cmd/dwarf.rs b/src/cmd/dwarf.rs index 7db9773..b4f3a0a 100644 --- a/src/cmd/dwarf.rs +++ b/src/cmd/dwarf.rs @@ -19,8 +19,8 @@ use typed_path::Utf8NativePathBuf; use crate::{ util::{ dwarf::{ - process_compile_unit, process_cu_tag, process_overlay_branch, read_debug_section, - should_skip_tag, tag_type_string, AttributeKind, TagKind, + parse_producer, process_compile_unit, process_cu_tag, process_overlay_branch, + read_debug_section, should_skip_tag, tag_type_string, AttributeKind, TagKind, }, file::buf_writer, path::native_path, @@ -166,7 +166,8 @@ where } let mut reader = Cursor::new(&*data); - let info = read_debug_section(&mut reader, obj_file.endianness().into(), args.include_erased)?; + let mut info = + read_debug_section(&mut reader, obj_file.endianness().into(), args.include_erased)?; for (&addr, tag) in &info.tags { log::debug!("{}: {:?}", addr, tag); @@ -210,6 +211,7 @@ where writeln!(w, "\n/*\n Compile unit: {}", unit.name)?; if let Some(producer) = unit.producer { writeln!(w, " Producer: {producer}")?; + info.producer = parse_producer(&producer); } if let Some(comp_dir) = unit.comp_dir { writeln!(w, " Compile directory: {comp_dir}")?; diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 7a1f374..0a26c36 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -369,6 +369,14 @@ pub type TypedefMap = BTreeMap>; pub struct DwarfInfo { pub e: Endian, pub tags: TagMap, + pub producer: Producer, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Producer { + MWCC, + GCC, + OTHER, } impl Tag { @@ -488,7 +496,7 @@ where R: BufRead + Seek + ?Sized { len }; - let mut info = DwarfInfo { e, tags: BTreeMap::new() }; + let mut info = DwarfInfo { e, tags: BTreeMap::new(), producer: Producer::OTHER }; loop { let position = reader.stream_position()?; if position >= len { @@ -502,6 +510,14 @@ where R: BufRead + Seek + ?Sized { Ok(info) } +pub fn parse_producer(producer: &str) -> Producer { + match producer { + p if p.starts_with("MW") => Producer::MWCC, + p if p.starts_with("GNU C") => Producer::GCC, + _ => Producer::OTHER, + } +} + #[allow(unused)] pub fn read_aranges_section(reader: &mut R, e: Endian) -> Result<()> where R: BufRead + Seek + ?Sized { @@ -1405,26 +1421,28 @@ pub fn subroutine_def_string( let mut omit_return_type = false; let mut full_written_name = String::new(); - if t.override_ { - // GCC behavior - if let Some(direct_member_of) = t.direct_member_of { - let tag = info.tags.get(&direct_member_of).ok_or_else(|| { - anyhow!("Failed to locate direct_member_of tag {}", direct_member_of) - })?; - let direct_base_name = tag.string_attribute(AttributeKind::Name).ok_or_else(|| { - anyhow!("direct_member_of tag {} has no name attribute", direct_member_of) - })?; - - write!(full_written_name, "{direct_base_name}::")?; - - if let Some(name) = t.name.as_ref() { - // in GCC the ctor and dtor are called the same, so we need to check the return value - if name == direct_base_name { - if let TypeKind::Fundamental(FundType::Void) = t.return_type.kind { - write!(full_written_name, "~{direct_base_name}")?; - name_written = true; + if let Producer::GCC = info.producer { + if t.override_ { + if let Some(direct_member_of) = t.direct_member_of { + let tag = info.tags.get(&direct_member_of).ok_or_else(|| { + anyhow!("Failed to locate direct_member_of tag {}", direct_member_of) + })?; + let direct_base_name = + tag.string_attribute(AttributeKind::Name).ok_or_else(|| { + anyhow!("direct_member_of tag {} has no name attribute", direct_member_of) + })?; + + write!(full_written_name, "{direct_base_name}::")?; + + if let Some(name) = t.name.as_ref() { + // in GCC the ctor and dtor are called the same, so we need to check the return value + if name == direct_base_name { + if let TypeKind::Fundamental(FundType::Void) = t.return_type.kind { + write!(full_written_name, "~{direct_base_name}")?; + name_written = true; + } + omit_return_type = true; } - omit_return_type = true; } } } @@ -2151,13 +2169,26 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result { TagKind::Inheritance => bases.push(process_inheritance_tag(info, child)?), TagKind::Member => members.push(process_structure_member_tag(info, child)?), TagKind::Typedef => { - let td = process_typedef_tag(info, child)?; - // GCC generates a typedef in templated structs with the name of the template - // Let's filter it out to not confuse the user - let is_template = - name.as_deref().is_some_and(|n| n.starts_with(&format!("{}<", td.name))); - if !is_template { - typedefs.push(td); + match info.producer { + Producer::MWCC => { + // TODO handle visibility + // MWCC handles static members as typedefs for whatever reason + static_members.push(process_variable_tag(info, child)?) + } + Producer::GCC => { + // GCC generates a typedef in templated structs with the name of the template + // Let's filter it out to not confuse the user + let td = process_typedef_tag(info, child)?; + let is_template = name + .as_deref() + .is_some_and(|n| n.starts_with(&format!("{}<", td.name))); + if !is_template { + typedefs.push(td); + } + } + _ => { + typedefs.push(process_typedef_tag(info, child)?); + } } } TagKind::Subroutine | TagKind::GlobalSubroutine => { @@ -3091,11 +3122,13 @@ fn process_typedef_tag(info: &DwarfInfo, tag: &Tag) -> Result { } 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 is_variable = match tag.kind { + TagKind::GlobalVariable | TagKind::LocalVariable => true, + TagKind::Typedef if info.producer == Producer::MWCC => true, + _ => false, + }; + + ensure!(is_variable, "{:?} is not a variable tag", tag.kind); let mut name = None; let mut mangled_name = None; From 009ae07e4a50835fee9c40d7116b0985dd08a867 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Fri, 19 Dec 2025 00:46:42 +0100 Subject: [PATCH 18/24] Enable static member function detection only for GCC --- src/util/dwarf.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 0a26c36..c522f74 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -2592,7 +2592,12 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result .unwrap_or_else(|| Type { kind: TypeKind::Fundamental(FundType::Void), modifiers: vec![] }); let direct_member_of = direct_base_key; let local = tag.kind == TagKind::Subroutine; - let static_member = member_of.is_some() && direct_base.is_none(); + + let mut static_member = false; + if let Producer::GCC = info.producer { + // GCC doesn't retain namespaces, so this is a nice way to determine staticness + static_member = member_of.is_some() && direct_base.is_none(); + } let override_ = virtual_ && member_of != direct_base_key; let subroutine = SubroutineType { name, From 033a39bb57181da7b891a6fb37c52d952f931089 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Fri, 19 Dec 2025 01:22:05 +0100 Subject: [PATCH 19/24] Fix bug where function parameter wasn't applied if both names were None --- src/util/dwarf.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index c522f74..053d227 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -2556,7 +2556,12 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result } } // Avoid applying ones that were already in the specification - if !parameters.iter().any(|p| p.name == param.name) { + if !parameters.iter().any(|p| { + matches!( + (p.name.as_ref(), param.name.as_ref()), + (Some(a), Some(b)) if a == b + ) + }) { parameters.push(param); } } From d005426589c4babf1df1ed25c74642ca2e081905 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Fri, 19 Dec 2025 02:08:47 +0100 Subject: [PATCH 20/24] Fix gcc boolean logic and omit __in_chrg --- src/util/dwarf.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 053d227..3c43d01 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1419,10 +1419,11 @@ pub fn subroutine_def_string( let mut name_written = false; let mut omit_return_type = false; + let mut is_gcc_destructor = false; let mut full_written_name = String::new(); - if let Producer::GCC = info.producer { - if t.override_ { + if t.override_ { + if let Producer::GCC = info.producer { if let Some(direct_member_of) = t.direct_member_of { let tag = info.tags.get(&direct_member_of).ok_or_else(|| { anyhow!("Failed to locate direct_member_of tag {}", direct_member_of) @@ -1432,13 +1433,16 @@ pub fn subroutine_def_string( anyhow!("direct_member_of tag {} has no name attribute", direct_member_of) })?; + // we need to emit the real parent write!(full_written_name, "{direct_base_name}::")?; if let Some(name) = t.name.as_ref() { // in GCC the ctor and dtor are called the same, so we need to check the return value + // this is only for the dtor, the ctor can be left as is if name == direct_base_name { if let TypeKind::Fundamental(FundType::Void) = t.return_type.kind { write!(full_written_name, "~{direct_base_name}")?; + is_gcc_destructor = true; name_written = true; } omit_return_type = true; @@ -1462,6 +1466,9 @@ pub fn subroutine_def_string( } else if name == base_name { if let TypeKind::Fundamental(FundType::Void) = t.return_type.kind { write!(full_written_name, "~{base_name}")?; + if let Producer::GCC = info.producer { + is_gcc_destructor = true; + } name_written = true; } omit_return_type = true; @@ -1489,7 +1496,11 @@ pub fn subroutine_def_string( parameters = "void".to_string(); } } else { - let start_index = if is_non_static_member { 1 } else { 0 }; + let mut start_index = if is_non_static_member { 1 } else { 0 }; + // omit __in_chrg parameter + if is_gcc_destructor { + start_index += 1; + } for (idx, parameter) in t.parameters.iter().enumerate().skip(start_index) { if idx > start_index { write!(parameters, ", ")?; From bca6fde9257fd7ec7a7e9a0f7c3773df2cf31115 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Fri, 19 Dec 2025 02:54:45 +0100 Subject: [PATCH 21/24] Add parent fallback for PS2 MW --- src/util/dwarf.rs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 3c43d01..bcacbe1 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -1381,6 +1381,7 @@ pub fn subroutine_def_string( } let mut base_name_opt = None; + let mut direct_base_name_opt = None; if let Some(member_of) = t.member_of { let tag = info @@ -1397,6 +1398,22 @@ pub fn subroutine_def_string( base_name_opt = Some(base_name); } + if let Some(direct_member_of) = t.direct_member_of { + let tag = info + .tags + .get(&direct_member_of) + .ok_or_else(|| anyhow!("Failed to locate direct_member_of tag {}", direct_member_of))?; + let direct_base_name = tag + .string_attribute(AttributeKind::Name) + .ok_or_else(|| anyhow!("direct_member_of tag {} has no name attribute", direct_member_of))?; + + direct_base_name_opt = Some(direct_base_name); + if base_name_opt.is_none() { + // Fall back to the parsed out direct_base_name on PS2 MW because it doesn't emit a base class + base_name_opt = direct_base_name_opt; + } + } + let is_non_static_member = t.direct_member_of.is_some() && !t.static_member; if is_non_static_member { if let Some(param) = t.parameters.first() { @@ -1424,16 +1441,8 @@ pub fn subroutine_def_string( if t.override_ { if let Producer::GCC = info.producer { - if let Some(direct_member_of) = t.direct_member_of { - let tag = info.tags.get(&direct_member_of).ok_or_else(|| { - anyhow!("Failed to locate direct_member_of tag {}", direct_member_of) - })?; - let direct_base_name = - tag.string_attribute(AttributeKind::Name).ok_or_else(|| { - anyhow!("direct_member_of tag {} has no name attribute", direct_member_of) - })?; - - // we need to emit the real parent + if let Some(direct_base_name) = direct_base_name_opt { + // we need to emit the real parent on GCC write!(full_written_name, "{direct_base_name}::")?; if let Some(name) = t.name.as_ref() { @@ -2543,8 +2552,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result match child.kind { TagKind::FormalParameter => { let param = process_subroutine_parameter_tag(info, child)?; - if member_of.is_some() - && direct_base.is_none() + if direct_base.is_none() && param.name.as_deref() == Some("this") { let modifiers = ¶m.kind.modifiers; From 175eb9ecb3173458804065051009da7e91c11573 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Fri, 19 Dec 2025 04:26:34 +0100 Subject: [PATCH 22/24] Demangle mangled function names --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/util/dwarf.rs | 34 +++++++++++++++++++++++++++------- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0ccc76..3728ce7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -362,6 +362,7 @@ dependencies = [ "fixedbitset", "flagset", "glob", + "gnuv2_demangle", "hex", "indent", "indexmap", @@ -586,6 +587,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "gnuv2_demangle" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4eda49d36ceaaaf3af38940a636b0514472cdba8b2158b20b58e91e50df4c7" + [[package]] name = "hashbrown" version = "0.15.5" diff --git a/Cargo.toml b/Cargo.toml index c3ab88b..b2e9829 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ filetime = "0.2" fixedbitset = "0.5" flagset = { version = "0.4", features = ["serde"] } glob = "0.3" +gnuv2_demangle = "0.4" hex = "0.4" indent = "0.1" indexmap = "2.6" diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index bcacbe1..4b229f1 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -7,6 +7,8 @@ use std::{ }; use anyhow::{anyhow, bail, ensure, Context, Result}; +use cwdemangle::demangle as cw_demangle; +use gnuv2_demangle::{demangle as gnu_demangle, DemangleConfig}; use indent::indent_all_by; use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError}; @@ -1403,9 +1405,9 @@ pub fn subroutine_def_string( .tags .get(&direct_member_of) .ok_or_else(|| anyhow!("Failed to locate direct_member_of tag {}", direct_member_of))?; - let direct_base_name = tag - .string_attribute(AttributeKind::Name) - .ok_or_else(|| anyhow!("direct_member_of tag {} has no name attribute", direct_member_of))?; + let direct_base_name = tag.string_attribute(AttributeKind::Name).ok_or_else(|| { + anyhow!("direct_member_of tag {} has no name attribute", direct_member_of) + })?; direct_base_name_opt = Some(direct_base_name); if base_name_opt.is_none() { @@ -1486,7 +1488,7 @@ pub fn subroutine_def_string( } if !name_written { if let Some(name) = t.name.as_ref() { - full_written_name.push_str(name); + full_written_name.push_str(&maybe_demangle_name(info, name)); } } let rt = type_string(info, typedefs, &t.return_type, true)?; @@ -2552,9 +2554,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result match child.kind { TagKind::FormalParameter => { let param = process_subroutine_parameter_tag(info, child)?; - if direct_base.is_none() - && param.name.as_deref() == Some("this") - { + if direct_base.is_none() && param.name.as_deref() == Some("this") { let modifiers = ¶m.kind.modifiers; // TODO is this a proper check? if modifiers.len() >= 3 @@ -3195,3 +3195,23 @@ fn process_variable_tag(info: &DwarfInfo, tag: &Tag) -> Result { let local = tag.kind == TagKind::LocalVariable; Ok(VariableTag { name, mangled_name, kind, address, local }) } + +// TODO expand for more compilers? +fn maybe_demangle_name(info: &DwarfInfo, name: &str) -> String { + let fake_name = format!("{}__0", name); + let name_opt = match info.producer { + // for __pl this looks like operator+ + Producer::MWCC => cw_demangle(&fake_name, &Default::default()), + // for __pl this looks like ::operator+(void) + Producer::GCC => { + gnu_demangle(&fake_name, &DemangleConfig::new()).ok().and_then(|demangled| { + demangled + .split_once("::") + .and_then(|(_, rest)| rest.split_once("(void)")) + .map(|(op, _)| op.to_string()) + }) + } + Producer::OTHER => None, + }; + name_opt.unwrap_or_else(|| name.to_string()) +} From 0f5acabc1f50ceec50e1ed831d1df9a10c6335d6 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:43:12 +0100 Subject: [PATCH 23/24] Have inlines and blocks in the same vec to keep their correct order --- src/util/dwarf.rs | 76 +++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 4b229f1..911ff8e 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -826,12 +826,17 @@ pub struct SubroutineBlock { pub start_address: Option, pub end_address: Option, pub variables: Vec, - pub blocks: Vec, - pub inlines: Vec, + pub blocks_and_inlines: Vec, pub inner_types: Vec, pub typedefs: Vec, } +#[derive(Debug, Clone)] +pub enum SubroutineNode { + Block(SubroutineBlock), + Inline(SubroutineType), +} + #[derive(Debug, Clone)] pub struct SubroutineType { pub name: Option, @@ -848,8 +853,7 @@ pub struct SubroutineType { pub virtual_: bool, pub local: bool, pub labels: Vec, - pub blocks: Vec, - pub inlines: Vec, + pub blocks_and_inlines: Vec, pub inner_types: Vec, pub typedefs: Vec, pub start_address: Option, @@ -1604,19 +1608,16 @@ pub fn subroutine_def_string( } } - 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 inline_str = subroutine_def_string(info, typedefs, inline, is_erased)?; - out.push_str(&indent_all_by(4, inline_str)); + if !t.blocks_and_inlines.is_empty() { + for node in &t.blocks_and_inlines { + let node_str = match node { + SubroutineNode::Block(block) => subroutine_block_string(info, typedefs, block)?, + SubroutineNode::Inline(inline) => { + writeln!(out, "\n // Inline")?; + subroutine_def_string(info, typedefs, inline, is_erased)? + } + }; + out.push_str(&indent_all_by(4, node_str)); } } @@ -1675,17 +1676,18 @@ fn subroutine_block_string( } } - if !block.inlines.is_empty() { - writeln!(out, "\n // Inlines")?; - for inline in &block.inlines { - let inline_str = subroutine_def_string(info, typedefs, inline, false)?; - out.push_str(&indent_all_by(4, inline_str)); + if !block.blocks_and_inlines.is_empty() { + for node in &block.blocks_and_inlines { + let node_str = match node { + SubroutineNode::Block(block) => subroutine_block_string(info, typedefs, block)?, + SubroutineNode::Inline(inline) => { + writeln!(out, "\n // Inline")?; + subroutine_def_string(info, typedefs, inline, false)? + } + }; + out.push_str(&indent_all_by(4, node_str)); } } - 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) } @@ -2546,8 +2548,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result let mut variables = Vec::new(); let mut labels = Vec::new(); - let mut blocks = Vec::new(); - let mut inlines = Vec::new(); + let mut blocks_and_inlines = Vec::new(); let mut inner_types = Vec::new(); let mut typedefs = Vec::new(); for child in tag.children(&info.tags) { @@ -2592,10 +2593,11 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result TagKind::Label => labels.push(process_subroutine_label_tag(info, child)?), TagKind::LexicalBlock => { if let Some(block) = process_subroutine_block_tag(info, child)? { - blocks.push(block); + blocks_and_inlines.push(SubroutineNode::Block(block)); } } - TagKind::InlinedSubroutine => inlines.push(process_subroutine_tag(info, child)?), + TagKind::InlinedSubroutine => blocks_and_inlines + .push(SubroutineNode::Inline(process_subroutine_tag(info, child)?)), TagKind::StructureType | TagKind::ClassType => { inner_types.push(UserDefinedType::Structure(process_structure_tag(info, child)?)) } @@ -2638,8 +2640,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result virtual_, local, labels, - blocks, - inlines, + blocks_and_inlines, inner_types, typedefs, start_address, @@ -2693,8 +2694,7 @@ fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result Result { if let Some(block) = process_subroutine_block_tag(info, child)? { - blocks.push(block); + blocks_and_inlines.push(SubroutineNode::Block(block)); } } TagKind::InlinedSubroutine => { - inlines.push(process_subroutine_tag(info, child)?); + blocks_and_inlines + .push(SubroutineNode::Inline(process_subroutine_tag(info, child)?)); } TagKind::StructureType | TagKind::ClassType => { inner_types.push(UserDefinedType::Structure(process_structure_tag(info, child)?)) @@ -2736,8 +2737,7 @@ fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result Date: Fri, 19 Dec 2025 15:29:27 +0100 Subject: [PATCH 24/24] Change comment --- src/util/dwarf.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 911ff8e..623695b 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -2567,7 +2567,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result if modifiers.contains(&Modifier::Volatile) { volatile_ = true; } - // This is needed because parent_structure differs from member_of in virtual function overrides + // This is needed because direct_base differs from member_of in virtual function overrides if let TypeKind::UserDefined(key) = param.kind.kind { if let UserDefinedType::Structure(structure) = get_udt_by_key(info, key)? { direct_base = Some(structure); @@ -2650,7 +2650,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result override_, volatile_, }; - // TODO save this to a key => SubroutineType map and add a reference to the key in parent_structure + // TODO save this to a key => SubroutineType map and add a reference to the key in direct_base Ok(subroutine) }