//! A simple example of parsing `.debug_line`. use object::{Object, ObjectSection}; use std::{borrow, env, fs, path}; 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(); } } 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, 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(&[][..])), } }; // 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); // Create `EndianSlice`s for all of the sections. let dwarf = dwarf_cow.borrow(&borrow_section); // Iterate over the compilation units. let mut iter = dwarf.units(); while let Some(header) = iter.next()? { println!( "Line number info for unit at <.debug_info+0x{:x}>", header.offset().as_debug_info_offset().unwrap().0 ); let unit = dwarf.unit(header)?; // Get the line program for the compilation unit. if let Some(program) = unit.line_program.clone() { let comp_dir = if let Some(ref dir) = unit.comp_dir { path::PathBuf::from(dir.to_string_lossy().into_owned()) } else { path::PathBuf::new() }; // Iterate over the line program rows. let mut rows = program.rows(); while let Some((header, row)) = rows.next_row()? { if row.end_sequence() { // End of sequence indicates a possible gap in addresses. println!("{:x} end-sequence", row.address()); } else { // Determine the path. Real applications should cache this for performance. let mut path = path::PathBuf::new(); if let Some(file) = row.file(header) { path = comp_dir.clone(); // The directory index 0 is defined to correspond to the compilation unit directory. if file.directory_index() != 0 { if let Some(dir) = file.directory(header) { path.push( dwarf.attr_string(&unit, dir)?.to_string_lossy().as_ref(), ); } } path.push( dwarf .attr_string(&unit, file.path_name())? .to_string_lossy() .as_ref(), ); } // Determine line/column. DWARF line/column is never 0, so we use that // but other applications may want to display this differently. let line = match row.line() { Some(line) => line.get(), None => 0, }; let column = match row.column() { gimli::ColumnType::LeftEdge => 0, gimli::ColumnType::Column(column) => column.get(), }; println!("{:x} {}:{}:{}", row.address(), path.display(), line, column); } } } } Ok(()) }