dwarf dump: Better support for GCC/ProDG & various fixes
- Displays subroutine "static" and "inline" if present - Displays subroutine labels, blocks and inlines if present - Displays struct member visibility if present - Skips tags that can't be processed rather than bailing
This commit is contained in:
parent
5128ff67b2
commit
4cb6f4f85d
|
@ -178,11 +178,43 @@ where
|
||||||
let children = tag.children(&tags);
|
let children = tag.children(&tags);
|
||||||
let mut typedefs = BTreeMap::<u32, Vec<u32>>::new();
|
let mut typedefs = BTreeMap::<u32, Vec<u32>>::new();
|
||||||
for child in children {
|
for child in children {
|
||||||
let tag_type = process_root_tag(&tags, child)?;
|
let tag_type = match process_root_tag(&tags, child) {
|
||||||
|
Ok(tag_type) => tag_type,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to process tag {} (unit {}): {}",
|
||||||
|
child.key,
|
||||||
|
unit,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"// ERROR: Failed to process tag {} ({:?})",
|
||||||
|
child.key, child.kind
|
||||||
|
)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
if should_skip_tag(&tag_type) {
|
if should_skip_tag(&tag_type) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
writeln!(w, "{}", tag_type_string(&tags, &typedefs, &tag_type)?)?;
|
match tag_type_string(&tags, &typedefs, &tag_type) {
|
||||||
|
Ok(s) => writeln!(w, "{}", s)?,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to emit tag {} (unit {}): {}",
|
||||||
|
child.key,
|
||||||
|
unit,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"// ERROR: Failed to emit tag {} ({:?})",
|
||||||
|
child.key, child.kind
|
||||||
|
)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let TagKind::Typedef = child.kind {
|
if let TagKind::Typedef = child.kind {
|
||||||
// TODO fundamental typedefs?
|
// TODO fundamental typedefs?
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
cmp::max,
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
fmt::{Display, Formatter, Write},
|
fmt::{Display, Formatter, Write},
|
||||||
|
@ -567,7 +568,7 @@ pub enum StructureKind {
|
||||||
pub struct StructureType {
|
pub struct StructureType {
|
||||||
pub kind: StructureKind,
|
pub kind: StructureKind,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub byte_size: u32,
|
pub byte_size: Option<u32>,
|
||||||
pub members: Vec<StructureMember>,
|
pub members: Vec<StructureMember>,
|
||||||
pub bases: Vec<StructureBase>,
|
pub bases: Vec<StructureBase>,
|
||||||
}
|
}
|
||||||
|
@ -581,7 +582,7 @@ pub enum Visibility {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StructureBase {
|
pub struct StructureBase {
|
||||||
pub name: String,
|
pub name: Option<String>,
|
||||||
pub base_type: Type,
|
pub base_type: Type,
|
||||||
pub offset: u32,
|
pub offset: u32,
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
@ -622,6 +623,28 @@ pub struct SubroutineVariable {
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SubroutineLabel {
|
||||||
|
pub name: String,
|
||||||
|
pub address: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SubroutineBlock {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub start_address: u32,
|
||||||
|
pub end_address: u32,
|
||||||
|
pub variables: Vec<SubroutineVariable>,
|
||||||
|
pub blocks: Vec<SubroutineBlock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SubroutineInline {
|
||||||
|
pub specification: u32,
|
||||||
|
pub start_address: u32,
|
||||||
|
pub end_address: u32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SubroutineType {
|
pub struct SubroutineType {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
@ -633,6 +656,11 @@ pub struct SubroutineType {
|
||||||
pub references: Vec<u32>,
|
pub references: Vec<u32>,
|
||||||
pub member_of: Option<u32>,
|
pub member_of: Option<u32>,
|
||||||
pub variables: Vec<SubroutineVariable>,
|
pub variables: Vec<SubroutineVariable>,
|
||||||
|
pub inline: bool,
|
||||||
|
pub local: bool,
|
||||||
|
pub labels: Vec<SubroutineLabel>,
|
||||||
|
pub blocks: Vec<SubroutineBlock>,
|
||||||
|
pub inlines: Vec<SubroutineInline>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -674,6 +702,16 @@ pub enum TagType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserDefinedType {
|
impl UserDefinedType {
|
||||||
|
pub fn name(&self) -> Option<String> {
|
||||||
|
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 {
|
pub fn is_definition(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
UserDefinedType::Array(_) | UserDefinedType::PtrToMember(_) => false,
|
UserDefinedType::Array(_) | UserDefinedType::PtrToMember(_) => false,
|
||||||
|
@ -693,7 +731,20 @@ impl UserDefinedType {
|
||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
UserDefinedType::Structure(t) => t.byte_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(tags)?,
|
||||||
|
};
|
||||||
|
max_end = max(max_end, member.offset + size);
|
||||||
|
}
|
||||||
|
max_end
|
||||||
|
}
|
||||||
|
},
|
||||||
UserDefinedType::Enumeration(t) => t.byte_size,
|
UserDefinedType::Enumeration(t) => t.byte_size,
|
||||||
UserDefinedType::Union(t) => t.byte_size,
|
UserDefinedType::Union(t) => t.byte_size,
|
||||||
UserDefinedType::Subroutine(_) => 0,
|
UserDefinedType::Subroutine(_) => 0,
|
||||||
|
@ -826,6 +877,27 @@ pub fn type_string(
|
||||||
apply_modifiers(str, &t.modifiers)
|
apply_modifiers(str, &t.modifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_name(tags: &TagMap, typedefs: &TypedefMap, t: &Type) -> Result<String> {
|
||||||
|
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()) {
|
||||||
|
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 = tags
|
||||||
|
.get(&key)
|
||||||
|
.ok_or_else(|| anyhow!("Failed to locate user defined type {}", key))?;
|
||||||
|
let udt = ud_type(tags, tag)?;
|
||||||
|
udt.name().ok_or_else(|| anyhow!("User defined type without name"))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn array_type_string(
|
fn array_type_string(
|
||||||
tags: &TagMap,
|
tags: &TagMap,
|
||||||
typedefs: &TypedefMap,
|
typedefs: &TypedefMap,
|
||||||
|
@ -1037,7 +1109,11 @@ pub fn subroutine_def_string(
|
||||||
t: &SubroutineType,
|
t: &SubroutineType,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let rt = type_string(tags, typedefs, &t.return_type, true)?;
|
let rt = type_string(tags, typedefs, &t.return_type, true)?;
|
||||||
let mut out = rt.prefix;
|
let mut out = if t.local { "static ".to_string() } else { String::new() };
|
||||||
|
if t.inline {
|
||||||
|
out.push_str("inline ");
|
||||||
|
}
|
||||||
|
out.push_str(&rt.prefix);
|
||||||
out.push(' ');
|
out.push(' ');
|
||||||
|
|
||||||
let mut name_written = false;
|
let mut name_written = false;
|
||||||
|
@ -1089,7 +1165,7 @@ pub fn subroutine_def_string(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.var_args {
|
if t.var_args {
|
||||||
write!(out, ", ...")?;
|
write!(parameters, ", ...")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write!(out, "({}){} {{", parameters, rt.suffix)?;
|
write!(out, "({}){} {{", parameters, rt.suffix)?;
|
||||||
|
@ -1129,6 +1205,74 @@ pub fn subroutine_def_string(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(tags, 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 = tags
|
||||||
|
.get(&inline.specification)
|
||||||
|
.ok_or_else(|| anyhow!("Failed to locate inline tag {}", inline.specification))?;
|
||||||
|
let subroutine = process_subroutine_tag(tags, spec_tag)?;
|
||||||
|
writeln!(
|
||||||
|
out,
|
||||||
|
" // -> {} ({:#X} - {:#X})",
|
||||||
|
subroutine_type_string(tags, typedefs, &subroutine)?,
|
||||||
|
inline.start_address,
|
||||||
|
inline.end_address,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(out, "}}")?;
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subroutine_block_string(
|
||||||
|
tags: &TagMap,
|
||||||
|
typedefs: &TypedefMap,
|
||||||
|
block: &SubroutineBlock,
|
||||||
|
) -> Result<String> {
|
||||||
|
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(tags, 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(tags, typedefs, block)?;
|
||||||
|
out.push_str(&indent_all_by(4, block_str));
|
||||||
|
}
|
||||||
writeln!(out, "}}")?;
|
writeln!(out, "}}")?;
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
@ -1161,11 +1305,30 @@ pub fn struct_def_string(
|
||||||
if base.virtual_base {
|
if base.virtual_base {
|
||||||
out.push_str("virtual ");
|
out.push_str("virtual ");
|
||||||
}
|
}
|
||||||
out.push_str(&base.name);
|
if let Some(name) = &base.name {
|
||||||
|
out.push_str(name);
|
||||||
|
} else {
|
||||||
|
out.push_str(&type_name(tags, typedefs, &base.base_type)?);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
writeln!(out, " {{\n // total size: {:#X}", t.byte_size)?;
|
out.push_str(" {\n");
|
||||||
let mut var_out = String::new();
|
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 {
|
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(tags, typedefs, &member.kind, true)?;
|
let ts = type_string(tags, typedefs, &member.kind, true)?;
|
||||||
write!(var_out, "{} {}{}", ts.prefix, member.name, ts.suffix)?;
|
write!(var_out, "{} {}{}", ts.prefix, member.name, ts.suffix)?;
|
||||||
if let Some(bit) = &member.bit {
|
if let Some(bit) = &member.bit {
|
||||||
|
@ -1173,9 +1336,9 @@ pub fn struct_def_string(
|
||||||
}
|
}
|
||||||
let size = if let Some(size) = member.byte_size { size } else { member.kind.size(tags)? };
|
let size = if let Some(size) = member.byte_size { size } else { member.kind.size(tags)? };
|
||||||
writeln!(var_out, "; // offset {:#X}, size {:#X}", member.offset, size)?;
|
writeln!(var_out, "; // offset {:#X}, size {:#X}", member.offset, size)?;
|
||||||
|
out.push_str(&indent_all_by(4, var_out));
|
||||||
}
|
}
|
||||||
write!(out, "{}", indent_all_by(4, var_out))?;
|
out.push('}');
|
||||||
write!(out, "}}")?;
|
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1309,7 +1472,6 @@ fn process_inheritance_tag(tags: &TagMap, tag: &Tag) -> Result<StructureBase> {
|
||||||
bail!("Unhandled Inheritance child {:?}", child.kind);
|
bail!("Unhandled Inheritance child {:?}", child.kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = name.ok_or_else(|| anyhow!("Inheritance without name: {:?}", tag))?;
|
|
||||||
let base_type = base_type.ok_or_else(|| anyhow!("Inheritance without base type: {:?}", tag))?;
|
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 offset = offset.ok_or_else(|| anyhow!("Inheritance without offset: {:?}", tag))?;
|
||||||
let visibility =
|
let visibility =
|
||||||
|
@ -1347,6 +1509,9 @@ fn process_structure_member_tag(tags: &TagMap, tag: &Tag) -> Result<StructureMem
|
||||||
(AttributeKind::Private, _) => visibility = Some(Visibility::Private),
|
(AttributeKind::Private, _) => visibility = Some(Visibility::Private),
|
||||||
(AttributeKind::Protected, _) => visibility = Some(Visibility::Protected),
|
(AttributeKind::Protected, _) => visibility = Some(Visibility::Protected),
|
||||||
(AttributeKind::Public, _) => visibility = Some(Visibility::Public),
|
(AttributeKind::Public, _) => visibility = Some(Visibility::Public),
|
||||||
|
(AttributeKind::Member, &AttributeValue::Reference(_key)) => {
|
||||||
|
// Pointer to parent structure, ignore
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
bail!("Unhandled Member attribute {:?}", attr);
|
bail!("Unhandled Member attribute {:?}", attr);
|
||||||
}
|
}
|
||||||
|
@ -1390,6 +1555,9 @@ fn process_structure_tag(tags: &TagMap, tag: &Tag) -> Result<StructureType> {
|
||||||
(AttributeKind::Sibling, _) => {}
|
(AttributeKind::Sibling, _) => {}
|
||||||
(AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()),
|
(AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()),
|
||||||
(AttributeKind::ByteSize, &AttributeValue::Data4(value)) => byte_size = Some(value),
|
(AttributeKind::ByteSize, &AttributeValue::Data4(value)) => byte_size = Some(value),
|
||||||
|
(AttributeKind::Member, &AttributeValue::Reference(_key)) => {
|
||||||
|
// Pointer to parent structure, ignore
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
bail!("Unhandled structure attribute {:?}", attr);
|
bail!("Unhandled structure attribute {:?}", attr);
|
||||||
}
|
}
|
||||||
|
@ -1406,12 +1574,22 @@ fn process_structure_tag(tags: &TagMap, tag: &Tag) -> Result<StructureType> {
|
||||||
// TODO?
|
// TODO?
|
||||||
// info!("Structure {:?} Typedef: {:?}", name, child);
|
// 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),
|
kind => bail!("Unhandled StructureType child {:?}", kind),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let byte_size =
|
|
||||||
byte_size.ok_or_else(|| anyhow!("StructureType without ByteSize: {:?}", tag))?;
|
|
||||||
Ok(StructureType {
|
Ok(StructureType {
|
||||||
kind: if tag.kind == TagKind::ClassType {
|
kind: if tag.kind == TagKind::ClassType {
|
||||||
StructureKind::Class
|
StructureKind::Class
|
||||||
|
@ -1477,6 +1655,20 @@ fn process_array_subscript_data(data: &[u8]) -> Result<(Type, Vec<ArrayDimension
|
||||||
size: NonZeroU32::new(high_bound.wrapping_add(1)),
|
size: NonZeroU32::new(high_bound.wrapping_add(1)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
SubscriptFormat::FundTypeConstLocation => {
|
||||||
|
let index_type = FundType::try_from(u16::from_be_bytes(*array_ref!(data, 0, 2)))
|
||||||
|
.context("Invalid fundamental type ID")?;
|
||||||
|
let low_bound = u32::from_be_bytes(*array_ref!(data, 2, 4));
|
||||||
|
ensure!(low_bound == 0, "Invalid array low bound {low_bound}, expected 0");
|
||||||
|
let size = u16::from_be_bytes(*array_ref!(data, 6, 2));
|
||||||
|
let (block, remain) = data[8..].split_at(size as usize);
|
||||||
|
let location = if block.is_empty() { 0 } else { process_offset(block)? };
|
||||||
|
data = remain;
|
||||||
|
dimensions.push(ArrayDimension {
|
||||||
|
index_type: Type { kind: TypeKind::Fundamental(index_type), modifiers: vec![] },
|
||||||
|
size: NonZeroU32::new(location),
|
||||||
|
});
|
||||||
|
}
|
||||||
SubscriptFormat::ElementType => {
|
SubscriptFormat::ElementType => {
|
||||||
let mut cursor = Cursor::new(data);
|
let mut cursor = Cursor::new(data);
|
||||||
let type_attr = read_attribute(&mut cursor)?;
|
let type_attr = read_attribute(&mut cursor)?;
|
||||||
|
@ -1529,21 +1721,33 @@ fn process_union_tag(tags: &TagMap, tag: &Tag) -> Result<UnionType> {
|
||||||
|
|
||||||
let mut name = None;
|
let mut name = None;
|
||||||
let mut byte_size = None;
|
let mut byte_size = None;
|
||||||
let mut members = Vec::new();
|
|
||||||
for attr in &tag.attributes {
|
for attr in &tag.attributes {
|
||||||
match (attr.kind, &attr.value) {
|
match (attr.kind, &attr.value) {
|
||||||
(AttributeKind::Sibling, _) => {}
|
(AttributeKind::Sibling, _) => {}
|
||||||
(AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()),
|
(AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()),
|
||||||
(AttributeKind::ByteSize, &AttributeValue::Data4(value)) => byte_size = Some(value),
|
(AttributeKind::ByteSize, &AttributeValue::Data4(value)) => byte_size = Some(value),
|
||||||
|
(AttributeKind::Member, &AttributeValue::Reference(_key)) => {
|
||||||
|
// Pointer to parent structure, ignore
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
bail!("Unhandled UnionType attribute {:?}", attr);
|
bail!("Unhandled UnionType attribute {:?}", attr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut members = Vec::new();
|
||||||
for child in tag.children(tags) {
|
for child in tag.children(tags) {
|
||||||
match child.kind {
|
match child.kind {
|
||||||
TagKind::Member => members.push(process_structure_member_tag(tags, child)?),
|
TagKind::Member => members.push(process_structure_member_tag(tags, child)?),
|
||||||
|
TagKind::StructureType
|
||||||
|
| TagKind::ArrayType
|
||||||
|
| TagKind::EnumerationType
|
||||||
|
| TagKind::UnionType
|
||||||
|
| TagKind::ClassType
|
||||||
|
| TagKind::SubroutineType
|
||||||
|
| TagKind::PtrToMemberType => {
|
||||||
|
// Variable type, ignore
|
||||||
|
}
|
||||||
kind => bail!("Unhandled UnionType child {:?}", kind),
|
kind => bail!("Unhandled UnionType child {:?}", kind),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1570,6 +1774,7 @@ fn process_subroutine_tag(tags: &TagMap, tag: &Tag) -> Result<SubroutineType> {
|
||||||
let mut var_args = false;
|
let mut var_args = false;
|
||||||
let mut references = Vec::new();
|
let mut references = Vec::new();
|
||||||
let mut member_of = None;
|
let mut member_of = None;
|
||||||
|
let mut inline = false;
|
||||||
for attr in &tag.attributes {
|
for attr in &tag.attributes {
|
||||||
match (attr.kind, &attr.value) {
|
match (attr.kind, &attr.value) {
|
||||||
(AttributeKind::Sibling, _) => {}
|
(AttributeKind::Sibling, _) => {}
|
||||||
|
@ -1596,6 +1801,29 @@ fn process_subroutine_tag(tags: &TagMap, tag: &Tag) -> Result<SubroutineType> {
|
||||||
(AttributeKind::Member, &AttributeValue::Reference(key)) => {
|
(AttributeKind::Member, &AttributeValue::Reference(key)) => {
|
||||||
member_of = Some(key);
|
member_of = Some(key);
|
||||||
}
|
}
|
||||||
|
(AttributeKind::MwPrologueEnd, &AttributeValue::Address(_addr)) => {
|
||||||
|
// Prologue end
|
||||||
|
}
|
||||||
|
(AttributeKind::MwEpilogueStart, &AttributeValue::Address(_addr)) => {
|
||||||
|
// Epilogue start
|
||||||
|
}
|
||||||
|
(AttributeKind::Inline, _) => inline = true,
|
||||||
|
(AttributeKind::Specification, &AttributeValue::Reference(key)) => {
|
||||||
|
let spec_tag = tags
|
||||||
|
.get(&key)
|
||||||
|
.ok_or_else(|| anyhow!("Failed to locate specification tag {}", key))?;
|
||||||
|
// Merge attributes from specification tag
|
||||||
|
let spec = process_subroutine_tag(tags, 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);
|
bail!("Unhandled SubroutineType attribute {:?}", attr);
|
||||||
}
|
}
|
||||||
|
@ -1603,6 +1831,9 @@ fn process_subroutine_tag(tags: &TagMap, tag: &Tag) -> Result<SubroutineType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut variables = Vec::new();
|
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(tags) {
|
for child in tag.children(tags) {
|
||||||
ensure!(!var_args, "{:?} after UnspecifiedParameters", child.kind);
|
ensure!(!var_args, "{:?} after UnspecifiedParameters", child.kind);
|
||||||
match child.kind {
|
match child.kind {
|
||||||
|
@ -1611,12 +1842,30 @@ fn process_subroutine_tag(tags: &TagMap, tag: &Tag) -> Result<SubroutineType> {
|
||||||
}
|
}
|
||||||
TagKind::UnspecifiedParameters => var_args = true,
|
TagKind::UnspecifiedParameters => var_args = true,
|
||||||
TagKind::LocalVariable => variables.push(process_local_variable_tag(tags, child)?),
|
TagKind::LocalVariable => variables.push(process_local_variable_tag(tags, child)?),
|
||||||
|
TagKind::GlobalVariable => {
|
||||||
|
// TODO GlobalVariable refs?
|
||||||
|
}
|
||||||
|
TagKind::Label => labels.push(process_subroutine_label_tag(tags, child)?),
|
||||||
|
TagKind::LexicalBlock => blocks.push(process_subroutine_block_tag(tags, child)?),
|
||||||
|
TagKind::InlinedSubroutine => {
|
||||||
|
inlines.push(process_inlined_subroutine_tag(tags, child)?)
|
||||||
|
}
|
||||||
|
TagKind::StructureType
|
||||||
|
| TagKind::ArrayType
|
||||||
|
| TagKind::EnumerationType
|
||||||
|
| TagKind::UnionType
|
||||||
|
| TagKind::ClassType
|
||||||
|
| TagKind::SubroutineType
|
||||||
|
| TagKind::PtrToMemberType => {
|
||||||
|
// Variable type, ignore
|
||||||
|
}
|
||||||
kind => bail!("Unhandled SubroutineType child {:?}", kind),
|
kind => bail!("Unhandled SubroutineType child {:?}", kind),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_type = return_type
|
let return_type = return_type
|
||||||
.unwrap_or_else(|| Type { kind: TypeKind::Fundamental(FundType::Void), modifiers: vec![] });
|
.unwrap_or_else(|| Type { kind: TypeKind::Fundamental(FundType::Void), modifiers: vec![] });
|
||||||
|
let local = tag.kind == TagKind::Subroutine;
|
||||||
Ok(SubroutineType {
|
Ok(SubroutineType {
|
||||||
name,
|
name,
|
||||||
mangled_name,
|
mangled_name,
|
||||||
|
@ -1627,9 +1876,122 @@ fn process_subroutine_tag(tags: &TagMap, tag: &Tag) -> Result<SubroutineType> {
|
||||||
references,
|
references,
|
||||||
member_of,
|
member_of,
|
||||||
variables,
|
variables,
|
||||||
|
inline,
|
||||||
|
local,
|
||||||
|
labels,
|
||||||
|
blocks,
|
||||||
|
inlines,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_subroutine_label_tag(tags: &TagMap, tag: &Tag) -> Result<SubroutineLabel> {
|
||||||
|
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(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(tags: &TagMap, tag: &Tag) -> Result<SubroutineBlock> {
|
||||||
|
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(tags) {
|
||||||
|
match child.kind {
|
||||||
|
TagKind::LocalVariable => variables.push(process_local_variable_tag(tags, child)?),
|
||||||
|
TagKind::GlobalVariable => {
|
||||||
|
// TODO GlobalVariable refs?
|
||||||
|
}
|
||||||
|
TagKind::LexicalBlock => blocks.push(process_subroutine_block_tag(tags, 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(tags: &TagMap, tag: &Tag) -> Result<SubroutineInline> {
|
||||||
|
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(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(tags: &TagMap, tag: &Tag) -> Result<SubroutineParameter> {
|
fn process_subroutine_parameter_tag(tags: &TagMap, tag: &Tag) -> Result<SubroutineParameter> {
|
||||||
ensure!(tag.kind == TagKind::FormalParameter, "{:?} is not a FormalParameter tag", tag.kind);
|
ensure!(tag.kind == TagKind::FormalParameter, "{:?} is not a FormalParameter tag", tag.kind);
|
||||||
|
|
||||||
|
@ -1654,9 +2016,17 @@ fn process_subroutine_parameter_tag(tags: &TagMap, tag: &Tag) -> Result<Subrouti
|
||||||
// TODO?
|
// TODO?
|
||||||
// info!("MwDwarf2Location: {:?} in {:?}", block, tag);
|
// info!("MwDwarf2Location: {:?} in {:?}", block, tag);
|
||||||
}
|
}
|
||||||
_ => {
|
(AttributeKind::Specification, &AttributeValue::Reference(key)) => {
|
||||||
bail!("Unhandled SubroutineParameter attribute {:?}", attr);
|
let spec_tag = tags
|
||||||
|
.get(&key)
|
||||||
|
.ok_or_else(|| anyhow!("Failed to locate specification tag {}", key))?;
|
||||||
|
// Merge attributes from specification tag
|
||||||
|
let spec = process_subroutine_parameter_tag(tags, spec_tag)?;
|
||||||
|
name = name.or(spec.name);
|
||||||
|
kind = kind.or(Some(spec.kind));
|
||||||
|
location = location.or(spec.location);
|
||||||
}
|
}
|
||||||
|
_ => bail!("Unhandled SubroutineParameter attribute {:?}", attr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1686,12 +2056,24 @@ fn process_local_variable_tag(tags: &TagMap, tag: &Tag) -> Result<SubroutineVari
|
||||||
_,
|
_,
|
||||||
) => kind = Some(process_type(attr)?),
|
) => kind = Some(process_type(attr)?),
|
||||||
(AttributeKind::Location, AttributeValue::Block(block)) => {
|
(AttributeKind::Location, AttributeValue::Block(block)) => {
|
||||||
location = Some(process_variable_location(block)?)
|
if !block.is_empty() {
|
||||||
|
location = Some(process_variable_location(block)?);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => {
|
(AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => {
|
||||||
// TODO?
|
// TODO?
|
||||||
// info!("MwDwarf2Location: {:?} in {:?}", block, tag);
|
// info!("MwDwarf2Location: {:?} in {:?}", block, tag);
|
||||||
}
|
}
|
||||||
|
(AttributeKind::Specification, &AttributeValue::Reference(key)) => {
|
||||||
|
let spec_tag = tags
|
||||||
|
.get(&key)
|
||||||
|
.ok_or_else(|| anyhow!("Failed to locate specification tag {}", key))?;
|
||||||
|
// Merge attributes from specification tag
|
||||||
|
let spec = process_local_variable_tag(tags, spec_tag)?;
|
||||||
|
name = name.or(spec.name);
|
||||||
|
kind = kind.or(Some(spec.kind));
|
||||||
|
location = location.or(spec.location);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
bail!("Unhandled LocalVariable attribute {:?}", attr);
|
bail!("Unhandled LocalVariable attribute {:?}", attr);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue