Skip to content

Commit

Permalink
Bump addr2line to 0.20 and support packaged split DWARF.
Browse files Browse the repository at this point in the history
  • Loading branch information
khuey committed Apr 18, 2023
1 parent b3e5bb8 commit 87bf6dc
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ rustc-serialize = { version = "0.3", optional = true }
# Optionally demangle C++ frames' symbols in backtraces.
cpp_demangle = { default-features = false, version = "0.4.0", optional = true, features = ["alloc"] }

addr2line = { version = "0.19.0", default-features = false }
addr2line = { version = "0.20.0", default-features = false }
miniz_oxide = { version = "0.6.0", default-features = false }

[dependencies.object]
Expand Down
53 changes: 50 additions & 3 deletions src/symbolize/gimli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,15 @@ impl Mapping {
struct Context<'a> {
dwarf: addr2line::Context<EndianSlice<'a, Endian>>,
object: Object<'a>,
package: Option<gimli::DwarfPackage<EndianSlice<'a, Endian>>>,
}

impl<'data> Context<'data> {
fn new(
stash: &'data Stash,
object: Object<'data>,
sup: Option<Object<'data>>,
dwp: Option<Object<'data>>,
) -> Option<Context<'data>> {
let mut sections = gimli::Dwarf::load(|id| -> Result<_, ()> {
let data = object.section(stash, id.name()).unwrap_or(&[]);
Expand All @@ -129,7 +131,52 @@ impl<'data> Context<'data> {
}
let dwarf = addr2line::Context::from_dwarf(sections).ok()?;

Some(Context { dwarf, object })
let mut package = None;
if let Some(dwp) = dwp {
package = Some(
gimli::DwarfPackage::load(
|id| -> Result<_, gimli::Error> {
let data = dwp.section(stash, id.dwo_name().unwrap()).unwrap_or(&[]);
Ok(EndianSlice::new(data, Endian))
},
EndianSlice::new(&[], Endian),
)
.ok()?,
);
}

Some(Context {
dwarf,
object,
package,
})
}

fn find_frames(
&'_ self,
probe: u64,
) -> gimli::Result<addr2line::FrameIter<'_, EndianSlice<'data, Endian>>> {
use addr2line::{LookupContinuation, LookupResult};
use alloc::sync::Arc;

let mut l = self.dwarf.find_frames(probe);
loop {
let (load, continuation) = match l {
LookupResult::Output(output) => break output,
LookupResult::Load { load, continuation } => (load, continuation),
};

let mut r: Option<Arc<gimli::Dwarf<_>>> = None;
if let Some(dwp) = self.package.as_ref() {
if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) {
r = Some(Arc::new(cu));
}
}

// TODO: support unpacked split DWARF.

l = continuation.resume(r);
}
}
}

