Skip to content

Commit

Permalink
Retrieve line information from .debug_line for childless entries
Browse files Browse the repository at this point in the history
  • Loading branch information
matux committed Apr 25, 2023
1 parent 4f17288 commit 8c8c207
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 101 deletions.
2 changes: 1 addition & 1 deletion ators/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn symbolicate(dwarf: &Dwarf, obj: &object::File, addrs: &[Addr], ctx: &Context)
let iter_symbols = addrs
.iter()
.map(|addr| {
let symbols = match atos_dwarf(dwarf, addr, ctx.include_inlined) {
let symbols = match atos_dwarf(dwarf, *addr, ctx.include_inlined) {
Err(Error::AddrNotFound(addr)) | Err(Error::AddrDebugInfoOffsetMissing(addr)) => {
atos_obj(obj, addr)?
}
Expand Down
6 changes: 6 additions & 0 deletions atorsl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,9 @@ pub(crate) type Entry<'abbrev, 'unit, 'input> = gimli::DebuggingInformationEntry
gimli::EndianSlice<'input, gimli::RunTimeEndian>,
usize,
>;

pub(crate) type IncompleteLineProgramRows<'input> = gimli::LineRows<
gimli::EndianSlice<'input, gimli::RunTimeEndian>,
gimli::IncompleteLineProgram<gimli::EndianSlice<'input, gimli::RunTimeEndian>, usize>,
usize,
>;
227 changes: 127 additions & 100 deletions atorsl/src/symbolicator.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{data::*, *};
use fallible_iterator::FallibleIterator;
use gimli::{
ColumnType, DW_AT_abstract_origin, DW_AT_call_column, DW_AT_call_file, DW_AT_call_line,
DW_AT_decl_column, DW_AT_decl_file, DW_AT_decl_line, DW_AT_high_pc, DW_AT_linkage_name,
DW_AT_low_pc, DW_AT_name, DW_AT_ranges, DebugInfoOffset,
ColumnType, DW_AT_abstract_origin, DW_AT_artificial, DW_AT_call_column, DW_AT_call_file,
DW_AT_call_line, DW_AT_decl_column, DW_AT_decl_file, DW_AT_decl_line, DW_AT_high_pc,
DW_AT_linkage_name, DW_AT_low_pc, DW_AT_name, DW_AT_ranges, DebugInfoOffset,
};
use itertools::Either;
use object::Object;
Expand All @@ -12,19 +12,15 @@ use std::{
path::{Path, PathBuf},
};

pub fn atos_dwarf(
dwarf: &Dwarf,
addr: &Addr,
include_inlined: bool,
) -> Result<Vec<Symbol>, Error> {
let unit = dwarf.unit_from_addr(addr)?;
pub fn atos_dwarf(dwarf: &Dwarf, addr: Addr, include_inlined: bool) -> Result<Vec<Symbol>, Error> {
let unit = dwarf.unit_from_addr(&addr)?;
let mut entries = unit.entries();

let builder = &mut CompilationUnitBuilder::default();

let (_, comp_unit) = entries
.next_dfs()?
.ok_or_else(|| Error::AddrNotFound(*addr))?;
.ok_or_else(|| Error::AddrNotFound(addr))?;

let comp_unit = comp_unit
.attrs()
Expand All @@ -47,28 +43,40 @@ pub fn atos_dwarf(
})?
.build()?;

let mut debug_line_rows = unit
.line_program
.clone()
.ok_or_else(|| Error::CompUnitLineProgramMissing(addr))?
.rows();

let mut symbols = Vec::default();

let subprogram = loop {
let (_, entry) = entries
.next_dfs()?
.ok_or_else(|| Error::AddrNotFound(*addr))?;

match entry.tag() {
gimli::DW_TAG_subprogram if dwarf.entry_contains(entry, addr, &unit) => {
symbols.push(Symbol {
name: demangler::demangle(&dwarf.entry_symbol(entry, &unit)?, comp_unit.lang),
loc: Either::Left(dwarf.entry_source_loc(entry, &comp_unit.dir, &unit)),
});
.ok_or_else(|| Error::AddrNotFound(addr))?;

break entry;
}
_ => continue,
if matches!(
entry.tag(),
gimli::DW_TAG_subprogram if dwarf.entry_contains(entry, &addr, &unit)
) {
break entry;
}
};

if include_inlined && subprogram.has_children() {
let mut parent = Option::<Entry>::None;
if !subprogram.has_children() {
symbols.push(Symbol {
addr,
name: demangler::demangle(&dwarf.entry_symbol(subprogram, &unit)?, comp_unit.lang),
loc: Either::Left(Some(dwarf.entry_debug_line(
&addr,
&comp_unit.dir,
&mut debug_line_rows,
&unit,
)?)),
});
} else if include_inlined && subprogram.has_children() {
let mut parent = subprogram.clone();
let mut depth = 0;

let last_child = loop {
Expand All @@ -83,80 +91,40 @@ pub fn atos_dwarf(
}

if child.tag() == gimli::DW_TAG_inlined_subroutine
&& dwarf.entry_contains(child, addr, &unit)
&& dwarf.entry_contains(child, &addr, &unit)
{
if let Some(ref parent) = parent {
symbols.insert(
0,
Symbol {
name: demangler::demangle(
&dwarf.entry_symbol(parent, &unit)?,
comp_unit.lang,
),
loc: Either::Left(dwarf.entry_source_loc(
child,
&comp_unit.dir,
&unit,
)),
},
);
}

parent = Some(child.clone());
symbols.insert(
0,
Symbol {
addr,
name: demangler::demangle(
&dwarf.entry_symbol(&parent, &unit)?,
comp_unit.lang,
),
loc: Either::Left(dwarf.entry_source_loc(child, &comp_unit.dir, &unit)),
},
);

parent = child.clone();
}
};

if let Some(last_child) = last_child {
let mut rows = unit
.line_program
.clone()
.ok_or_else(|| Error::CompUnitLineProgramMissing(*addr))?
.rows();

let source_loc = loop {
let (header, row) = rows
.next_row()?
.ok_or_else(|| Error::AddrLineInfoMissing(*addr))?;

if row.address() == addr {
let path = row
.file(header)
.ok_or_else(|| Error::AddrFileInfoMissing(*addr))
.and_then(|file| {
let mut path = match file.directory(header) {
Some(dir) if file.directory_index() != 0 => {
PathBuf::from(&*dwarf.attr_lossy_string(&unit, dir)?)
}
_ => comp_unit.dir,
};

path.push(&*dwarf.attr_lossy_string(&unit, file.path_name())?);

Ok(path)
})?;

break SourceLoc {
file: path,
line: row.line().map(|l| l.get()).unwrap_or_default() as u16,
col: match row.column() {
ColumnType::LeftEdge => Some(0),
ColumnType::Column(c) => Some(c.get() as u16),
},
};
}
};

symbols.insert(
0,
Symbol {
name: demangler::demangle(
&dwarf.entry_symbol(&last_child, &unit)?,
comp_unit.lang,
),
loc: Either::Left(Some(source_loc)),
},
);
}
symbols.insert(
0,
Symbol {
addr,
name: demangler::demangle(
&dwarf.entry_symbol(&last_child, &unit)?,
comp_unit.lang,
),
loc: Either::Left(Some(dwarf.entry_debug_line(
&addr,
&comp_unit.dir,
&mut debug_line_rows,
&unit,
)?)),
},
);
}

Ok(symbols)
Expand All @@ -169,6 +137,7 @@ pub fn atos_obj(obj: &object::File, addr: Addr) -> Result<Vec<Symbol>, Error> {
};

Ok(vec![Symbol {
addr: Addr::from(symbol.address()),
name: demangler::demangle(symbol.name(), None),
loc: Either::Right(addr - symbol.address()),
}])
Expand All @@ -177,7 +146,15 @@ pub fn atos_obj(obj: &object::File, addr: Addr) -> Result<Vec<Symbol>, Error> {
trait DwarfExt {
fn entry_name<'a>(&'a self, entry: &'a Entry, unit: &'a Unit) -> Result<Cow<str>, Error>;
fn entry_symbol<'a>(&'a self, entry: &'a Entry, unit: &'a Unit) -> Result<Cow<str>, Error>;

fn entry_source_loc(&self, entry: &Entry, path: &Path, unit: &Unit) -> Option<SourceLoc>;
fn entry_debug_line(
&self,
addr: &Addr,
comp_dir: &Path,
line_rows: &mut IncompleteLineProgramRows,
unit: &Unit,
) -> Result<SourceLoc, Error>;

fn entry_contains(&self, entry: &Entry, addr: &Addr, unit: &Unit) -> bool;
fn entry_pc_contains(&self, entry: &Entry, addr: &Addr) -> Option<bool>;
Expand Down Expand Up @@ -218,6 +195,47 @@ impl DwarfExt for Dwarf<'_> {
})
}

fn entry_debug_line(
&self,
addr: &Addr,
comp_dir: &Path,
line_rows: &mut IncompleteLineProgramRows,
unit: &Unit,
) -> Result<SourceLoc, Error> {
Ok(loop {
let (header, row) = line_rows
.next_row()?
.ok_or_else(|| Error::AddrLineInfoMissing(*addr))?;

if row.address() == addr {
let path = row
.file(header)
.ok_or_else(|| Error::AddrFileInfoMissing(*addr))
.and_then(|file| {
let mut path = match file.directory(header) {
Some(dir) if file.directory_index() != 0 => {
PathBuf::from(&*self.attr_lossy_string(unit, dir)?)
}
_ => comp_dir.to_path_buf(),
};

path.push(&*self.attr_lossy_string(unit, file.path_name())?);

Ok(path)
})?;

break SourceLoc {
file: path,
line: row.line().map(|l| l.get()).unwrap_or_default() as u16,
col: match row.column() {
ColumnType::LeftEdge => Some(0),
ColumnType::Column(c) => Some(c.get() as u16),
},
};
}
})
}

fn entry_source_loc(&self, entry: &Entry, path: &Path, unit: &Unit) -> Option<SourceLoc> {
let Some(AttrValue::FileIndex(offset)) = [DW_AT_decl_file, DW_AT_call_file]
.into_iter()
Expand All @@ -232,6 +250,7 @@ impl DwarfExt for Dwarf<'_> {

let header = unit.line_program.as_ref()?.header();
let file = header.file(offset)?;
let is_artificial = entry.attr_value(DW_AT_artificial) == Ok(Some(AttrValue::Flag(true)));

Some(SourceLoc {
file: PathBuf::from(
Expand All @@ -241,13 +260,21 @@ impl DwarfExt for Dwarf<'_> {
)
.join(&*self.attr_lossy_string(unit, file.path_name()).ok()?),

line: [DW_AT_decl_line, DW_AT_call_line]
.into_iter()
.find_map(|name| entry.attr_value(name).ok()??.u16_value())?,

col: [DW_AT_decl_column, DW_AT_call_column]
.into_iter()
.find_map(|name| entry.attr_value(name).ok()??.u16_value()),
line: if is_artificial {
0
} else {
[DW_AT_decl_line, DW_AT_call_line]
.into_iter()
.find_map(|name| entry.attr_value(name).ok()??.u16_value())?
},

col: if is_artificial {
Some(0)
} else {
[DW_AT_decl_column, DW_AT_call_column]
.into_iter()
.find_map(|name| entry.attr_value(name).ok()??.u16_value())
},
})
}

Expand Down

0 comments on commit 8c8c207

Please sign in to comment.