Skip to content

Commit

Permalink
Merge pull request #513 from khuey/addr2line-0.20
Browse files Browse the repository at this point in the history
  • Loading branch information
Amanieu committed May 23, 2023
2 parents 4245978 + 57af7c2 commit ef961e2
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ jobs:
with:
submodules: true
- name: Install Rust
run: rustup update 1.42.0 && rustup default 1.42.0
run: rustup update 1.55.0 && rustup default 1.55.0
- run: cargo build

miri:
Expand Down
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
2 changes: 1 addition & 1 deletion crates/as-if-std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ bench = false
cfg-if = "1.0"
rustc-demangle = "0.1.4"
libc = { version = "0.2.45", default-features = false }
addr2line = { version = "0.16.0", default-features = false, optional = true }
addr2line = { version = "0.20.0", default-features = false, optional = true }
miniz_oxide = { version = "0.4.0", default-features = false }

[dependencies.object]
Expand Down
72 changes: 59 additions & 13 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,21 +97,23 @@ 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,
})
}
}

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,46 @@ 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 = id
.dwo_name()
.and_then(|name| dwp.section(stash, name))
.unwrap_or(&[]);
Ok(EndianSlice::new(data, Endian))
},
EndianSlice::new(&[], Endian),
)
.ok()?,
);
}

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

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

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),
};

l = continuation.resume(handle_split_dwarf(self.package.as_ref(), stash, load));
}
}
}

Expand All @@ -142,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 @@ -302,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 @@ -328,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 @@ -353,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.dwarf.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 @@ -374,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.dwarf.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
13 changes: 11 additions & 2 deletions src/symbolize/gimli/coff.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{Context, Mapping, Path, Stash, Vec};
use super::{gimli, Context, Endian, EndianSlice, Mapping, Path, Stash, Vec};
use alloc::sync::Arc;
use core::convert::TryFrom;
use object::pe::{ImageDosHeader, ImageSymbol};
use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable};
Expand All @@ -14,7 +15,7 @@ impl Mapping {
pub fn new(path: &Path) -> Option<Mapping> {
let map = super::mmap(path)?;
Mapping::mk(map, |data, stash| {
Context::new(stash, Object::parse(data)?, None)
Context::new(stash, Object::parse(data)?, None, None)
})
}
}
Expand Down Expand Up @@ -106,3 +107,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
}
92 changes: 82 additions & 10 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 All @@ -24,24 +25,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 +54,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));
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_);
}
}
}
}

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.cache_mmap(map_dwp);
if let Some(dwp_) = Object::parse(map_dwp) {
return Some(dwp_);
}
}

None
}
}

struct ParsedSym {
Expand Down Expand Up @@ -421,3 +449,47 @@ 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() {
path.push(convert_path(p).ok()?);
}

path.push(convert_path(load.path.as_ref()?).ok()?);

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
}
17 changes: 13 additions & 4 deletions src/symbolize/gimli/macho.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{Box, Context, Mapping, Path, Stash, Vec};
use super::{gimli, Box, Context, Endian, EndianSlice, Mapping, Path, Stash, Vec};
use alloc::sync::Arc;
use core::convert::TryInto;
use object::macho;
use object::read::macho::{MachHeader, Nlist, Section, Segment as _};
Expand Down Expand Up @@ -45,7 +46,7 @@ impl Mapping {
let (macho, data) = find_header(data)?;
let endian = macho.endian().ok()?;
let obj = Object::parse(macho, endian, data)?;
Context::new(stash, obj, None)
Context::new(stash, obj, None, None)
})
}

Expand Down Expand Up @@ -82,7 +83,7 @@ impl Mapping {
return None;
}
let obj = Object::parse(macho, endian, data)?;
Context::new(stash, obj, None)
Context::new(stash, obj, None, None)
});
if let Some(candidate) = candidate {
return Some(candidate);
Expand Down Expand Up @@ -309,7 +310,7 @@ fn object_mapping(path: &[u8]) -> Option<Mapping> {
let (macho, data) = find_header(data)?;
let endian = macho.endian().ok()?;
let obj = Object::parse(macho, endian, data)?;
Context::new(stash, obj, None)
Context::new(stash, obj, None, None)
})
}

Expand All @@ -322,3 +323,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
}

0 comments on commit ef961e2

Please sign in to comment.