Skip to content

Commit 6592a51

Browse files
committed
Support unpackaged split DWARF.
1 parent 7536d43 commit 6592a51

File tree

5 files changed

+103
-58
lines changed

5 files changed

+103
-58
lines changed

src/symbolize/gimli.rs

+23-24
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ struct Mapping {
5858
// 'static lifetime is a lie to hack around lack of support for self-referential structs.
5959
cx: Context<'static>,
6060
_map: Mmap,
61-
_stash: Stash,
61+
stash: Stash,
6262
}
6363

6464
enum Either<A, B> {
@@ -97,7 +97,7 @@ impl Mapping {
9797
// only borrow `map` and `stash` and we're preserving them below.
9898
cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) },
9999
_map: data,
100-
_stash: stash,
100+
stash: stash,
101101
})
102102
}
103103
}
@@ -136,7 +136,10 @@ impl<'data> Context<'data> {
136136
package = Some(
137137
gimli::DwarfPackage::load(
138138
|id| -> Result<_, gimli::Error> {
139-
let data = dwp.section(stash, id.dwo_name().unwrap()).unwrap_or(&[]);
139+
let data = id
140+
.dwo_name()
141+
.and_then(|name| dwp.section(stash, name))
142+
.unwrap_or(&[]);
140143
Ok(EndianSlice::new(data, Endian))
141144
},
142145
EndianSlice::new(&[], Endian),
@@ -154,10 +157,10 @@ impl<'data> Context<'data> {
154157

155158
fn find_frames(
156159
&'_ self,
160+
stash: &'data Stash,
157161
probe: u64,
158162
) -> gimli::Result<addr2line::FrameIter<'_, EndianSlice<'data, Endian>>> {
159163
use addr2line::{LookupContinuation, LookupResult};
160-
use alloc::sync::Arc;
161164

162165
let mut l = self.dwarf.find_frames(probe);
163166
loop {
@@ -166,16 +169,7 @@ impl<'data> Context<'data> {
166169
LookupResult::Load { load, continuation } => (load, continuation),
167170
};
168171

169-
let mut r: Option<Arc<gimli::Dwarf<_>>> = None;
170-
if let Some(dwp) = self.package.as_ref() {
171-
if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) {
172-
r = Some(Arc::new(cu));
173-
}
174-
}
175-
176-
// TODO: support unpacked split DWARF.
177-
178-
l = continuation.resume(r);
172+
l = continuation.resume(handle_split_dwarf(self.package.as_ref(), stash, load));
179173
}
180174
}
181175
}
@@ -189,18 +183,18 @@ fn mmap(path: &Path) -> Option<Mmap> {
189183
cfg_if::cfg_if! {
190184
if #[cfg(windows)] {
191185
mod coff;
192-
use self::coff::Object;
186+
use self::coff::{handle_split_dwarf, Object};
193187
} else if #[cfg(any(
194188
target_os = "macos",
195189
target_os = "ios",
196190
target_os = "tvos",
197191
target_os = "watchos",
198192
))] {
199193
mod macho;
200-
use self::macho::Object;
194+
use self::macho::{handle_split_dwarf, Object};
201195
} else {
202196
mod elf;
203-
use self::elf::Object;
197+
use self::elf::{handle_split_dwarf, Object};
204198
}
205199
}
206200

@@ -349,7 +343,7 @@ impl Cache {
349343
.next()
350344
}
351345

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

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

378-
let cx: &'a mut Context<'static> = &mut self.mappings[0].1.cx;
372+
let mapping = &mut self.mappings[0].1;
373+
let cx: &'a mut Context<'static> = &mut mapping.cx;
374+
let stash: &'a Stash = &mapping.stash;
379375
// don't leak the `'static` lifetime, make sure it's scoped to just
380376
// ourselves
381-
Some(unsafe { mem::transmute::<&'a mut Context<'static>, &'a mut Context<'a>>(cx) })
377+
Some((
378+
unsafe { mem::transmute::<&'a mut Context<'static>, &'a mut Context<'a>>(cx) },
379+
stash,
380+
))
382381
}
383382
}
384383

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

