From 89f6a2782ba45e5e1bc730fbba300008e85484aa Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Wed, 3 Apr 2024 14:03:42 +0100 Subject: [PATCH] Allow loading of the dwarf information from the gba rom --- agb-addr2line/Cargo.toml | 2 + agb-addr2line/src/load_dwarf.rs | 74 +++++++++++++++++++++++++++++++++ agb-addr2line/src/main.rs | 8 ++-- 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 agb-addr2line/src/load_dwarf.rs diff --git a/agb-addr2line/Cargo.toml b/agb-addr2line/Cargo.toml index 63130c8b..22aee0ef 100644 --- a/agb-addr2line/Cargo.toml +++ b/agb-addr2line/Cargo.toml @@ -15,6 +15,8 @@ addr2line = { version = "0.21", default-features = false, features = [ "std-object", ] } colored = "2" +rmp-serde = "1" +lz4_flex = "0.11" [profile.dev] opt-level = 3 diff --git a/agb-addr2line/src/load_dwarf.rs b/agb-addr2line/src/load_dwarf.rs new file mode 100644 index 00000000..6282d531 --- /dev/null +++ b/agb-addr2line/src/load_dwarf.rs @@ -0,0 +1,74 @@ +use std::{borrow::Cow, collections::HashMap, io::Cursor, rc::Rc}; + +use addr2line::{ + gimli, + object::{self, Object}, +}; +use anyhow::bail; + +pub fn load_dwarf( + file_content: &[u8], +) -> anyhow::Result>> { + if let Ok(object) = object::File::parse(file_content) { + return load_from_object(&object); + } + + let last_8_bytes = &file_content[file_content.len() - 8..]; + let len = u32::from_le_bytes(last_8_bytes[0..4].try_into()?) as usize; + let version = u32::from_le_bytes(last_8_bytes[4..].try_into()?) as usize; + + if version != 1 { + bail!("Only version 1 of the debug info is supported"); + } + + let compressed_debug_data = &file_content[file_content.len() - len - 8..file_content.len() - 8]; + + let decompressing_reader = + lz4_flex::frame::FrameDecoder::new(Cursor::new(compressed_debug_data)); + let debug_info: HashMap> = rmp_serde::decode::from_read(decompressing_reader)?; + + let dwarf = gimli::Dwarf::load(|id| { + let data = debug_info + .get(id.name()) + .map(|data| Cow::Borrowed(data.as_slice())) + .unwrap_or(Cow::Borrowed(&[])); + + Result::<_, gimli::Error>::Ok(gimli::EndianRcSlice::new( + Rc::from(&*data), + gimli::RunTimeEndian::Little, + )) + })?; + + Ok(dwarf) +} + +fn load_from_object<'file>( + object: &object::File<'file, &'file [u8]>, +) -> anyhow::Result>> { + let endian = if object.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + + fn load_section<'data: 'file, 'file, O, Endian>( + id: gimli::SectionId, + file: &'file O, + endian: Endian, + ) -> Result, gimli::Error> + where + O: object::Object<'data, 'file>, + Endian: gimli::Endianity, + { + use object::ObjectSection; + + let data = file + .section_by_name(id.name()) + .and_then(|section| section.uncompressed_data().ok()) + .unwrap_or(Cow::Borrowed(&[])); + Ok(gimli::EndianRcSlice::new(Rc::from(&*data), endian)) + } + + let dwarf = gimli::Dwarf::load(|id| load_section(id, object, endian))?; + Ok(dwarf) +} diff --git a/agb-addr2line/src/main.rs b/agb-addr2line/src/main.rs index 35f5bc3b..efb5bd62 100644 --- a/agb-addr2line/src/main.rs +++ b/agb-addr2line/src/main.rs @@ -6,11 +6,13 @@ use std::{ time::SystemTime, }; -use addr2line::{gimli, object}; +use addr2line::gimli; use clap::Parser; use colored::Colorize; +use load_dwarf::load_dwarf; mod gwilym_encoding; +mod load_dwarf; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] @@ -46,9 +48,9 @@ fn main() -> anyhow::Result<()> { .unwrap_or(SystemTime::UNIX_EPOCH); let file = fs::read(&cli.elf_path)?; - let object = object::File::parse(file.as_slice())?; + let dwarf = load_dwarf(&file)?; - let ctx = addr2line::Context::new(&object)?; + let ctx = addr2line::Context::from_dwarf(dwarf)?; for (i, address) in gwilym_encoding::gwilym_decode(&cli.dump)?.enumerate() { print_address(&ctx, i, address.into(), modification_time)?;