mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-06-06 06:33:28 +00:00
Add dwarf dump
cmd
This commit is contained in:
parent
36e609eab7
commit
f1b4afa885
297
src/cmd/dwarf.rs
Normal file
297
src/cmd/dwarf.rs
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
use std::{
|
||||||
|
collections::{btree_map, BTreeMap},
|
||||||
|
fs::File,
|
||||||
|
io::{stdout, BufWriter, Cursor, Read, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use argh::FromArgs;
|
||||||
|
use object::{elf, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, Section};
|
||||||
|
|
||||||
|
use crate::util::{
|
||||||
|
dwarf::{
|
||||||
|
process_address, process_offset, process_type, process_variable_location,
|
||||||
|
read_debug_section, type_string, ud_type, ud_type_def, ud_type_string, AttributeKind,
|
||||||
|
TagKind, TypeKind,
|
||||||
|
},
|
||||||
|
file::map_file,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
/// process DWARF 1.1 information
|
||||||
|
#[argh(subcommand, name = "dwarf")]
|
||||||
|
pub struct Args {
|
||||||
|
#[argh(subcommand)]
|
||||||
|
command: SubCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
#[argh(subcommand)]
|
||||||
|
enum SubCommand {
|
||||||
|
Dump(DumpArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||||
|
/// dumps DWARF 1.1 info from an object or archive
|
||||||
|
#[argh(subcommand, name = "dump")]
|
||||||
|
pub struct DumpArgs {
|
||||||
|
#[argh(positional)]
|
||||||
|
/// input object (ELF or archive)
|
||||||
|
in_file: PathBuf,
|
||||||
|
#[argh(option, short = 'o')]
|
||||||
|
/// output file (or directory, for archive)
|
||||||
|
out: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(args: Args) -> Result<()> {
|
||||||
|
match args.command {
|
||||||
|
SubCommand::Dump(c_args) => dump(c_args),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump(args: DumpArgs) -> Result<()> {
|
||||||
|
let mmap = map_file(&args.in_file)?;
|
||||||
|
if mmap.starts_with(b"!<arch>\n") {
|
||||||
|
let mut archive = ar::Archive::new(&*mmap);
|
||||||
|
while let Some(result) = archive.next_entry() {
|
||||||
|
let mut e = match result {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(e) => bail!("Failed to read archive entry: {:?}", e),
|
||||||
|
};
|
||||||
|
let name = String::from_utf8_lossy(e.header().identifier()).to_string();
|
||||||
|
let mut data = vec![0u8; e.header().size() as usize];
|
||||||
|
e.read(&mut data)?;
|
||||||
|
let obj_file = object::read::File::parse(&*data)?;
|
||||||
|
let debug_section = match obj_file.section_by_name(".debug") {
|
||||||
|
Some(section) => {
|
||||||
|
log::info!("Processing '{}'", name);
|
||||||
|
section
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
log::warn!("Object '{}' missing .debug section", name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(out_path) = &args.out {
|
||||||
|
// TODO make a basename method
|
||||||
|
let name = name.trim_start_matches("D:").replace('\\', "/");
|
||||||
|
let name = name.rsplit_once('/').map(|(a, b)| b).unwrap_or(&name);
|
||||||
|
let file_path = out_path.join(format!("{}.txt", name));
|
||||||
|
let mut file = BufWriter::new(File::create(file_path)?);
|
||||||
|
dump_debug_section(&mut file, &obj_file, debug_section)?;
|
||||||
|
file.flush()?;
|
||||||
|
} else {
|
||||||
|
println!("\nFile {}:", name);
|
||||||
|
dump_debug_section(&mut stdout(), &obj_file, debug_section)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let obj_file = object::read::File::parse(&*mmap)?;
|
||||||
|
let debug_section = obj_file
|
||||||
|
.section_by_name(".debug")
|
||||||
|
.ok_or_else(|| anyhow!("Failed to locate .debug section"))?;
|
||||||
|
if let Some(out_path) = &args.out {
|
||||||
|
let mut file = BufWriter::new(File::create(out_path)?);
|
||||||
|
dump_debug_section(&mut file, &obj_file, debug_section)?;
|
||||||
|
file.flush()?;
|
||||||
|
} else {
|
||||||
|
dump_debug_section(&mut stdout(), &obj_file, debug_section)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_debug_section<W: Write>(
|
||||||
|
w: &mut W,
|
||||||
|
obj_file: &object::File<'_>,
|
||||||
|
debug_section: Section,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut data = debug_section.uncompressed_data()?.into_owned();
|
||||||
|
|
||||||
|
// Apply relocations to data
|
||||||
|
for (addr, reloc) in debug_section.relocations() {
|
||||||
|
match reloc.kind() {
|
||||||
|
RelocationKind::Absolute | RelocationKind::Elf(elf::R_PPC_UADDR32) => {
|
||||||
|
let target = match reloc.target() {
|
||||||
|
RelocationTarget::Symbol(symbol_idx) => {
|
||||||
|
let symbol = obj_file.symbol_by_index(symbol_idx)?;
|
||||||
|
(symbol.address() as i64 + reloc.addend()) as u32
|
||||||
|
}
|
||||||
|
_ => bail!("Invalid .debug relocation target"),
|
||||||
|
};
|
||||||
|
data[addr as usize..addr as usize + 4].copy_from_slice(&target.to_be_bytes());
|
||||||
|
}
|
||||||
|
RelocationKind::Elf(elf::R_PPC_NONE) => {}
|
||||||
|
_ => bail!("Unhandled .debug relocation type {:?}", reloc.kind()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(&*data);
|
||||||
|
let tags = read_debug_section(&mut reader)?;
|
||||||
|
|
||||||
|
for (&addr, tag) in &tags {
|
||||||
|
log::debug!("{}: {:?}", addr, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut units = Vec::<String>::new();
|
||||||
|
if let Some((_, mut tag)) = tags.first_key_value() {
|
||||||
|
loop {
|
||||||
|
match tag.kind {
|
||||||
|
TagKind::CompileUnit => {
|
||||||
|
let unit = tag
|
||||||
|
.string_attribute(AttributeKind::Name)
|
||||||
|
.ok_or_else(|| anyhow!("CompileUnit without name {:?}", tag))?;
|
||||||
|
if units.contains(unit) {
|
||||||
|
log::warn!("Duplicate unit '{}'", unit);
|
||||||
|
} else {
|
||||||
|
units.push(unit.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let children = tag.children(&tags);
|
||||||
|
let mut typedefs = BTreeMap::<u32, Vec<u32>>::new();
|
||||||
|
for child in children {
|
||||||
|
match child.kind {
|
||||||
|
TagKind::GlobalSubroutine | TagKind::Subroutine => {
|
||||||
|
let _is_prototyped =
|
||||||
|
child.string_attribute(AttributeKind::Prototyped).is_some();
|
||||||
|
if let (Some(_hi), Some(_lo)) = (
|
||||||
|
child.address_attribute(AttributeKind::HighPc),
|
||||||
|
child.address_attribute(AttributeKind::LowPc),
|
||||||
|
) {}
|
||||||
|
let name = child
|
||||||
|
.string_attribute(AttributeKind::Name)
|
||||||
|
.ok_or_else(|| anyhow!("Subroutine without name"))?;
|
||||||
|
let udt = ud_type(&tags, child)?;
|
||||||
|
let ts = ud_type_string(&tags, &typedefs, &udt)?;
|
||||||
|
writeln!(w, "{} {}{} {{", ts.prefix, name, ts.suffix)?;
|
||||||
|
for tag in child.children(&tags) {
|
||||||
|
match tag.kind {
|
||||||
|
TagKind::LocalVariable => {}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
let address = if let Some(location) =
|
||||||
|
tag.block_attribute(AttributeKind::Location)
|
||||||
|
{
|
||||||
|
process_variable_location(location)?
|
||||||
|
} else {
|
||||||
|
"[unknown]".to_string()
|
||||||
|
};
|
||||||
|
let type_attr = tag.type_attribute().ok_or_else(|| {
|
||||||
|
anyhow!("LocalVariable without type attr")
|
||||||
|
})?;
|
||||||
|
let var_type = process_type(type_attr)?;
|
||||||
|
let ts = type_string(&tags, &typedefs, &var_type)?;
|
||||||
|
let name = tag
|
||||||
|
.string_attribute(AttributeKind::Name)
|
||||||
|
.ok_or_else(|| anyhow!("LocalVariable without name"))?;
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"\t{} {}{}; // {}",
|
||||||
|
ts.prefix, name, ts.suffix, address
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
writeln!(w, "}}")?;
|
||||||
|
}
|
||||||
|
TagKind::Typedef => {
|
||||||
|
let name = child
|
||||||
|
.string_attribute(AttributeKind::Name)
|
||||||
|
.ok_or_else(|| anyhow!("Typedef without name"))?;
|
||||||
|
let attr = child
|
||||||
|
.type_attribute()
|
||||||
|
.ok_or_else(|| anyhow!("Typedef without type attribute"))?;
|
||||||
|
let t = process_type(attr)?;
|
||||||
|
let ts = type_string(&tags, &typedefs, &t)?;
|
||||||
|
writeln!(w, "typedef {} {}{};", ts.prefix, name, ts.suffix)?;
|
||||||
|
|
||||||
|
// TODO fundamental typedefs?
|
||||||
|
if let Some(ud_type_ref) =
|
||||||
|
child.reference_attribute(AttributeKind::UserDefType)
|
||||||
|
{
|
||||||
|
match typedefs.entry(ud_type_ref) {
|
||||||
|
btree_map::Entry::Vacant(e) => {
|
||||||
|
e.insert(vec![child.key]);
|
||||||
|
}
|
||||||
|
btree_map::Entry::Occupied(e) => {
|
||||||
|
e.into_mut().push(child.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TagKind::GlobalVariable | TagKind::LocalVariable => {
|
||||||
|
let name = child
|
||||||
|
.string_attribute(AttributeKind::Name)
|
||||||
|
.ok_or_else(|| anyhow!("Variable without name"))?;
|
||||||
|
let address = if let Some(location) =
|
||||||
|
child.block_attribute(AttributeKind::Location)
|
||||||
|
{
|
||||||
|
Some(process_address(location)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if let Some(type_attr) = child.type_attribute() {
|
||||||
|
let var_type = process_type(type_attr)?;
|
||||||
|
// log::info!("{:?}", var_type);
|
||||||
|
// if let TypeKind::UserDefined(key) = var_type.kind {
|
||||||
|
// let ud_tag = tags
|
||||||
|
// .get(&key)
|
||||||
|
// .ok_or_else(|| anyhow!("Invalid UD type ref"))?;
|
||||||
|
// let ud_type = ud_type(&tags, ud_tag)?;
|
||||||
|
// log::info!("{:?}", ud_type);
|
||||||
|
// }
|
||||||
|
let ts = type_string(&tags, &typedefs, &var_type)?;
|
||||||
|
let st = if child.kind == TagKind::LocalVariable {
|
||||||
|
"static "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
let address_str = match address {
|
||||||
|
Some(addr) => format!(" : {:#010X}", addr),
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
|
let size = var_type.size(&tags)?;
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"{}{} {}{}{}; // size: {:#X}",
|
||||||
|
st, ts.prefix, name, ts.suffix, address_str, size,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TagKind::StructureType
|
||||||
|
| TagKind::ArrayType
|
||||||
|
| TagKind::EnumerationType
|
||||||
|
| TagKind::UnionType
|
||||||
|
| TagKind::ClassType
|
||||||
|
| TagKind::SubroutineType => {
|
||||||
|
let udt = ud_type(&tags, child)?;
|
||||||
|
if child.string_attribute(AttributeKind::Name).is_some() {
|
||||||
|
writeln!(w, "{}", ud_type_def(&tags, &typedefs, &udt)?)?;
|
||||||
|
} else {
|
||||||
|
// log::warn!("No name for tag: {:?}", child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log::warn!("Unhandled CompileUnit child {:?}", child.kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!("Children: {:?}", children.iter().map(|c| c.kind).collect::<Vec<TagKind>>());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log::warn!("Expected CompileUnit, got {:?}", tag.kind);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(next) = tag.next_sibling(&tags) {
|
||||||
|
tag = next;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// log::info!("Link order:");
|
||||||
|
// for x in units {
|
||||||
|
// log::info!("{}", x);
|
||||||
|
// }
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
pub mod ar;
|
pub mod ar;
|
||||||
pub mod demangle;
|
pub mod demangle;
|
||||||
pub mod dol;
|
pub mod dol;
|
||||||
|
pub mod dwarf;
|
||||||
pub mod elf;
|
pub mod elf;
|
||||||
pub mod elf2dol;
|
pub mod elf2dol;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
|
@ -19,6 +19,7 @@ enum SubCommand {
|
|||||||
Ar(cmd::ar::Args),
|
Ar(cmd::ar::Args),
|
||||||
Demangle(cmd::demangle::Args),
|
Demangle(cmd::demangle::Args),
|
||||||
Dol(cmd::dol::Args),
|
Dol(cmd::dol::Args),
|
||||||
|
Dwarf(cmd::dwarf::Args),
|
||||||
Elf(cmd::elf::Args),
|
Elf(cmd::elf::Args),
|
||||||
Elf2Dol(cmd::elf2dol::Args),
|
Elf2Dol(cmd::elf2dol::Args),
|
||||||
Map(cmd::map::Args),
|
Map(cmd::map::Args),
|
||||||
@ -36,6 +37,7 @@ fn main() {
|
|||||||
SubCommand::Ar(c_args) => cmd::ar::run(c_args),
|
SubCommand::Ar(c_args) => cmd::ar::run(c_args),
|
||||||
SubCommand::Demangle(c_args) => cmd::demangle::run(c_args),
|
SubCommand::Demangle(c_args) => cmd::demangle::run(c_args),
|
||||||
SubCommand::Dol(c_args) => cmd::dol::run(c_args),
|
SubCommand::Dol(c_args) => cmd::dol::run(c_args),
|
||||||
|
SubCommand::Dwarf(c_args) => cmd::dwarf::run(c_args),
|
||||||
SubCommand::Elf(c_args) => cmd::elf::run(c_args),
|
SubCommand::Elf(c_args) => cmd::elf::run(c_args),
|
||||||
SubCommand::Elf2Dol(c_args) => cmd::elf2dol::run(c_args),
|
SubCommand::Elf2Dol(c_args) => cmd::elf2dol::run(c_args),
|
||||||
SubCommand::Map(c_args) => cmd::map::run(c_args),
|
SubCommand::Map(c_args) => cmd::map::run(c_args),
|
||||||
|
@ -777,13 +777,14 @@ pub fn struct_def_string(
|
|||||||
Some(name) => format!("struct {} {{\n", name),
|
Some(name) => format!("struct {} {{\n", name),
|
||||||
None => "struct {\n".to_string(),
|
None => "struct {\n".to_string(),
|
||||||
};
|
};
|
||||||
|
writeln!(out, "\t// total size: {:#X}", t.byte_size)?;
|
||||||
for member in &t.members {
|
for member in &t.members {
|
||||||
let ts = type_string(tags, typedefs, &member.kind)?;
|
let ts = type_string(tags, typedefs, &member.kind)?;
|
||||||
write!(out, "\t{} {}{}", ts.prefix, member.name, ts.suffix)?;
|
write!(out, "\t{} {}{}", ts.prefix, member.name, ts.suffix)?;
|
||||||
if let Some(bit) = &member.bit {
|
if let Some(bit) = &member.bit {
|
||||||
write!(out, " : {}", bit.bit_size)?;
|
write!(out, " : {}", bit.bit_size)?;
|
||||||
}
|
}
|
||||||
write!(out, ";\n")?;
|
write!(out, "; // offset {:#X}, size {:#X}\n", member.offset, member.kind.size(tags)?)?;
|
||||||
}
|
}
|
||||||
write!(out, "}}")?;
|
write!(out, "}}")?;
|
||||||
Ok(out)
|
Ok(out)
|
||||||
@ -844,7 +845,7 @@ pub fn fund_type_string(ft: FundType) -> Result<&'static str> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_offset(block: &[u8]) -> Result<u32> {
|
pub fn process_offset(block: &[u8]) -> Result<u32> {
|
||||||
if block.len() == 6 && block[0] == LocationOp::Const as u8 && block[5] == LocationOp::Add as u8
|
if block.len() == 6 && block[0] == LocationOp::Const as u8 && block[5] == LocationOp::Add as u8
|
||||||
{
|
{
|
||||||
Ok(u32::from_be_bytes(block[1..5].try_into()?))
|
Ok(u32::from_be_bytes(block[1..5].try_into()?))
|
||||||
@ -861,6 +862,17 @@ pub fn process_address(block: &[u8]) -> Result<u32> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_variable_location(block: &[u8]) -> Result<String> {
|
||||||
|
// TODO: float regs
|
||||||
|
if block.len() == 5 && block[0] == LocationOp::Register as u8 {
|
||||||
|
Ok(format!("r{}", u32::from_be_bytes(block[1..].try_into()?)))
|
||||||
|
} else if block.len() == 11 && block[0] == LocationOp::BaseRegister as u8 && block[5] == LocationOp::Const as u8 && block[10] == LocationOp::Add as u8 {
|
||||||
|
Ok(format!("r{}+{:#X}", u32::from_be_bytes(block[1..5].try_into()?), u32::from_be_bytes(block[6..10].try_into()?)))
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("Unhandled location data {:?}, expected variable loc", block))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ud_type(tags: &TagMap, tag: &Tag) -> Result<UserDefinedType> {
|
pub fn ud_type(tags: &TagMap, tag: &Tag) -> Result<UserDefinedType> {
|
||||||
match tag.kind {
|
match tag.kind {
|
||||||
TagKind::ArrayType => {
|
TagKind::ArrayType => {
|
||||||
|
182
src/util/elf.rs
182
src/util/elf.rs
@ -44,8 +44,6 @@ enum BoundaryState {
|
|||||||
FilesEnded,
|
FilesEnded,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ENABLE_DWARF: bool = false;
|
|
||||||
|
|
||||||
pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
let mmap = map_file(path)?;
|
let mmap = map_file(path)?;
|
||||||
let obj_file = object::read::File::parse(&*mmap)?;
|
let obj_file = object::read::File::parse(&*mmap)?;
|
||||||
@ -60,14 +58,6 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
|||||||
kind => bail!("Unexpected ELF type: {kind:?}"),
|
kind => bail!("Unexpected ELF type: {kind:?}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if ENABLE_DWARF {
|
|
||||||
if let Some(debug_section) = obj_file.section_by_name(".debug") {
|
|
||||||
if debug_section.size() > 0 {
|
|
||||||
load_debug_section(&obj_file, debug_section)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut obj_name = String::new();
|
let mut obj_name = String::new();
|
||||||
let mut stack_address: Option<u32> = None;
|
let mut stack_address: Option<u32> = None;
|
||||||
let mut stack_end: Option<u32> = None;
|
let mut stack_end: Option<u32> = None;
|
||||||
@ -719,175 +709,3 @@ fn to_obj_reloc(
|
|||||||
let reloc_data = ObjReloc { kind: reloc_kind, address, target_symbol, addend };
|
let reloc_data = ObjReloc { kind: reloc_kind, address, target_symbol, addend };
|
||||||
Ok(reloc_data)
|
Ok(reloc_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_debug_section(obj_file: &object::File<'_>, debug_section: Section) -> Result<()> {
|
|
||||||
let mut data = debug_section.uncompressed_data()?.into_owned();
|
|
||||||
|
|
||||||
// Apply relocations to data
|
|
||||||
for (addr, reloc) in debug_section.relocations() {
|
|
||||||
match reloc.kind() {
|
|
||||||
RelocationKind::Absolute | RelocationKind::Elf(elf::R_PPC_UADDR32) => {
|
|
||||||
let target = match reloc.target() {
|
|
||||||
RelocationTarget::Symbol(symbol_idx) => {
|
|
||||||
let symbol = obj_file.symbol_by_index(symbol_idx)?;
|
|
||||||
(symbol.address() as i64 + reloc.addend()) as u32
|
|
||||||
}
|
|
||||||
// RelocationTarget::Section(section_idx) => {
|
|
||||||
// let section = obj_file.section_by_index(section_idx)?;
|
|
||||||
// (section.address() as i64 + reloc.addend()) as u32
|
|
||||||
// }
|
|
||||||
// RelocationTarget::Absolute => reloc.addend() as u32,
|
|
||||||
_ => bail!("Invalid .debug relocation target"),
|
|
||||||
};
|
|
||||||
data[addr as usize..addr as usize + 4].copy_from_slice(&target.to_be_bytes());
|
|
||||||
}
|
|
||||||
RelocationKind::Elf(elf::R_PPC_NONE) => {}
|
|
||||||
_ => bail!("Unhandled .debug relocation type {:?}", reloc.kind()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut reader = Cursor::new(&*data);
|
|
||||||
let tags = read_debug_section(&mut reader)?;
|
|
||||||
|
|
||||||
// let mut w = BufWriter::new(File::create("dwarfdump2.txt")?);
|
|
||||||
// for (&addr, tag) in &tags {
|
|
||||||
// writeln!(w, "{}: {:?}", addr, tag)?;
|
|
||||||
// }
|
|
||||||
// w.flush()?;
|
|
||||||
|
|
||||||
let mut units = Vec::<String>::new();
|
|
||||||
if let Some((_, mut tag)) = tags.first_key_value() {
|
|
||||||
loop {
|
|
||||||
match tag.kind {
|
|
||||||
TagKind::CompileUnit => {
|
|
||||||
let unit = tag
|
|
||||||
.string_attribute(AttributeKind::Name)
|
|
||||||
.ok_or_else(|| anyhow!("CompileUnit without name {:?}", tag))?;
|
|
||||||
if units.contains(unit) {
|
|
||||||
log::warn!("Duplicate unit '{}'", unit);
|
|
||||||
} else {
|
|
||||||
units.push(unit.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let children = tag.children(&tags);
|
|
||||||
let mut typedefs = BTreeMap::<u32, Vec<u32>>::new();
|
|
||||||
for child in children {
|
|
||||||
match child.kind {
|
|
||||||
TagKind::GlobalSubroutine | TagKind::Subroutine => {
|
|
||||||
let _is_prototyped =
|
|
||||||
child.string_attribute(AttributeKind::Prototyped).is_some();
|
|
||||||
if let (Some(_hi), Some(_lo)) = (
|
|
||||||
child.address_attribute(AttributeKind::HighPc),
|
|
||||||
child.address_attribute(AttributeKind::LowPc),
|
|
||||||
) {}
|
|
||||||
let name = child
|
|
||||||
.string_attribute(AttributeKind::Name)
|
|
||||||
.ok_or_else(|| anyhow!("Subroutine without name"))?;
|
|
||||||
let udt = ud_type(&tags, child)?;
|
|
||||||
let ts = ud_type_string(&tags, &typedefs, &udt)?;
|
|
||||||
// log::info!("{} {}{};", ts.prefix, name, ts.suffix);
|
|
||||||
}
|
|
||||||
TagKind::Typedef => {
|
|
||||||
let name = child
|
|
||||||
.string_attribute(AttributeKind::Name)
|
|
||||||
.ok_or_else(|| anyhow!("Typedef without name"))?;
|
|
||||||
let attr = child
|
|
||||||
.type_attribute()
|
|
||||||
.ok_or_else(|| anyhow!("Typedef without type attribute"))?;
|
|
||||||
let t = process_type(attr)?;
|
|
||||||
let ts = type_string(&tags, &typedefs, &t)?;
|
|
||||||
// log::info!("typedef {} {}{};", ts.prefix, name, ts.suffix);
|
|
||||||
|
|
||||||
// TODO fundamental typedefs?
|
|
||||||
if let Some(ud_type_ref) =
|
|
||||||
child.reference_attribute(AttributeKind::UserDefType)
|
|
||||||
{
|
|
||||||
match typedefs.entry(ud_type_ref) {
|
|
||||||
Entry::Vacant(e) => {
|
|
||||||
e.insert(vec![child.key]);
|
|
||||||
}
|
|
||||||
Entry::Occupied(e) => {
|
|
||||||
e.into_mut().push(child.key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TagKind::GlobalVariable | TagKind::LocalVariable => {
|
|
||||||
let name = child
|
|
||||||
.string_attribute(AttributeKind::Name)
|
|
||||||
.ok_or_else(|| anyhow!("Variable without name"))?;
|
|
||||||
let address = if let Some(location) =
|
|
||||||
child.block_attribute(AttributeKind::Location)
|
|
||||||
{
|
|
||||||
Some(process_address(location)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
if let Some(type_attr) = child.type_attribute() {
|
|
||||||
let var_type = process_type(type_attr)?;
|
|
||||||
// log::info!("{:?}", var_type);
|
|
||||||
if let TypeKind::UserDefined(key) = var_type.kind {
|
|
||||||
let ud_tag = tags
|
|
||||||
.get(&key)
|
|
||||||
.ok_or_else(|| anyhow!("Invalid UD type ref"))?;
|
|
||||||
let ud_type = ud_type(&tags, ud_tag)?;
|
|
||||||
// log::info!("{:?}", ud_type);
|
|
||||||
}
|
|
||||||
let ts = type_string(&tags, &typedefs, &var_type)?;
|
|
||||||
let st = if child.kind == TagKind::LocalVariable {
|
|
||||||
"static "
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
};
|
|
||||||
let address_str = match address {
|
|
||||||
Some(addr) => format!(" : {:#010X}", addr),
|
|
||||||
None => String::new(),
|
|
||||||
};
|
|
||||||
let size = var_type.size(&tags)?;
|
|
||||||
log::info!(
|
|
||||||
"{}{} {}{}{}; // size: {:#X}",
|
|
||||||
st,
|
|
||||||
ts.prefix,
|
|
||||||
name,
|
|
||||||
ts.suffix,
|
|
||||||
address_str,
|
|
||||||
size,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TagKind::StructureType
|
|
||||||
| TagKind::ArrayType
|
|
||||||
| TagKind::EnumerationType
|
|
||||||
| TagKind::UnionType
|
|
||||||
| TagKind::ClassType
|
|
||||||
| TagKind::SubroutineType => {
|
|
||||||
let udt = ud_type(&tags, child)?;
|
|
||||||
if child.string_attribute(AttributeKind::Name).is_some() {
|
|
||||||
// log::info!("{}", ud_type_def(&tags, &typedefs, &udt)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
log::warn!("Unhandled CompileUnit child {:?}", child.kind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// println!("Children: {:?}", children.iter().map(|c| c.kind).collect::<Vec<TagKind>>());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
log::warn!("Expected CompileUnit, got {:?}", tag.kind);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(next) = tag.next_sibling(&tags) {
|
|
||||||
tag = next;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// log::info!("Link order:");
|
|
||||||
// for x in units {
|
|
||||||
// log::info!("{}", x);
|
|
||||||
// }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user