Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DWP improvements #698

Merged
merged 3 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ default = ["read", "std", "fallible-iterator"]

[[bin]]
name = "simple"
required-features = ["read"]
required-features = ["read", "std"]

[[bin]]
name = "simple_line"
required-features = ["read"]
required-features = ["read", "std"]

[[bin]]
name = "dwarfdump"
Expand Down
138 changes: 98 additions & 40 deletions crates/examples/src/bin/simple.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,82 @@
//! A simple example of parsing `.debug_info`.
//!
//! This example demonstrates how to parse the `.debug_info` section of a
//! DWARF object file and iterate over the compilation units and their DIEs.
//! It also demonstrates how to find the DWO unit for each CU in a DWP file.
//!
//! Most of the complexity is due to loading the sections from the object
//! file and DWP file, which is not something that is provided by gimli itself.

use object::{Object, ObjectSection};
use std::{borrow, env, fs};
use std::{borrow, env, error, fs};

fn main() {
for path in env::args().skip(1) {
let file = fs::File::open(&path).unwrap();
let mmap = unsafe { memmap2::Mmap::map(&file).unwrap() };
let object = object::File::parse(&*mmap).unwrap();
let endian = if object.is_little_endian() {
gimli::RunTimeEndian::Little
} else {
gimli::RunTimeEndian::Big
};
dump_file(&object, endian).unwrap();
let mut args = env::args();
if args.len() != 2 && args.len() != 3 {
println!("Usage: {} <file> [dwp]", args.next().unwrap());
return;
}
}
args.next().unwrap();
let path = args.next().unwrap();
let dwp_path = args.next();

fn dump_file(object: &object::File, endian: gimli::RunTimeEndian) -> Result<(), gimli::Error> {
// Load a section and return as `Cow<[u8]>`.
let load_section = |id: gimli::SectionId| -> Result<borrow::Cow<[u8]>, gimli::Error> {
match object.section_by_name(id.name()) {
Some(ref section) => Ok(section
.uncompressed_data()
.unwrap_or(borrow::Cow::Borrowed(&[][..]))),
None => Ok(borrow::Cow::Borrowed(&[][..])),
}
let file = fs::File::open(path).unwrap();
let mmap = unsafe { memmap2::Mmap::map(&file).unwrap() };
let object = object::File::parse(&*mmap).unwrap();
let endian = if object.is_little_endian() {
gimli::RunTimeEndian::Little
} else {
gimli::RunTimeEndian::Big
};

// Load all of the sections.
let dwarf_cow = gimli::Dwarf::load(&load_section)?;
if let Some(dwp_path) = dwp_path {
let dwp_file = fs::File::open(dwp_path).unwrap();
let dwp_mmap = unsafe { memmap2::Mmap::map(&dwp_file).unwrap() };
let dwp_object = object::File::parse(&*dwp_mmap).unwrap();
assert_eq!(dwp_object.is_little_endian(), object.is_little_endian());

dump_file(&object, Some(&dwp_object), endian).unwrap();
} else {
dump_file(&object, None, endian).unwrap();
}
}

fn dump_file(
object: &object::File,
dwp_object: Option<&object::File>,
endian: gimli::RunTimeEndian,
) -> Result<(), Box<dyn error::Error>> {
// Load a section and return as `Cow<[u8]>`.
fn load_section<'a>(
object: &'a object::File,
name: &str,
) -> Result<borrow::Cow<'a, [u8]>, Box<dyn error::Error>> {
Ok(match object.section_by_name(name) {
Some(section) => section.uncompressed_data()?,
None => borrow::Cow::Borrowed(&[]),
})
}

// Borrow a `Cow<[u8]>` to create an `EndianSlice`.
let borrow_section: &dyn for<'a> Fn(
&'a borrow::Cow<[u8]>,
) -> gimli::EndianSlice<'a, gimli::RunTimeEndian> =
&|section| gimli::EndianSlice::new(section, endian);
let borrow_section = |section| gimli::EndianSlice::new(borrow::Cow::as_ref(section), endian);

// Load all of the sections.
let dwarf_sections = gimli::DwarfSections::load(|id| load_section(object, id.name()))?;
let dwp_sections = dwp_object
.map(|dwp_object| {
gimli::DwarfPackageSections::load(|id| load_section(dwp_object, id.dwo_name().unwrap()))
})
.transpose()?;

// Create `EndianSlice`s for all of the sections.
let dwarf = dwarf_cow.borrow(&borrow_section);
// Create `EndianSlice`s for all of the sections and do preliminary parsing.
// Alternatively, we could have used `Dwarf::load` with an owned type such as `EndianRcSlice`.
let dwarf = dwarf_sections.borrow(borrow_section);
let dwp = dwp_sections
.as_ref()
.map(|dwp_sections| {
dwp_sections.borrow(borrow_section, gimli::EndianSlice::new(&[], endian))
})
.transpose()?;

// Iterate over the compilation units.
let mut iter = dwarf.units();
Expand All @@ -48,19 +86,39 @@ fn dump_file(object: &object::File, endian: gimli::RunTimeEndian) -> Result<(),
header.offset().as_debug_info_offset().unwrap().0
);
let unit = dwarf.unit(header)?;
dump_unit(&unit)?;

// Check for a DWO unit.
let Some(dwp) = &dwp else { continue };
let Some(dwo_id) = unit.dwo_id else { continue };
println!("DWO Unit ID {:x}", dwo_id.0);
let Some(dwo) = dwp.find_cu(dwo_id, &dwarf)? else {
continue;
};
let Some(header) = dwo.units().next()? else {
continue;
};
let unit = dwo.unit(header)?;
dump_unit(&unit)?;
}