Expand Down Expand Up @@ -358,7 +405,7 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol))
None => return,
};
let mut any_frames = false;
if let Ok(mut frames) = cx.dwarf.find_frames(addr as u64) {
if let Ok(mut frames) = cx.find_frames(addr as u64) {
while let Ok(Some(frame)) = frames.next() {
any_frames = true;
let name = match frame.function {
Expand All @@ -374,7 +421,7 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol))
}
if !any_frames {
if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) {
if let Ok(mut frames) = object_cx.dwarf.find_frames(object_addr) {
if let Ok(mut frames) = object_cx.find_frames(object_addr) {
while let Ok(Some(frame)) = frames.next() {
any_frames = true;
call(Symbol::Frame {
Expand Down
43 changes: 35 additions & 8 deletions src/symbolize/gimli/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,26 @@ impl Mapping {

// Try to locate an external debug file using the build ID.
if let Some(path_debug) = object.build_id().and_then(locate_build_id) {
if let Some(mapping) = Mapping::new_debug(path_debug, None) {
if let Some(mapping) = Mapping::new_debug(path, path_debug, None) {
return Some(Either::A(mapping));
}
}

// Try to locate an external debug file using the GNU debug link section.
if let Some((path_debug, crc)) = object.gnu_debuglink_path(path) {
if let Some(mapping) = Mapping::new_debug(path_debug, Some(crc)) {
if let Some(mapping) = Mapping::new_debug(path, path_debug, Some(crc)) {
return Some(Either::A(mapping));
}
}

Context::new(stash, object, None).map(Either::B)
let dwp = Mapping::load_dwarf_package(path, stash);

Context::new(stash, object, None, dwp).map(Either::B)
})
}

/// Load debuginfo from an external debug file.
fn new_debug(path: PathBuf, crc: Option<u32>) -> Option<Mapping> {
fn new_debug(original_path: &Path, path: PathBuf, crc: Option<u32>) -> Option<Mapping> {
let map = super::mmap(&path)?;
Mapping::mk(map, |map, stash| {
let object = Object::parse(&map)?;
Expand All @@ -51,20 +53,45 @@ impl Mapping {
}

// Try to locate a supplementary object file.
let mut sup = None;
if let Some((path_sup, build_id_sup)) = object.gnu_debugaltlink_path(&path) {
if let Some(map_sup) = super::mmap(&path_sup) {
let map_sup = stash.set_mmap_aux(map_sup);
if let Some(sup) = Object::parse(map_sup) {
if sup.build_id() == Some(build_id_sup) {
return Context::new(stash, object, Some(sup));
if let Some(sup_) = Object::parse(map_sup) {
if sup_.build_id() == Some(build_id_sup) {
sup = Some(sup_);
}
}
}
}

Context::new(stash, object, None)
let dwp = Mapping::load_dwarf_package(original_path, stash);

Context::new(stash, object, sup, dwp)
})
}

/// Try to locate a DWARF package file.
fn load_dwarf_package<'data>(path: &Path, stash: &'data Stash) -> Option<Object<'data>> {
let mut path_dwp = path.to_path_buf();
let dwp_extension = path
.extension()
.map(|previous_extension| {
let mut previous_extension = previous_extension.to_os_string();
previous_extension.push(".dwp");
previous_extension
})
.unwrap_or_else(|| "dwp".into());
path_dwp.set_extension(dwp_extension);
if let Some(map_dwp) = super::mmap(&path_dwp) {
let map_dwp = stash.set_mmap_dwp(map_dwp);
if let Some(dwp_) = Object::parse(map_dwp) {
return Some(dwp_);
}
}

None
}
}

struct ParsedSym {
Expand Down
19 changes: 19 additions & 0 deletions src/symbolize/gimli/stash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ use core::cell::UnsafeCell;
pub struct Stash {
buffers: UnsafeCell<Vec<Vec<u8>>>,
mmap_aux: UnsafeCell<Option<Mmap>>,
mmap_dwp: UnsafeCell<Option<Mmap>>,
}

impl Stash {
pub fn new() -> Stash {
Stash {
buffers: UnsafeCell::new(Vec::new()),
mmap_aux: UnsafeCell::new(None),
mmap_dwp: UnsafeCell::new(None),
}
}

Expand Down Expand Up @@ -49,4 +51,21 @@ impl Stash {
mmap_aux.as_ref().unwrap()
}
}

/// Stores a `Mmap` for the lifetime of this `Stash`, returning a pointer
/// which is scoped to just this lifetime.
pub fn set_mmap_dwp(&self, map: Mmap) -> &[u8] {
// SAFETY: this is the only location for a mutable pointer to
// `mmap_dwp`, and this structure isn't threadsafe to shared across
// threads either. This also is careful to store at most one `mmap_dwp`
// since overwriting a previous one would invalidate the previous
// pointer. Given that though we can safely return a pointer to our
// interior-owned contents.
unsafe {
let mmap_dwp = &mut *self.mmap_dwp.get();
assert!(mmap_dwp.is_none());
*mmap_dwp = Some(map);
mmap_dwp.as_ref().unwrap()
}
}
}

0 comments on commit 87bf6dc

Please sign in to comment.