SN GCC DWARF improvements

Now emits all DWARF tags for
Spongebob Squarepants: Creature
from the Krusty Krab properly.

Resolves #46
This commit is contained in:
Luke Street 2024-06-03 18:48:28 -06:00
parent 61cd72cd11
commit d63111466b
1 changed files with 86 additions and 100 deletions

View File

@ -697,7 +697,7 @@ pub struct BitData {
#[derive(Debug, Clone)]
pub struct StructureMember {
pub name: String,
pub name: Option<String>,
pub kind: Type,
pub offset: u32,
pub bit: Option<BitData>,
@ -732,7 +732,7 @@ pub struct StructureBase {
pub name: Option<String>,
pub base_type: Type,
pub offset: u32,
pub visibility: Visibility,
pub visibility: Option<Visibility>,
pub virtual_base: bool,
}
@ -780,17 +780,11 @@ pub struct SubroutineLabel {
#[derive(Debug, Clone)]
pub struct SubroutineBlock {
pub name: Option<String>,
pub start_address: u32,
pub end_address: u32,
pub start_address: Option<u32>,
pub end_address: Option<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,
pub inlines: Vec<SubroutineType>,
}
#[derive(Debug, Clone)]
@ -805,10 +799,11 @@ pub struct SubroutineType {
pub member_of: Option<u32>,
pub variables: Vec<SubroutineVariable>,
pub inline: bool,
pub virtual_: bool,
pub local: bool,
pub labels: Vec<SubroutineLabel>,
pub blocks: Vec<SubroutineBlock>,
pub inlines: Vec<SubroutineInline>,
pub inlines: Vec<SubroutineType>,
pub start_address: Option<u32>,
pub end_address: Option<u32>,
}
@ -1349,6 +1344,9 @@ pub fn subroutine_def_string(
if t.inline {
out.push_str("inline ");
}
if t.virtual_ {
out.push_str("virtual ");
}
out.push_str(&rt.prefix);
out.push(' ');
@ -1461,18 +1459,8 @@ pub fn subroutine_def_string(
if !t.inlines.is_empty() {
writeln!(out, "\n // Inlines")?;
for inline in &t.inlines {
let spec_tag = info
.tags
.get(&inline.specification)
.ok_or_else(|| anyhow!("Failed to locate inline tag {}", inline.specification))?;
let subroutine = process_subroutine_tag(info, spec_tag)?;
writeln!(
out,
" // -> {} ({:#X} - {:#X})",
subroutine_type_string(info, typedefs, &subroutine)?,
inline.start_address,
inline.end_address,
)?;
let inline_str = subroutine_def_string(info, typedefs, inline, is_erased)?;
out.push_str(&indent_all_by(4, inline_str));
}
}
@ -1491,7 +1479,10 @@ fn subroutine_block_string(
} else {
out.push_str("/* anonymous block */ ");
}
writeln!(out, "{{\n // Range: {:#X} -> {:#X}", block.start_address, block.end_address)?;
out.push_str("{\n");
if let (Some(start), Some(end)) = (block.start_address, block.end_address) {
writeln!(out, " // Range: {:#X} -> {:#X}", start, end)?;
}
let mut var_out = String::new();
for variable in &block.variables {
let ts = type_string(info, typedefs, &variable.kind, true)?;
@ -1508,6 +1499,13 @@ fn subroutine_block_string(
writeln!(var_out)?;
}
write!(out, "{}", indent_all_by(4, var_out))?;
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));
}
}
for block in &block.blocks {
let block_str = subroutine_block_string(info, typedefs, block)?;
out.push_str(&indent_all_by(4, block_str));
@ -1649,9 +1647,10 @@ pub fn struct_def_string(
out.push_str(", ");
}
match base.visibility {
Visibility::Private => out.push_str("private "),
Visibility::Protected => out.push_str("protected "),
Visibility::Public => out.push_str("public "),
Some(Visibility::Private) => out.push_str("private "),
Some(Visibility::Protected) => out.push_str("protected "),
Some(Visibility::Public) => out.push_str("public "),
None => {}
}
if base.virtual_base {
out.push_str("virtual ");
@ -1720,7 +1719,11 @@ pub fn struct_def_string(
}
let mut var_out = String::new();
let ts = type_string(info, typedefs, &member.kind, true)?;
write!(var_out, "{} {}{}", ts.prefix, member.name, ts.suffix)?;
if let Some(name) = &member.name {
write!(var_out, "{} {}{}", ts.prefix, name, ts.suffix)?;
} else {
write!(var_out, "{}{}", ts.prefix, ts.suffix)?;
}
if let Some(bit) = &member.bit {
write!(var_out, " : {}", bit.bit_size)?;
}
@ -1774,7 +1777,11 @@ pub fn union_def_string(info: &DwarfInfo, typedefs: &TypedefMap, t: &UnionType)
let mut var_out = String::new();
for member in t.members.iter() {
let ts = type_string(info, typedefs, &member.kind, true)?;
write!(var_out, "{} {}{};", ts.prefix, member.name, ts.suffix)?;
if let Some(name) = &member.name {
write!(var_out, "{} {}{};", ts.prefix, name, ts.suffix)?;
} else {
write!(var_out, "{}{};", ts.prefix, ts.suffix)?;
}
let size = if let Some(size) = member.byte_size { size } else { member.kind.size(info)? };
write!(var_out, " // offset {:#X}, size {:#X}", member.offset, size)?;
writeln!(var_out)?;
@ -1888,8 +1895,6 @@ fn process_inheritance_tag(info: &DwarfInfo, tag: &Tag) -> Result<StructureBase>
let base_type = base_type.ok_or_else(|| anyhow!("Inheritance without base type: {:?}", tag))?;
let offset = offset.ok_or_else(|| anyhow!("Inheritance without offset: {:?}", tag))?;
let visibility =
visibility.ok_or_else(|| anyhow!("Inheritance without visibility: {:?}", tag))?;
Ok(StructureBase { name, base_type, offset, visibility, virtual_base })
}
@ -1936,8 +1941,7 @@ fn process_structure_member_tag(info: &DwarfInfo, tag: &Tag) -> Result<Structure
bail!("Unhandled Member child {:?}", child.kind);
}
let name = name.ok_or_else(|| anyhow!("Member without name: {:?}", tag))?;
let member_type = member_type.ok_or_else(|| anyhow!("Member without type: {:?}", tag))?;
let kind = member_type.ok_or_else(|| anyhow!("Member without type: {:?}", tag))?;
let offset = offset.ok_or_else(|| anyhow!("Member without offset: {:?}", tag))?;
let bit = match (bit_size, bit_offset) {
(Some(bit_size), Some(bit_offset)) => Some(BitData { bit_size, bit_offset }),
@ -1945,14 +1949,7 @@ fn process_structure_member_tag(info: &DwarfInfo, tag: &Tag) -> Result<Structure
_ => bail!("Mismatched bit attributes in Member: {tag:?}"),
};
let visibility = visibility.unwrap_or(Visibility::Public);
Ok(StructureMember {
name: name.clone(),
kind: member_type,
offset,
bit,
visibility,
byte_size,
})
Ok(StructureMember { name, kind, offset, bit, visibility, byte_size })
}
fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result<StructureType> {
@ -1991,6 +1988,9 @@ fn process_structure_tag(info: &DwarfInfo, tag: &Tag) -> Result<StructureType> {
TagKind::Subroutine | TagKind::GlobalSubroutine => {
// TODO
}
TagKind::GlobalVariable => {
// TODO
}
TagKind::StructureType
| TagKind::ArrayType
| TagKind::EnumerationType
@ -2130,6 +2130,9 @@ fn process_enumeration_tag(info: &DwarfInfo, tag: &Tag) -> Result<EnumerationTyp
members.push(EnumerationMember { name, value });
}
}
(AttributeKind::Member, &AttributeValue::Reference(_key)) => {
// Pointer to parent structure, ignore
}
_ => {
bail!("Unhandled EnumerationType attribute {:?}", attr);
}
@ -2174,7 +2177,8 @@ fn process_union_tag(info: &DwarfInfo, tag: &Tag) -> Result<UnionType> {
| TagKind::UnionType
| TagKind::ClassType
| TagKind::SubroutineType
| TagKind::PtrToMemberType => {
| TagKind::PtrToMemberType
| TagKind::Typedef => {
// Variable type, ignore
}
kind => bail!("Unhandled UnionType child {:?}", kind),
@ -2189,7 +2193,10 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
ensure!(
matches!(
tag.kind,
TagKind::SubroutineType | TagKind::GlobalSubroutine | TagKind::Subroutine
TagKind::SubroutineType
| TagKind::GlobalSubroutine
| TagKind::Subroutine
| TagKind::InlinedSubroutine
),
"{:?} is not a Subroutine tag",
tag.kind
@ -2206,6 +2213,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
let mut inline = false;
let mut start_address = None;
let mut end_address = None;
let mut virtual_ = false;
for attr in &tag.attributes {
match (attr.kind, &attr.value) {
(AttributeKind::Sibling, _) => {}
@ -2271,6 +2279,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
// Restore register
}
(AttributeKind::Inline, _) => inline = true,
(AttributeKind::Virtual, _) => virtual_ = true,
(AttributeKind::Specification, &AttributeValue::Reference(key)) => {
let spec_tag = info
.tags
@ -2287,6 +2296,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
references.extend(spec.references);
member_of = member_of.or(spec.member_of);
inline = inline || spec.inline;
virtual_ = virtual_ || spec.virtual_;
}
_ => {
bail!("Unhandled SubroutineType attribute {:?}", attr);
@ -2299,7 +2309,6 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
let mut blocks = Vec::new();
let mut inlines = Vec::new();
for child in tag.children(&info.tags) {
ensure!(!var_args, "{:?} after UnspecifiedParameters", child.kind);
match child.kind {
TagKind::FormalParameter => {
parameters.push(process_subroutine_parameter_tag(info, child)?)
@ -2310,17 +2319,20 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
// TODO GlobalVariable refs?
}
TagKind::Label => labels.push(process_subroutine_label_tag(info, child)?),
TagKind::LexicalBlock => blocks.push(process_subroutine_block_tag(info, child)?),
TagKind::InlinedSubroutine => {
inlines.push(process_inlined_subroutine_tag(info, child)?)
TagKind::LexicalBlock => {
if let Some(block) = process_subroutine_block_tag(info, child)? {
blocks.push(block);
}
}
TagKind::InlinedSubroutine => inlines.push(process_subroutine_tag(info, child)?),
TagKind::StructureType
| TagKind::ArrayType
| TagKind::EnumerationType
| TagKind::UnionType
| TagKind::ClassType
| TagKind::SubroutineType
| TagKind::PtrToMemberType => {
| TagKind::PtrToMemberType
| TagKind::Typedef => {
// Variable type, ignore
}
kind => bail!("Unhandled SubroutineType child {:?}", kind),
@ -2341,6 +2353,7 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
member_of,
variables,
inline,
virtual_,
local,
labels,
blocks,
@ -2373,7 +2386,7 @@ fn process_subroutine_label_tag(info: &DwarfInfo, tag: &Tag) -> Result<Subroutin
Ok(SubroutineLabel { name, address })
}
fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineBlock> {
fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result<Option<SubroutineBlock>> {
ensure!(tag.kind == TagKind::LexicalBlock, "{:?} is not a LexicalBlock tag", tag.kind);
let mut name = None;
@ -2391,13 +2404,21 @@ fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result<Subroutin
let mut variables = Vec::new();
let mut blocks = Vec::new();
let mut inlines = Vec::new();
for child in tag.children(&info.tags) {
match child.kind {
TagKind::LocalVariable => variables.push(process_local_variable_tag(info, child)?),
TagKind::GlobalVariable => {
// TODO GlobalVariable refs?
}
TagKind::LexicalBlock => blocks.push(process_subroutine_block_tag(info, child)?),
TagKind::LexicalBlock => {
if let Some(block) = process_subroutine_block_tag(info, child)? {
blocks.push(block);
}
}
TagKind::InlinedSubroutine => {
inlines.push(process_subroutine_tag(info, child)?);
}
TagKind::StructureType
| TagKind::ArrayType
| TagKind::EnumerationType
@ -2411,51 +2432,7 @@ fn process_subroutine_block_tag(info: &DwarfInfo, tag: &Tag) -> Result<Subroutin
}
}
let start_address =
start_address.ok_or_else(|| anyhow!("LexicalBlock without start address: {:?}", tag))?;
let end_address =
end_address.ok_or_else(|| anyhow!("LexicalBlock without end address: {:?}", tag))?;
Ok(SubroutineBlock { name, start_address, end_address, variables, blocks })
}
fn process_inlined_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<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(&info.tags) {
match child.kind {
TagKind::GlobalVariable => {
// TODO GlobalVariable refs?
}
kind => bail!("Unhandled InlinedSubroutine child {:?}", kind),
}
}
let specification = specification
.ok_or_else(|| anyhow!("InlinedSubroutine without specification: {:?}", tag))?;
let start_address = start_address
.ok_or_else(|| anyhow!("InlinedSubroutine without start address: {:?}", tag))?;
let end_address =
end_address.ok_or_else(|| anyhow!("InlinedSubroutine without end address: {:?}", tag))?;
Ok(SubroutineInline { specification, start_address, end_address })
Ok(Some(SubroutineBlock { name, start_address, end_address, variables, blocks, inlines }))
}
fn process_subroutine_parameter_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineParameter> {
@ -2476,15 +2453,21 @@ fn process_subroutine_parameter_tag(info: &DwarfInfo, tag: &Tag) -> Result<Subro
_,
) => kind = Some(process_type(attr, info.e)?),
(AttributeKind::Location, AttributeValue::Block(block)) => {
location = Some(process_variable_location(
block,
if tag.is_erased { Endian::Little } else { info.e },
)?)
if !block.is_empty() {
location = Some(process_variable_location(
block,
if tag.is_erased { Endian::Little } else { info.e },
)?);
}
}
(AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => {
// TODO?
// info!("MwDwarf2Location: {:?} in {:?}", block, tag);
}
(AttributeKind::ConstValueBlock2 | AttributeKind::ConstValueBlock4, _) => {
// TODO?
// info!("ConstValueBlock: {:?} in {:?}", block, tag);
}
(AttributeKind::Specification, &AttributeValue::Reference(key)) => {
let spec_tag = info
.tags
@ -2869,6 +2852,9 @@ fn process_variable_tag(info: &DwarfInfo, tag: &Tag) -> Result<VariableTag> {
(AttributeKind::Location, AttributeValue::Block(block)) => {
address = Some(process_address(block, info.e)?)
}
(AttributeKind::Member, &AttributeValue::Reference(_key)) => {
// Pointer to parent structure, ignore
}
_ => {
bail!("Unhandled Variable attribute {:?}", attr);
}