Skip to content

Commit 9377836

Browse files
committed
Bump addr2line to 0.20 and support packaged split DWARF.
1 parent b3e5bb8 commit 9377836

File tree

4 files changed

+105
-12
lines changed

4 files changed

+105
-12
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ rustc-serialize = { version = "0.3", optional = true }
3737
# Optionally demangle C++ frames' symbols in backtraces.
3838
cpp_demangle = { default-features = false, version = "0.4.0", optional = true, features = ["alloc"] }
3939

40-
addr2line = { version = "0.19.0", default-features = false }
40+
addr2line = { version = "0.20.0", default-features = false }
4141
miniz_oxide = { version = "0.6.0", default-features = false }
4242

4343
[dependencies.object]

src/symbolize/gimli.rs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,15 @@ impl Mapping {
105105
struct Context<'a> {
106106
dwarf: addr2line::Context<EndianSlice<'a, Endian>>,
107107
object: Object<'a>,
108+
package: Option<gimli::DwarfPackage<EndianSlice<'a, Endian>>>,
108109
}
109110

110111
impl<'data> Context<'data> {
111112
fn new(
112113
stash: &'data Stash,
113114
object: Object<'data>,
114115
sup: Option<Object<'data>>,
116+
dwp: Option<Object<'data>>,
115117
) -> Option<Context<'data>> {
116118
let mut sections = gimli::Dwarf::load(|id| -> Result<_, ()> {
117119
let data = object.section(stash, id.name()).unwrap_or(&[]);
@@ -129,7 +131,52 @@ impl<'data> Context<'data> {
129131
}
130132
let dwarf = addr2line::Context::from_dwarf(sections).ok()?;
131133

132-
Some(Context { dwarf, object })
134+
let mut package = None;
135+
if let Some(dwp) = dwp {
136+
package = Some(
137+
gimli::DwarfPackage::load(
138+
|id| -> Result<_, gimli::Error> {
139+
let data = dwp.section(stash, id.dwo_name().unwrap()).unwrap_or(&[]);
140+
Ok(EndianSlice::new(data, Endian))
141+
},
142+
EndianSlice::new(&[], Endian),
143+
)
144+
.ok()?,
145+
);
146+
}
147+
148+
Some(Context {
149+
dwarf,
150+
object,
151+
package,
152+
})
153+
}
154+
155+
fn find_frames(
156+
&'_ self,
157+
probe: u64,
158+
) -> gimli::Result<addr2line::FrameIter<'_, EndianSlice<'data, Endian>>> {
159+
use addr2line::{LookupContinuation, LookupResult};
160+
use alloc::sync::Arc;
161+
162+
let mut l = self.dwarf.find_frames(probe);
163+
loop {
164+
let (load, continuation) = match l {
165+
LookupResult::Output(output) => break output,
166+
LookupResult::Load { load, continuation } => (load, continuation),
167+
};
168+
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);
179+
}
133180
}
134181
}
135182

@@ -358,7 +405,7 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol))
358405
None => return,
359406
};
360407
let mut any_frames = false;
361-
if let Ok(mut frames) = cx.dwarf.find_frames(addr as u64) {
408+
if let Ok(mut frames) = cx.find_frames(addr as u64) {
362409
while let Ok(Some(frame)) = frames.next() {
363410
any_frames = true;
364411
let name = match frame.function {
@@ -374,7 +421,7 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol))
374421
}
375422
if !any_frames {
376423
if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) {
377-
if let Ok(mut frames) = object_cx.dwarf.find_frames(object_addr) {
424+
if let Ok(mut frames) = object_cx.find_frames(object_addr) {
378425
while let Ok(Some(frame)) = frames.next() {
379426
any_frames = true;
380427
call(Symbol::Frame {

src/symbolize/gimli/elf.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,26 @@ impl Mapping {
2424

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

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

39-
Context::new(stash, object, None).map(Either::B)
39+
let dwp = Mapping::load_dwarf_package(path, stash);
40+
41+
Context::new(stash, object, None, dwp).map(Either::B)
4042
})
4143
}
4244

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

5355
// Try to locate a supplementary object file.
56+
let mut sup = None;
5457
if let Some((path_sup, build_id_sup)) = object.gnu_debugaltlink_path(&path) {
5558
if let Some(map_sup) = super::mmap(&path_sup) {
5659
let map_sup = stash.set_mmap_aux(map_sup);
57-
if let Some(sup) = Object::parse(map_sup) {
58-
if sup.build_id() == Some(build_id_sup) {
59-
return Context::new(stash, object, Some(sup));
60+
if let Some(sup_) = Object::parse(map_sup) {
61+
if sup_.build_id() == Some(build_id_sup) {
62+
sup = Some(sup_);
6063
}
6164
}
6265
}
6366
}
6467

65-
Context::new(stash, object, None)
68+
let dwp = Mapping::load_dwarf_package(original_path, stash);
69+
70+
Context::new(stash, object, sup, dwp)
6671
})
6772
}
73+
74+
/// Try to locate a DWARF package file.
75+
fn load_dwarf_package<'data>(path: &Path, stash: &'data Stash) -> Option<Object<'data>> {
76+
let mut path_dwp = path.to_path_buf();
77+
let dwp_extension = path
78+
.extension()
79+
.map(|previous_extension| {
80+
let mut previous_extension = previous_extension.to_os_string();
81+
previous_extension.push(".dwp");
82+
previous_extension
83+
})
84+
.unwrap_or_else(|| "dwp".into());
85+
path_dwp.set_extension(dwp_extension);
86+
if let Some(map_dwp) = super::mmap(&path_dwp) {
87+
let map_dwp = stash.set_mmap_dwp(map_dwp);
88+
if let Some(dwp_) = Object::parse(map_dwp) {
89+
return Some(dwp_);
90+
}
91+
}
92+
93+
None
94+
}
6895
}
6996

7097
struct ParsedSym {

src/symbolize/gimli/stash.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ use core::cell::UnsafeCell;
1010
pub struct Stash {
1111
buffers: UnsafeCell<Vec<Vec<u8>>>,
1212
mmap_aux: UnsafeCell<Option<Mmap>>,
13+
mmap_dwp: UnsafeCell<Option<Mmap>>,
1314
}
1415

1516
impl Stash {
1617
pub fn new() -> Stash {
1718
Stash {
1819
buffers: UnsafeCell::new(Vec::new()),
1920
mmap_aux: UnsafeCell::new(None),
21+
mmap_dwp: UnsafeCell::new(None),
2022
}
2123
}
2224

@@ -49,4 +51,21 @@ impl Stash {
4951
mmap_aux.as_ref().unwrap()
5052
}
5153
}
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()
69+
}
70+
}
5271
}

0 commit comments

Comments
 (0)