Infer anonymous unions from type layout (#26)
* Infer anonymous unions from type layout * Add comments to signify inferred types * Improve union detection * Fix some output weirdness * Handle some more anonymous union edge cases * Change union offset validation * Skip anonymous unions with less than 2 members.
This commit is contained in:
parent
9f8c55efe9
commit
5a3256b2b6
|
@ -1407,6 +1407,114 @@ fn subroutine_block_string(
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct AnonUnion {
|
||||||
|
offset: u32,
|
||||||
|
member_index: usize,
|
||||||
|
member_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct AnonUnionGroup {
|
||||||
|
member_index: usize,
|
||||||
|
member_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_anon_unions(info: &DwarfInfo, members: &[StructureMember]) -> Result<Vec<AnonUnion>> {
|
||||||
|
let mut unions = Vec::<AnonUnion>::new();
|
||||||
|
let mut offset = u32::MAX;
|
||||||
|
'member: for (prev, member) in members.iter().skip(1).enumerate() {
|
||||||
|
if let Some(bit) = &member.bit {
|
||||||
|
if bit.bit_offset != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if member.offset <= members[prev].offset && member.offset != offset {
|
||||||
|
offset = member.offset;
|
||||||
|
for (i, member) in members.iter().enumerate() {
|
||||||
|
if member.offset == offset {
|
||||||
|
for anon in &unions {
|
||||||
|
if anon.member_index == i {
|
||||||
|
continue 'member;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unions.push(AnonUnion { offset, member_index: i, member_count: 0 });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for anon in &mut unions {
|
||||||
|
for (i, member) in members.iter().skip(anon.member_index).enumerate() {
|
||||||
|
if let Some(bit) = &member.bit {
|
||||||
|
if bit.bit_offset != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if member.offset == anon.offset {
|
||||||
|
anon.member_count = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut max_offset = 0;
|
||||||
|
for member in members.iter().skip(anon.member_index).take(anon.member_count + 1) {
|
||||||
|
if let Some(bit) = &member.bit {
|
||||||
|
if bit.bit_offset != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let size =
|
||||||
|
if let Some(size) = member.byte_size { size } else { member.kind.size(info)? };
|
||||||
|
max_offset = max(max_offset, member.offset + size);
|
||||||
|
}
|
||||||
|
for member in members.iter().skip(anon.member_index + anon.member_count) {
|
||||||
|
if let Some(bit) = &member.bit {
|
||||||
|
if bit.bit_offset != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if member.offset >= max_offset || member.offset < anon.offset {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
anon.member_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(unions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_anon_union_groups(members: &[StructureMember], unions: &[AnonUnion]) -> Vec<AnonUnionGroup> {
|
||||||
|
let mut groups = Vec::new();
|
||||||
|
for anon in unions {
|
||||||
|
for (i, member) in
|
||||||
|
members.iter().skip(anon.member_index).take(anon.member_count).enumerate()
|
||||||
|
{
|
||||||
|
if let Some(bit) = &member.bit {
|
||||||
|
if bit.bit_offset != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if member.offset == anon.offset {
|
||||||
|
let mut group =
|
||||||
|
AnonUnionGroup { member_index: anon.member_index + i, member_count: 1 };
|
||||||
|
|
||||||
|
for member in
|
||||||
|
members.iter().skip(anon.member_index).take(anon.member_count).skip(i + 1)
|
||||||
|
{
|
||||||
|
if member.offset == anon.offset {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
group.member_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if group.member_count > 1 {
|
||||||
|
groups.push(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
groups
|
||||||
|
}
|
||||||
|
|
||||||
pub fn struct_def_string(
|
pub fn struct_def_string(
|
||||||
info: &DwarfInfo,
|
info: &DwarfInfo,
|
||||||
typedefs: &TypedefMap,
|
typedefs: &TypedefMap,
|
||||||
|
@ -1453,7 +1561,12 @@ pub fn struct_def_string(
|
||||||
StructureKind::Struct => Visibility::Public,
|
StructureKind::Struct => Visibility::Public,
|
||||||
StructureKind::Class => Visibility::Private,
|
StructureKind::Class => Visibility::Private,
|
||||||
};
|
};
|
||||||
for member in &t.members {
|
let mut indent = 4;
|
||||||
|
let unions = get_anon_unions(info, &t.members)?;
|
||||||
|
let groups = get_anon_union_groups(&t.members, &unions);
|
||||||
|
let mut in_union = 0;
|
||||||
|
let mut in_group = 0;
|
||||||
|
for (i, member) in t.members.iter().enumerate() {
|
||||||
if vis != member.visibility {
|
if vis != member.visibility {
|
||||||
vis = member.visibility;
|
vis = member.visibility;
|
||||||
match member.visibility {
|
match member.visibility {
|
||||||
|
@ -1462,6 +1575,40 @@ pub fn struct_def_string(
|
||||||
Visibility::Public => out.push_str("public:\n"),
|
Visibility::Public => out.push_str("public:\n"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for anon in &groups {
|
||||||
|
if i == anon.member_index + anon.member_count {
|
||||||
|
indent -= 4;
|
||||||
|
out.push_str(&indent_all_by(indent, "};\n"));
|
||||||
|
in_group -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for anon in &unions {
|
||||||
|
if anon.member_count < 2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if i == anon.member_index + anon.member_count {
|
||||||
|
indent -= 4;
|
||||||
|
out.push_str(&indent_all_by(indent, "};\n"));
|
||||||
|
in_union -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for anon in &unions {
|
||||||
|
if anon.member_count < 2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if i == anon.member_index {
|
||||||
|
out.push_str(&indent_all_by(indent, "union { // inferred\n"));
|
||||||
|
indent += 4;
|
||||||
|
in_union += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for anon in &groups {
|
||||||
|
if i == anon.member_index {
|
||||||
|
out.push_str(&indent_all_by(indent, "struct { // inferred\n"));
|
||||||
|
indent += 4;
|
||||||
|
in_group += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut var_out = String::new();
|
let mut var_out = String::new();
|
||||||
let ts = type_string(info, typedefs, &member.kind, true)?;
|
let ts = type_string(info, typedefs, &member.kind, true)?;
|
||||||
write!(var_out, "{} {}{}", ts.prefix, member.name, ts.suffix)?;
|
write!(var_out, "{} {}{}", ts.prefix, member.name, ts.suffix)?;
|
||||||
|
@ -1470,7 +1617,17 @@ pub fn struct_def_string(
|
||||||
}
|
}
|
||||||
let size = if let Some(size) = member.byte_size { size } else { member.kind.size(info)? };
|
let size = if let Some(size) = member.byte_size { size } else { member.kind.size(info)? };
|
||||||
writeln!(var_out, "; // offset {:#X}, size {:#X}", member.offset, size)?;
|
writeln!(var_out, "; // offset {:#X}, size {:#X}", member.offset, size)?;
|
||||||
out.push_str(&indent_all_by(4, var_out));
|
out.push_str(&indent_all_by(indent, var_out));
|
||||||
|
}
|
||||||
|
while in_group > 0 {
|
||||||
|
indent -= 4;
|
||||||
|
out.push_str(&indent_all_by(indent, "};\n"));
|
||||||
|
in_group -= 1;
|
||||||
|
}
|
||||||
|
while in_union > 0 {
|
||||||
|
indent -= 4;
|
||||||
|
out.push_str(&indent_all_by(indent, "};\n"));
|
||||||
|
in_union -= 1;
|
||||||
}
|
}
|
||||||
out.push('}');
|
out.push('}');
|
||||||
Ok(out)
|
Ok(out)
|
||||||
|
|
Loading…
Reference in New Issue