Skip to content

Commit

Permalink
Support unpackaged split DWARF.
Browse files Browse the repository at this point in the history
  • Loading branch information
khuey committed Apr 20, 2023
1 parent 6b06452 commit 76c1f20
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 58 deletions.
47 changes: 23 additions & 24 deletions src/symbolize/gimli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct Mapping {
// 'static lifetime is a lie to hack around lack of support for self-referential structs.
cx: Context<'static>,
_map: Mmap,
_stash: Stash,
stash: Stash,
}

enum Either<A, B> {
Expand Down Expand Up @@ -97,7 +97,7 @@ impl Mapping {
// only borrow `map` and `stash` and we're preserving them below.
cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) },
_map: data,
_stash: stash,
stash: stash,
})
}
}
Expand Down Expand Up @@ -136,7 +136,10 @@ impl<'data> Context<'data> {
package = Some(
gimli::DwarfPackage::load(
|id| -> Result<_, gimli::Error> {
let data = dwp.section(stash, id.dwo_name().unwrap()).unwrap_or(&[]);
let data = id
.dwo_name()
.and_then(|name| dwp.section(stash, name))
.unwrap_or(&[]);
Ok(EndianSlice::new(data, Endian))
},
EndianSlice::new(&[], Endian),
Expand All @@ -154,10 +157,10 @@ impl<'data> Context<'data> {

fn find_frames(
&'_ self,
stash: &'data Stash,
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 {
Expand All @@ -166,16 +169,7 @@ impl<'data> Context<'data> {
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);
l = continuation.resume(handle_split_dwarf(self.package.as_ref(), stash, load));
}
}
}
Expand All @@ -189,18 +183,18 @@ fn mmap(path: &Path) -> Option<Mmap> {
cfg_if::cfg_if! {
if #[cfg(windows)] {
mod coff;
use self::coff::Object;
use self::coff::{handle_split_dwarf, Object};
} else if #[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
))] {
mod macho;
use self::macho::Object;
use self::macho::{handle_split_dwarf, Object};
} else {
mod elf;
use self::elf::Object;
use self::elf::{handle_split_dwarf, Object};
}
}

Expand Down Expand Up @@ -349,7 +343,7 @@ impl Cache {
.next()
}

fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<&'a mut Context<'a>> {
fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<(&'a mut Context<'a>, &'a Stash)> {
let idx = self.mappings.iter().position(|(idx, _)| *idx == lib);

// Invariant: after this conditional completes without early returning
Expand All @@ -375,10 +369,15 @@ impl Cache {
self.mappings.insert(0, (lib, mapping));
}

let cx: &'a mut Context<'static> = &mut self.mappings[0].1.cx;
let mapping = &mut self.mappings[0].1;
let cx: &'a mut Context<'static> = &mut mapping.cx;
let stash: &'a Stash = &mapping.stash;
// don't leak the `'static` lifetime, make sure it's scoped to just
// ourselves
Some(unsafe { mem::transmute::<&'a mut Context<'static>, &'a mut Context<'a>>(cx) })
Some((
unsafe { mem::transmute::<&'a mut Context<'static>, &'a mut Context<'a>>(cx) },
stash,
))
}
}

Expand All @@ -400,12 +399,12 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol))

