From 075ee512324df66bd5df6f358174244498a85756 Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Thu, 26 Nov 2020 19:17:16 +0100 Subject: [PATCH] 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. --- symbolic-debuginfo/src/dwarf.rs | 67 ++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/symbolic-debuginfo/src/dwarf.rs b/symbolic-debuginfo/src/dwarf.rs index 602c2cef7..338971f48 100644 --- a/symbolic-debuginfo/src/dwarf.rs +++ b/symbolic-debuginfo/src/dwarf.rs @@ -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}; @@ -317,7 +317,7 @@ impl<'d, 'a> UnitRef<'d, 'a> { /// abbrev can only be temporarily accessed in the callback. fn resolve_reference(&self, attr: Attribute<'d>, f: F) -> Result, DwarfError> where - F: FnOnce(UnitRef<'d, '_>, &Die<'d, '_>) -> Result, DwarfError>, + F: FnOnce(Self, &Die<'d, '_>) -> Result, DwarfError>, { let (unit, offset) = match attr.value() { AttributeValue::UnitRef(offset) => (*self, offset), @@ -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>, DwarfError> { + fn resolve_function_name( + &self, + entry: &Die<'d, '_>, + language: Language, + ) -> Result>, DwarfError> { let mut attrs = entry.attrs(); let mut fallback_name = None; let mut reference_target = None; @@ -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); @@ -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) } @@ -387,6 +391,7 @@ struct DwarfUnit<'d, 'a> { inner: UnitRef<'d, 'a>, language: Language, line_program: Option>, + prefer_dwarf_names: bool, } impl<'d, 'a> DwarfUnit<'d, 'a> { @@ -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, })) } @@ -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(&self, range: R) -> Option> + where + R: RangeBounds, + { + 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> { + self.inner + .resolve_function_name(entry, self.language) + .ok() + .flatten() + } + /// Collects all functions within this compilation unit. fn functions(&self, range_buf: &mut Vec) -> Result>, DwarfError> { let mut depth = 0; @@ -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