401400
// Finally, get a cached mapping or create a new mapping for this file, and
402401
// evaluate the DWARF info to find the file/line/name for this address.
403-
let cx = match cache.mapping_for_lib(lib) {
404-
Some(cx) => cx,
402+
let (cx, stash) = match cache.mapping_for_lib(lib) {
403+
Some((cx, stash)) => (cx, stash),
405404
None => return,
406405
};
407406
let mut any_frames = false;
408-
if let Ok(mut frames) = cx.find_frames(addr as u64) {
407+
if let Ok(mut frames) = cx.find_frames(stash, addr as u64) {
409408
while let Ok(Some(frame)) = frames.next() {
410409
any_frames = true;
411410
let name = match frame.function {
@@ -421,7 +420,7 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol))
421420
}
422421
if !any_frames {
423422
if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) {
424-
if let Ok(mut frames) = object_cx.find_frames(object_addr) {
423+
if let Ok(mut frames) = object_cx.find_frames(stash, object_addr) {
425424
while let Ok(Some(frame)) = frames.next() {
426425
any_frames = true;
427426
call(Symbol::Frame {

src/symbolize/gimli/coff.rs

+8
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,11 @@ impl<'a> Object<'a> {
106106
None
107107
}
108108
}
109+
110+
pub(super) fn handle_split_dwarf<'data>(
111+
package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
112+
stash: &'data Stash,
113+
load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
114+
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
115+
None
116+
}

src/symbolize/gimli/elf.rs

+54-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use super::mystd::fs;
33
use super::mystd::os::unix::ffi::{OsStrExt, OsStringExt};
44
use super::mystd::path::{Path, PathBuf};
55
use super::Either;
6-
use super::{Context, Mapping, Stash, Vec};
6+
use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash, Vec};
7+
use alloc::sync::Arc;
78
use core::convert::{TryFrom, TryInto};
89
use core::str;
910
use object::elf::{ELFCOMPRESS_ZLIB, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED};
@@ -56,7 +57,7 @@ impl Mapping {
5657
let mut sup = None;
5758
if let Some((path_sup, build_id_sup)) = object.gnu_debugaltlink_path(&path) {
5859
if let Some(map_sup) = super::mmap(&path_sup) {
59-
let map_sup = stash.set_mmap_aux(map_sup);
60+
let map_sup = stash.cache_mmap(map_sup);
6061
if let Some(sup_) = Object::parse(map_sup) {
6162
if sup_.build_id() == Some(build_id_sup) {
6263
sup = Some(sup_);
@@ -84,7 +85,7 @@ impl Mapping {
8485
.unwrap_or_else(|| "dwp".into());
8586
path_dwp.set_extension(dwp_extension);
8687
if let Some(map_dwp) = super::mmap(&path_dwp) {
87-
let map_dwp = stash.set_mmap_dwp(map_dwp);
88+
let map_dwp = stash.cache_mmap(map_dwp);
8889
if let Some(dwp_) = Object::parse(map_dwp) {
8990
return Some(dwp_);
9091
}
@@ -448,3 +449,53 @@ fn locate_debugaltlink(path: &Path, filename: &[u8], build_id: &[u8]) -> Option<
448449

449450
locate_build_id(build_id)
450451
}
452+
453+
fn convert_path<R: gimli::Reader>(r: &R) -> Result<PathBuf, gimli::Error> {
454+
let bytes = r.to_slice()?;
455+
Ok(PathBuf::from(OsStr::from_bytes(&bytes)))
456+
}
457+
458+
pub(super) fn handle_split_dwarf<'data>(
459+
package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
460+
stash: &'data Stash,
461+
load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
462+
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
463+
if let Some(dwp) = package.as_ref() {
464+
if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) {
465+
return Some(Arc::new(cu));
466+
}
467+
}
468+
469+
let mut path = PathBuf::new();
470+
if let Some(p) = load.comp_dir.as_ref() {
471+
if let Ok(p) = convert_path(p) {
472+
path.push(p);
473+
}
474+
}
475+
476+
if let Some(p) = load.path.as_ref() {
477+
if let Ok(p) = convert_path(p) {
478+
path.push(p);
479+
}
480+
}
481+
482+
if let Some(map_dwo) = super::mmap(&path) {
483+
let map_dwo = stash.cache_mmap(map_dwo);
484+
if let Some(dwo) = Object::parse(map_dwo) {
485+
return gimli::Dwarf::load(|id| -> Result<_, ()> {
486+
let data = id
487+
.dwo_name()
488+
.and_then(|name| dwo.section(stash, name))
489+
.unwrap_or(&[]);
490+
Ok(EndianSlice::new(data, Endian))
491+
})
492+
.ok()
493+
.map(|mut dwo_dwarf| {
494+
dwo_dwarf.make_dwo(&load.parent);
495+
Arc::new(dwo_dwarf)
496+
});
497+
}
498+
}
499+
500+
None
501+
}

src/symbolize/gimli/macho.rs

+8
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,11 @@ fn split_archive_path(path: &[u8]) -> Option<(&[u8], &[u8])> {
322322
let (archive, rest) = path.split_at(index);
323323
Some((archive, &rest[1..]))
324324
}
325+
326+
pub(super) fn handle_split_dwarf<'data>(
327+
package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
328+
stash: &'data Stash,
329+
load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
330+
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
331+
None
332+
}

src/symbolize/gimli/stash.rs

+10-31
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,14 @@ use core::cell::UnsafeCell;
99
/// A simple arena allocator for byte buffers.
1010
pub struct Stash {
1111
buffers: UnsafeCell<Vec<Vec<u8>>>,
12-
mmap_aux: UnsafeCell<Option<Mmap>>,
13-
mmap_dwp: UnsafeCell<Option<Mmap>>,
12+
mmaps: UnsafeCell<Vec<Mmap>>,
1413
}
1514

1615
impl Stash {
1716
pub fn new() -> Stash {
1817
Stash {
1918
buffers: UnsafeCell::new(Vec::new()),
20-
mmap_aux: UnsafeCell::new(None),
21-
mmap_dwp: UnsafeCell::new(None),
19+
mmaps: UnsafeCell::new(Vec::new()),
2220
}
2321
}
2422

@@ -37,35 +35,16 @@ impl Stash {
3735

3836
/// Stores a `Mmap` for the lifetime of this `Stash`, returning a pointer
3937
/// which is scoped to just this lifetime.
40-
pub fn set_mmap_aux(&self, map: Mmap) -> &[u8] {
38+
pub fn cache_mmap(&self, map: Mmap) -> &[u8] {
4139
// SAFETY: this is the only location for a mutable pointer to
42-
// `mmap_aux`, and this structure isn't threadsafe to shared across
43-
// threads either. This also is careful to store at most one `mmap_aux`
44-
// since overwriting a previous one would invalidate the previous
45-
// pointer. Given that though we can safely return a pointer to our
46-
// interior-owned contents.
40+
// `mmaps`, and this structure isn't threadsafe to shared across
41+
// threads either. We also never remove elements from `self.mmaps`,
42+
// so a reference to the data inside the map will live as long as
43+
// `self` does.
4744
unsafe {
48-
let mmap_aux = &mut *self.mmap_aux.get();
49-
assert!(mmap_aux.is_none());
50-
*mmap_aux = Some(map);
51-
mmap_aux.as_ref().unwrap()
52-
}
53-
}
54-
55-
/// Stores a `Mmap` for the lifetime of this `Stash`, returning a pointer
56-
/// which is scoped to just this lifetime.
57-
pub fn set_mmap_dwp(&self, map: Mmap) -> &[u8] {
58-
// SAFETY: this is the only location for a mutable pointer to
59-
// `mmap_dwp`, and this structure isn't threadsafe to shared across
60-
// threads either. This also is careful to store at most one `mmap_dwp`
61-
// since overwriting a previous one would invalidate the previous
62-
// pointer. Given that though we can safely return a pointer to our
63-
// interior-owned contents.
64-
unsafe {
65-
let mmap_dwp = &mut *self.mmap_dwp.get();
66-
assert!(mmap_dwp.is_none());
67-
*mmap_dwp = Some(map);
68-
mmap_dwp.as_ref().unwrap()
45+
let mmaps = &mut *self.mmaps.get();
46+
mmaps.push(map);
47+
mmaps.last().unwrap()
6948
}
7049
}
7150
}

0 commit comments

Comments
 (0)