Skip to content

Commit

Permalink
fix(debuginfo): Prefer DWARF names for Dart functions (#293)
Browse files Browse the repository at this point in the history
We generally trust the symbol table to contain more accurate names than
DW_AT_name and other attributes on DIEs in DWARF debug information. Some
C++ compilers have been known to output truncated or simplified names
into these attributes, whereas the symbol table always contains
accurately mangled names.

The Dart compiler is an exception to this case. Its name mangling
appears to be lossy and there is no generally available demangler. Thus,
we need to prefer the demangled DW_AT_name.
  • Loading branch information
jan-auer committed Nov 26, 2020
1 parent be65658 commit 075ee51
Showing 1 changed file with 45 additions and 22 deletions.
67 changes: 45 additions & 22 deletions symbolic-debuginfo/src/dwarf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;
use std::ops::Deref;
use std::ops::{Deref, RangeBounds};

use fallible_iterator::FallibleIterator;
use gimli::read::{AttributeValue, Range};
Expand Down Expand Up @@ -317,7 +317,7 @@ impl<'d, 'a> UnitRef<'d, 'a> {
/// abbrev can only be temporarily accessed in the callback.
fn resolve_reference<T, F>(&self, attr: Attribute<'d>, f: F) -> Result<Option<T>, DwarfError>
where
F: FnOnce(UnitRef<'d, '_>, &Die<'d, '_>) -> Result<Option<T>, DwarfError>,
F: FnOnce(Self, &Die<'d, '_>) -> Result<Option<T>, DwarfError>,
{
let (unit, offset) = match attr.value() {
AttributeValue::UnitRef(offset) => (*self, offset),
Expand All @@ -337,7 +337,11 @@ impl<'d, 'a> UnitRef<'d, 'a> {
}

/// Resolves the function name of a debug entry.
fn resolve_function_name(&self, entry: &Die<'d, '_>) -> Result<Option<Name<'d>>, DwarfError> {
fn resolve_function_name(
&self,
entry: &Die<'d, '_>,
language: Language,
) -> Result<Option<Name<'d>>, DwarfError> {
let mut attrs = entry.attrs();
let mut fallback_name = None;
let mut reference_target = None;
Expand All @@ -348,7 +352,7 @@ impl<'d, 'a> UnitRef<'d, 'a> {
constants::DW_AT_linkage_name | constants::DW_AT_MIPS_linkage_name => {
return Ok(self
.string_value(attr.value())
.map(|n| Name::new(n, NameMangling::Mangled, Language::Unknown)));
.map(|n| Name::new(n, NameMangling::Mangled, language)));
}
constants::DW_AT_name => {
fallback_name = Some(attr);
Expand All @@ -363,14 +367,14 @@ impl<'d, 'a> UnitRef<'d, 'a> {
if let Some(attr) = fallback_name {
return Ok(self
.string_value(attr.value())
.map(|n| Name::new(n, NameMangling::Unmangled, Language::Unknown)));
.map(|n| Name::new(n, NameMangling::Unmangled, language)));
}

if let Some(attr) = reference_target {
return self.resolve_reference(attr, |ref_unit, ref_entry| {
if self.unit.offset != ref_unit.unit.offset || entry.offset() != ref_entry.offset()
{
ref_unit.resolve_function_name(ref_entry)
ref_unit.resolve_function_name(ref_entry, language)
} else {
Ok(None)
}
Expand All @@ -387,6 +391,7 @@ struct DwarfUnit<'d, 'a> {
inner: UnitRef<'d, 'a>,
language: Language,
line_program: Option<DwarfLineProgram<'d>>,
prefer_dwarf_names: bool,
}

impl<'d, 'a> DwarfUnit<'d, 'a> {
Expand Down Expand Up @@ -419,10 +424,20 @@ impl<'d, 'a> DwarfUnit<'d, 'a> {
None => None,
};

let producer = match entry.attr_value(constants::DW_AT_producer)? {
Some(AttributeValue::String(string)) => Some(string),
_ => None,
};

// Trust the symbol table more to contain accurate mangled names. However, since Dart's name
// mangling is lossy, we need to load the demangled name instead.
let prefer_dwarf_names = producer.as_deref() == Some(b"Dart VM");

Ok(Some(DwarfUnit {
inner: UnitRef { info, unit },
language,
line_program,
prefer_dwarf_names,
}))
}

Expand Down Expand Up @@ -621,6 +636,24 @@ impl<'d, 'a> DwarfUnit<'d, 'a> {
.map(|file| self.file_info(line_program, file))
}

/// Resolves the name of a function from the symbol table.
fn resolve_symbol_name<R>(&self, range: R) -> Option<Name<'d>>
where
R: RangeBounds<u64>,
{
let symbol = self.inner.info.symbol_map.lookup_range(range)?;
let name = symbol.name.clone()?;
Some(Name::new(name, NameMangling::Mangled, self.language))
}

/// Resolves the name of a function from DWARF debug information.
fn resolve_dwarf_name(&self, entry: &Die<'d, '_>) -> Option<Name<'d>> {
self.inner
.resolve_function_name(entry, self.language)
.ok()
.flatten()
}

/// Collects all functions within this compilation unit.
fn functions(&self, range_buf: &mut Vec<Range>) -> Result<Vec<Function<'d>>, DwarfError> {
let mut depth = 0;
Expand Down Expand Up @@ -684,25 +717,15 @@ impl<'d, 'a> DwarfUnit<'d, 'a> {
//
// XXX: Maybe we should actually parse the ranges in the resolve function and always
// look at the symbol table based on the start of the DIE range.
let symbol_name = if !inline {
self.inner
.info
.symbol_map
.lookup_range(function_address..function_end)
.and_then(|symbol| {
symbol
.name
.clone()
.map(|n| Name::new(n, NameMangling::Mangled, self.language))
})
} else {
let symbol_name = if self.prefer_dwarf_names || inline {
None
} else {
self.resolve_symbol_name(function_address..function_end)
};

let mut name = symbol_name
.or_else(|| self.inner.resolve_function_name(entry).ok().flatten())
.unwrap_or_else(|| Name::from(""));
name.set_language(self.language);
let name = symbol_name
.or_else(|| self.resolve_dwarf_name(entry))
.unwrap_or_else(|| Name::new("", NameMangling::Unmangled, self.language));

// Avoid constant allocations by collecting repeatedly into the same buffer and
// draining the results out of it. This keeps the original buffer allocated and
Expand Down

0 comments on commit 075ee51

Please sign in to comment.