Ok(())
}

// Iterate over the Debugging Information Entries (DIEs) in the unit.
let mut depth = 0;
let mut entries = unit.entries();
while let Some((delta_depth, entry)) = entries.next_dfs()? {
depth += delta_depth;
println!("<{}><{:x}> {}", depth, entry.offset().0, entry.tag());
fn dump_unit(
unit: &gimli::Unit<gimli::EndianSlice<gimli::RunTimeEndian>>,
) -> Result<(), gimli::Error> {
// Iterate over the Debugging Information Entries (DIEs) in the unit.
let mut depth = 0;
let mut entries = unit.entries();
while let Some((delta_depth, entry)) = entries.next_dfs()? {
depth += delta_depth;
println!("<{}><{:x}> {}", depth, entry.offset().0, entry.tag());

// Iterate over the attributes in the DIE.
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
println!(" {}: {:?}", attr.name(), attr.value());
}
// Iterate over the attributes in the DIE.
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
println!(" {}: {:?}", attr.name(), attr.value());
}
}
Ok(())
Expand Down
32 changes: 15 additions & 17 deletions crates/examples/src/bin/simple_line.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! A simple example of parsing `.debug_line`.

use object::{Object, ObjectSection};
use std::{borrow, env, fs, path};
use std::{borrow, env, error, fs, path};

fn main() {
for path in env::args().skip(1) {
Expand All @@ -17,28 +17,26 @@ fn main() {
}
}

fn dump_file(object: &object::File, endian: gimli::RunTimeEndian) -> Result<(), gimli::Error> {
fn dump_file(
object: &object::File,
endian: gimli::RunTimeEndian,
) -> Result<(), Box<dyn error::Error>> {
// Load a section and return as `Cow<[u8]>`.
let load_section = |id: gimli::SectionId| -> Result<borrow::Cow<[u8]>, gimli::Error> {
match object.section_by_name(id.name()) {
Some(ref section) => Ok(section
.uncompressed_data()
.unwrap_or(borrow::Cow::Borrowed(&[][..]))),
None => Ok(borrow::Cow::Borrowed(&[][..])),
}
let load_section = |id: gimli::SectionId| -> Result<borrow::Cow<[u8]>, Box<dyn error::Error>> {
Ok(match object.section_by_name(id.name()) {
Some(section) => section.uncompressed_data()?,
None => borrow::Cow::Borrowed(&[]),
})
};

// Load all of the sections.
let dwarf_cow = gimli::Dwarf::load(&load_section)?;

// Borrow a `Cow<[u8]>` to create an `EndianSlice`.
let borrow_section: &dyn for<'a> Fn(
&'a borrow::Cow<[u8]>,
) -> gimli::EndianSlice<'a, gimli::RunTimeEndian> =
&|section| gimli::EndianSlice::new(section, endian);
let borrow_section = |section| gimli::EndianSlice::new(borrow::Cow::as_ref(section), endian);

// Load all of the sections.
let dwarf_sections = gimli::DwarfSections::load(&load_section)?;

// Create `EndianSlice`s for all of the sections.
let dwarf = dwarf_cow.borrow(&borrow_section);
let dwarf = dwarf_sections.borrow(borrow_section);

// Iterate over the compilation units.
let mut iter = dwarf.units();
Expand Down
12 changes: 1 addition & 11 deletions src/read/abbrev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,7 @@ impl<T> DebugAbbrev<T> {
///
/// This is useful when `R` implements `Reader` but `T` does not.
///
/// ## Example Usage
///
/// ```rust,no_run
/// # let load_section = || unimplemented!();
/// // Read the DWARF section into a `Vec` with whatever object loader you're using.
/// let owned_section: gimli::DebugAbbrev<Vec<u8>> = load_section();
/// // Create a reference to the DWARF section.
/// let section = owned_section.borrow(|section| {
/// gimli::EndianSlice::new(&section, gimli::LittleEndian)
/// });
/// ```
/// Used by `DwarfSections::borrow`.
pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAbbrev<R>
where
F: FnMut(&'a T) -> R,
Expand Down
12 changes: 1 addition & 11 deletions src/read/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,7 @@ impl<T> DebugAddr<T> {
///
/// This is useful when `R` implements `Reader` but `T` does not.
///
/// ## Example Usage
///
/// ```rust,no_run
/// # let load_section = || unimplemented!();
/// // Read the DWARF section into a `Vec` with whatever object loader you're using.
/// let owned_section: gimli::DebugAddr<Vec<u8>> = load_section();
/// // Create a reference to the DWARF section.
/// let section = owned_section.borrow(|section| {
/// gimli::EndianSlice::new(&section, gimli::LittleEndian)
/// });
/// ```
/// Used by `DwarfSections::borrow`.
pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAddr<R>
where
F: FnMut(&'a T) -> R,
Expand Down
12 changes: 1 addition & 11 deletions src/read/aranges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,7 @@ impl<T> DebugAranges<T> {
///
/// This is useful when `R` implements `Reader` but `T` does not.
///
/// ## Example Usage
///
/// ```rust,no_run
/// # let load_section = || unimplemented!();
/// // Read the DWARF section into a `Vec` with whatever object loader you're using.
/// let owned_section: gimli::DebugAranges<Vec<u8>> = load_section();
/// // Create a reference to the DWARF section.
/// let section = owned_section.borrow(|section| {
/// gimli::EndianSlice::new(&section, gimli::LittleEndian)
/// });
/// ```
/// Used by `DwarfSections::borrow`.
pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAranges<R>
where
F: FnMut(&'a T) -> R,
Expand Down
Loading
Loading