// Finally, get a cached mapping or create a new mapping for this file, and
// evaluate the DWARF info to find the file/line/name for this address.
let cx = match cache.mapping_for_lib(lib) {
Some(cx) => cx,
let (cx, stash) = match cache.mapping_for_lib(lib) {
Some((cx, stash)) => (cx, stash),
None => return,
};
let mut any_frames = false;
if let Ok(mut frames) = cx.find_frames(addr as u64) {
if let Ok(mut frames) = cx.find_frames(stash, addr as u64) {
while let Ok(Some(frame)) = frames.next() {
any_frames = true;
let name = match frame.function {
Expand All @@ -421,7 +420,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.find_frames(object_addr) {
if let Ok(mut frames) = object_cx.find_frames(stash, object_addr) {
while let Ok(Some(frame)) = frames.next() {
any_frames = true;
call(Symbol::Frame {
Expand Down
8 changes: 8 additions & 0 deletions src/symbolize/gimli/coff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,11 @@ impl<'a> Object<'a> {
None
}
}

pub(super) fn handle_split_dwarf<'data>(
package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
stash: &'data Stash,
load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
None
}
57 changes: 54 additions & 3 deletions src/symbolize/gimli/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use super::mystd::fs;
use super::mystd::os::unix::ffi::{OsStrExt, OsStringExt};
use super::mystd::path::{Path, PathBuf};
use super::Either;
use super::{Context, Mapping, Stash, Vec};
use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash, Vec};
use alloc::sync::Arc;
use core::convert::{TryFrom, TryInto};
use core::str;
use object::elf::{ELFCOMPRESS_ZLIB, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED};
Expand Down Expand Up @@ -56,7 +57,7 @@ impl Mapping {
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);
let map_sup = stash.cache_mmap(map_sup);
if let Some(sup_) = Object::parse(map_sup) {
if sup_.build_id() == Some(build_id_sup) {
sup = Some(sup_);
Expand Down Expand Up @@ -84,7 +85,7 @@ impl Mapping {
.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);
let map_dwp = stash.cache_mmap(map_dwp);
if let Some(dwp_) = Object::parse(map_dwp) {
return Some(dwp_);
}
Expand Down Expand Up @@ -448,3 +449,53 @@ fn locate_debugaltlink(path: &Path, filename: &[u8], build_id: &[u8]) -> Option<

locate_build_id(build_id)
}

fn convert_path<R: gimli::Reader>(r: &R) -> Result<PathBuf, gimli::Error> {
let bytes = r.to_slice()?;
Ok(PathBuf::from(OsStr::from_bytes(&bytes)))
}

pub(super) fn handle_split_dwarf<'data>(
package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
stash: &'data Stash,
load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
if let Some(dwp) = package.as_ref() {
if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) {
return Some(Arc::new(cu));
}
}

let mut path = PathBuf::new();
if let Some(p) = load.comp_dir.as_ref() {
if let Ok(p) = convert_path(p) {
path.push(p);
}
}

if let Some(p) = load.path.as_ref() {
if let Ok(p) = convert_path(p) {
path.push(p);
}
}

if let Some(map_dwo) = super::mmap(&path) {
let map_dwo = stash.cache_mmap(map_dwo);
if let Some(dwo) = Object::parse(map_dwo) {
return gimli::Dwarf::load(|id| -> Result<_, ()> {
let data = id
.dwo_name()
.and_then(|name| dwo.section(stash, name))
.unwrap_or(&[]);
Ok(EndianSlice::new(data, Endian))
})
.ok()
.map(|mut dwo_dwarf| {
dwo_dwarf.make_dwo(&load.parent);
Arc::new(dwo_dwarf)
});
}
}

None
}
8 changes: 8 additions & 0 deletions src/symbolize/gimli/macho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,11 @@ fn split_archive_path(path: &[u8]) -> Option<(&[u8], &[u8])> {
let (archive, rest) = path.split_at(index);
Some((archive, &rest[1..]))
}

pub(super) fn handle_split_dwarf<'data>(
package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
stash: &'data Stash,
load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
None
}
41 changes: 10 additions & 31 deletions src/symbolize/gimli/stash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@ use core::cell::UnsafeCell;
/// A simple arena allocator for byte buffers.
pub struct Stash {
buffers: UnsafeCell<Vec<Vec<u8>>>,
mmap_aux: UnsafeCell<Option<Mmap>>,
mmap_dwp: UnsafeCell<Option<Mmap>>,
mmaps: UnsafeCell<Vec<Mmap>>,
}

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

Expand All @@ -37,35 +35,16 @@ impl Stash {

/// Stores a `Mmap` for the lifetime of this `Stash`, returning a pointer
/// which is scoped to just this lifetime.
pub fn set_mmap_aux(&self, map: Mmap) -> &[u8] {
pub fn cache_mmap(&self, map: Mmap) -> &[u8] {
// SAFETY: this is the only location for a mutable pointer to
// `mmap_aux`, and this structure isn't threadsafe to shared across
// threads either. This also is careful to store at most one `mmap_aux`
// since overwriting a previous one would invalidate the previous
// pointer. Given that though we can safely return a pointer to our
// interior-owned contents.
// `mmaps`, and this structure isn't threadsafe to shared across
// threads either. We also never remove elements from `self.mmaps`,
// so a reference to the data inside the map will live as long as
// `self` does.
unsafe {
let mmap_aux = &mut *self.mmap_aux.get();
assert!(mmap_aux.is_none());
*mmap_aux = Some(map);
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()
let mmaps = &mut *self.mmaps.get();
mmaps.push(map);
mmaps.last().unwrap()
}
}
}

0 comments on commit 76c1f20

Please sign in to comment.