Skip to content

Commit 075ee51

Browse files
authored
fix(debuginfo): Prefer DWARF names for Dart functions (#293)
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.
1 parent be65658 commit 075ee51

File tree

1 file changed

+45
-22
lines changed

1 file changed

+45
-22
lines changed

symbolic-debuginfo/src/dwarf.rs

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
use std::borrow::Cow;
1111
use std::fmt;
1212
use std::marker::PhantomData;
13-
use std::ops::Deref;
13+
use std::ops::{Deref, RangeBounds};
1414

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

339339
/// Resolves the function name of a debug entry.
340-
fn resolve_function_name(&self, entry: &Die<'d, '_>) -> Result<Option<Name<'d>>, DwarfError> {
340+
fn resolve_function_name(
341+
&self,
342+
entry: &Die<'d, '_>,
343+
language: Language,
344+
) -> Result<Option<Name<'d>>, DwarfError> {
341345
let mut attrs = entry.attrs();
342346
let mut fallback_name = None;
343347
let mut reference_target = None;
@@ -348,7 +352,7 @@ impl<'d, 'a> UnitRef<'d, 'a> {
348352
constants::DW_AT_linkage_name | constants::DW_AT_MIPS_linkage_name => {
349353
return Ok(self
350354
.string_value(attr.value())
351-
.map(|n| Name::new(n, NameMangling::Mangled, Language::Unknown)));
355+
.map(|n| Name::new(n, NameMangling::Mangled, language)));
352356
}
353357
constants::DW_AT_name => {
354358
fallback_name = Some(attr);
@@ -363,14 +367,14 @@ impl<'d, 'a> UnitRef<'d, 'a> {
363367
if let Some(attr) = fallback_name {
364368
return Ok(self
365369
.string_value(attr.value())
366-
.map(|n| Name::new(n, NameMangling::Unmangled, Language::Unknown)));
370+
.map(|n| Name::new(n, NameMangling::Unmangled, language)));
367371
}
368372

369373
if let Some(attr) = reference_target {
370374
return self.resolve_reference(attr, |ref_unit, ref_entry| {
371375
if self.unit.offset != ref_unit.unit.offset || entry.offset() != ref_entry.offset()
372376
{
373-
ref_unit.resolve_function_name(ref_entry)
377+
ref_unit.resolve_function_name(ref_entry, language)
374378
} else {
375379
Ok(None)
376380
}
@@ -387,6 +391,7 @@ struct DwarfUnit<'d, 'a> {
387391
inner: UnitRef<'d, 'a>,
388392
language: Language,
389393
line_program: Option<DwarfLineProgram<'d>>,
394+
prefer_dwarf_names: bool,
390395
}
391396

392397
impl<'d, 'a> DwarfUnit<'d, 'a> {
@@ -419,10 +424,20 @@ impl<'d, 'a> DwarfUnit<'d, 'a> {
419424
None => None,
420425
};
421426

427+
let producer = match entry.attr_value(constants::DW_AT_producer)? {
428+
Some(AttributeValue::String(string)) => Some(string),
429+
_ => None,
430+
};
431+
432+
// Trust the symbol table more to contain accurate mangled names. However, since Dart's name
433+
// mangling is lossy, we need to load the demangled name instead.
434+
let prefer_dwarf_names = producer.as_deref() == Some(b"Dart VM");
435+
422436
Ok(Some(DwarfUnit {
423437
inner: UnitRef { info, unit },
424438
language,
425439
line_program,
440+
prefer_dwarf_names,
426441
}))
427442
}
428443

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

639+
/// Resolves the name of a function from the symbol table.
640+
fn resolve_symbol_name<R>(&self, range: R) -> Option<Name<'d>>
641+
where
642+
R: RangeBounds<u64>,
643+
{
644+
let symbol = self.inner.info.symbol_map.lookup_range(range)?;
645+
let name = symbol.name.clone()?;
646+
Some(Name::new(name, NameMangling::Mangled, self.language))
647+
}
648+
649+
/// Resolves the name of a function from DWARF debug information.
650+
fn resolve_dwarf_name(&self, entry: &Die<'d, '_>) -> Option<Name<'d>> {
651+
self.inner
652+
.resolve_function_name(entry, self.language)
653+
.ok()
654+
.flatten()
655+
}
656+
624657
/// Collects all functions within this compilation unit.
625658
fn functions(&self, range_buf: &mut Vec<Range>) -> Result<Vec<Function<'d>>, DwarfError> {
626659
let mut depth = 0;
@@ -684,25 +717,15 @@ impl<'d, 'a> DwarfUnit<'d, 'a> {
684717
//
685718
// XXX: Maybe we should actually parse the ranges in the resolve function and always
686719
// look at the symbol table based on the start of the DIE range.
687-
let symbol_name = if !inline {
688-
self.inner
689-
.info
690-
.symbol_map
691-
.lookup_range(function_address..function_end)
692-
.and_then(|symbol| {
693-
symbol
694-
.name
695-
.clone()
696-
.map(|n| Name::new(n, NameMangling::Mangled, self.language))
697-
})
698-
} else {
720+
let symbol_name = if self.prefer_dwarf_names || inline {
699721
None
722+
} else {
723+
self.resolve_symbol_name(function_address..function_end)
700724
};
701725

702-
let mut name = symbol_name
703-
.or_else(|| self.inner.resolve_function_name(entry).ok().flatten())
704-
.unwrap_or_else(|| Name::from(""));
705-
name.set_language(self.language);
726+
let name = symbol_name
727+
.or_else(|| self.resolve_dwarf_name(entry))
728+
.unwrap_or_else(|| Name::new("", NameMangling::Unmangled, self.language));
706729

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

0 commit comments

Comments
 (0)