Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
218
third-party/vendor/backtrace/src/symbolize/dbghelp.rs
vendored
Normal file
218
third-party/vendor/backtrace/src/symbolize/dbghelp.rs
vendored
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
//! Symbolication strategy using `dbghelp.dll` on Windows, only used for MSVC
|
||||
//!
|
||||
//! This symbolication strategy, like with backtraces, uses dynamically loaded
|
||||
//! information from `dbghelp.dll`. (see `src/dbghelp.rs` for info about why
|
||||
//! it's dynamically loaded).
|
||||
//!
|
||||
//! This API selects its resolution strategy based on the frame provided or the
|
||||
//! information we have at hand. If a frame from `StackWalkEx` is given to us
|
||||
//! then we use similar APIs to generate correct information about inlined
|
||||
//! functions. Otherwise if all we have is an address or an older stack frame
|
||||
//! from `StackWalk64` we use the older APIs for symbolication.
|
||||
//!
|
||||
//! There's a good deal of support in this module, but a good chunk of it is
|
||||
//! converting back and forth between Windows types and Rust types. For example
|
||||
//! symbols come to us as wide strings which we then convert to utf-8 strings if
|
||||
//! we can.
|
||||
|
||||
#![allow(bad_style)]
|
||||
|
||||
use super::super::{backtrace::StackFrame, dbghelp, windows::*};
|
||||
use super::{BytesOrWideString, ResolveWhat, SymbolName};
|
||||
use core::char;
|
||||
use core::ffi::c_void;
|
||||
use core::marker;
|
||||
use core::mem;
|
||||
use core::slice;
|
||||
|
||||
// Store an OsString on std so we can provide the symbol name and filename.
|
||||
pub struct Symbol<'a> {
|
||||
name: *const [u8],
|
||||
addr: *mut c_void,
|
||||
line: Option<u32>,
|
||||
filename: Option<*const [u16]>,
|
||||
#[cfg(feature = "std")]
|
||||
_filename_cache: Option<::std::ffi::OsString>,
|
||||
#[cfg(not(feature = "std"))]
|
||||
_filename_cache: (),
|
||||
_marker: marker::PhantomData<&'a i32>,
|
||||
}
|
||||
|
||||
impl Symbol<'_> {
|
||||
pub fn name(&self) -> Option<SymbolName<'_>> {
|
||||
Some(SymbolName::new(unsafe { &*self.name }))
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> Option<*mut c_void> {
|
||||
Some(self.addr as *mut _)
|
||||
}
|
||||
|
||||
pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
|
||||
self.filename
|
||||
.map(|slice| unsafe { BytesOrWideString::Wide(&*slice) })
|
||||
}
|
||||
|
||||
pub fn colno(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn lineno(&self) -> Option<u32> {
|
||||
self.line
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn filename(&self) -> Option<&::std::path::Path> {
|
||||
use std::path::Path;
|
||||
|
||||
self._filename_cache.as_ref().map(Path::new)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(8))]
|
||||
struct Aligned8<T>(T);
|
||||
|
||||
pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
|
||||
// Ensure this process's symbols are initialized
|
||||
let dbghelp = match dbghelp::init() {
|
||||
Ok(dbghelp) => dbghelp,
|
||||
Err(()) => return, // oh well...
|
||||
};
|
||||
|
||||
match what {
|
||||
ResolveWhat::Address(_) => resolve_without_inline(&dbghelp, what.address_or_ip(), cb),
|
||||
ResolveWhat::Frame(frame) => match &frame.inner.stack_frame {
|
||||
StackFrame::New(frame) => resolve_with_inline(&dbghelp, frame, cb),
|
||||
StackFrame::Old(_) => resolve_without_inline(&dbghelp, frame.ip(), cb),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn resolve_with_inline(
|
||||
dbghelp: &dbghelp::Init,
|
||||
frame: &STACKFRAME_EX,
|
||||
cb: &mut dyn FnMut(&super::Symbol),
|
||||
) {
|
||||
do_resolve(
|
||||
|info| {
|
||||
dbghelp.SymFromInlineContextW()(
|
||||
GetCurrentProcess(),
|
||||
super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
|
||||
frame.InlineFrameContext,
|
||||
&mut 0,
|
||||
info,
|
||||
)
|
||||
},
|
||||
|line| {
|
||||
dbghelp.SymGetLineFromInlineContextW()(
|
||||
GetCurrentProcess(),
|
||||
super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
|
||||
frame.InlineFrameContext,
|
||||
0,
|
||||
&mut 0,
|
||||
line,
|
||||
)
|
||||
},
|
||||
cb,
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn resolve_without_inline(
|
||||
dbghelp: &dbghelp::Init,
|
||||
addr: *mut c_void,
|
||||
cb: &mut dyn FnMut(&super::Symbol),
|
||||
) {
|
||||
do_resolve(
|
||||
|info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr as DWORD64, &mut 0, info),
|
||||
|line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr as DWORD64, &mut 0, line),
|
||||
cb,
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn do_resolve(
|
||||
sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL,
|
||||
get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL,
|
||||
cb: &mut dyn FnMut(&super::Symbol),
|
||||
) {
|
||||
const SIZE: usize = 2 * MAX_SYM_NAME + mem::size_of::<SYMBOL_INFOW>();
|
||||
let mut data = Aligned8([0u8; SIZE]);
|
||||
let data = &mut data.0;
|
||||
let info = &mut *(data.as_mut_ptr() as *mut SYMBOL_INFOW);
|
||||
info.MaxNameLen = MAX_SYM_NAME as ULONG;
|
||||
// the struct size in C. the value is different to
|
||||
// `size_of::<SYMBOL_INFOW>() - MAX_SYM_NAME + 1` (== 81)
|
||||
// due to struct alignment.
|
||||
info.SizeOfStruct = 88;
|
||||
|
||||
if sym_from_addr(info) != TRUE {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the symbol name is greater than MaxNameLen, SymFromAddrW will
|
||||
// give a buffer of (MaxNameLen - 1) characters and set NameLen to
|
||||
// the real value.
|
||||
let name_len = ::core::cmp::min(info.NameLen as usize, info.MaxNameLen as usize - 1);
|
||||
let name_ptr = info.Name.as_ptr() as *const u16;
|
||||
let name = slice::from_raw_parts(name_ptr, name_len);
|
||||
|
||||
// Reencode the utf-16 symbol to utf-8 so we can use `SymbolName::new` like
|
||||
// all other platforms
|
||||
let mut name_len = 0;
|
||||
let mut name_buffer = [0; 256];
|
||||
{
|
||||
let mut remaining = &mut name_buffer[..];
|
||||
for c in char::decode_utf16(name.iter().cloned()) {
|
||||
let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
|
||||
let len = c.len_utf8();
|
||||
if len < remaining.len() {
|
||||
c.encode_utf8(remaining);
|
||||
let tmp = remaining;
|
||||
remaining = &mut tmp[len..];
|
||||
name_len += len;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let name = &name_buffer[..name_len] as *const [u8];
|
||||
|
||||
let mut line = mem::zeroed::<IMAGEHLP_LINEW64>();
|
||||
line.SizeOfStruct = mem::size_of::<IMAGEHLP_LINEW64>() as DWORD;
|
||||
|
||||
let mut filename = None;
|
||||
let mut lineno = None;
|
||||
if get_line_from_addr(&mut line) == TRUE {
|
||||
lineno = Some(line.LineNumber as u32);
|
||||
|
||||
let base = line.FileName;
|
||||
let mut len = 0;
|
||||
while *base.offset(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
|
||||
let len = len as usize;
|
||||
|
||||
filename = Some(slice::from_raw_parts(base, len) as *const [u16]);
|
||||
}
|
||||
|
||||
cb(&super::Symbol {
|
||||
inner: Symbol {
|
||||
name,
|
||||
addr: info.Address as *mut _,
|
||||
line: lineno,
|
||||
filename,
|
||||
_filename_cache: cache(filename),
|
||||
_marker: marker::PhantomData,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> {
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
filename.map(|f| ::std::ffi::OsString::from_wide(&*f))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
unsafe fn cache(_filename: Option<*const [u16]>) {}
|
||||
|
||||
pub unsafe fn clear_symbol_cache() {}
|
||||
511
third-party/vendor/backtrace/src/symbolize/gimli.rs
vendored
Normal file
511
third-party/vendor/backtrace/src/symbolize/gimli.rs
vendored
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
//! Support for symbolication using the `gimli` crate on crates.io
|
||||
//!
|
||||
//! This is the default symbolication implementation for Rust.
|
||||
|
||||
use self::gimli::read::EndianSlice;
|
||||
use self::gimli::NativeEndian as Endian;
|
||||
use self::mmap::Mmap;
|
||||
use self::stash::Stash;
|
||||
use super::BytesOrWideString;
|
||||
use super::ResolveWhat;
|
||||
use super::SymbolName;
|
||||
use addr2line::gimli;
|
||||
use core::convert::TryInto;
|
||||
use core::mem;
|
||||
use core::u32;
|
||||
use libc::c_void;
|
||||
use mystd::ffi::OsString;
|
||||
use mystd::fs::File;
|
||||
use mystd::path::Path;
|
||||
use mystd::prelude::v1::*;
|
||||
|
||||
#[cfg(backtrace_in_libstd)]
|
||||
mod mystd {
|
||||
pub use crate::*;
|
||||
}
|
||||
#[cfg(not(backtrace_in_libstd))]
|
||||
extern crate std as mystd;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(windows)] {
|
||||
#[path = "gimli/mmap_windows.rs"]
|
||||
mod mmap;
|
||||
} else if #[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "fuchsia",
|
||||
target_os = "haiku",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "openbsd",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
))] {
|
||||
#[path = "gimli/mmap_unix.rs"]
|
||||
mod mmap;
|
||||
} else {
|
||||
#[path = "gimli/mmap_fake.rs"]
|
||||
mod mmap;
|
||||
}
|
||||
}
|
||||
|
||||
mod stash;
|
||||
|
||||
const MAPPINGS_CACHE_SIZE: usize = 4;
|
||||
|
||||
struct Mapping {
|
||||
// 'static lifetime is a lie to hack around lack of support for self-referential structs.
|
||||
cx: Context<'static>,
|
||||
_map: Mmap,
|
||||
stash: Stash,
|
||||
}
|
||||
|
||||
enum Either<A, B> {
|
||||
#[allow(dead_code)]
|
||||
A(A),
|
||||
B(B),
|
||||
}
|
||||
|
||||
impl Mapping {
|
||||
/// Creates a `Mapping` by ensuring that the `data` specified is used to
|
||||
/// create a `Context` and it can only borrow from that or the `Stash` of
|
||||
/// decompressed sections or auxiliary data.
|
||||
fn mk<F>(data: Mmap, mk: F) -> Option<Mapping>
|
||||
where
|
||||
F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Context<'a>>,
|
||||
{
|
||||
Mapping::mk_or_other(data, move |data, stash| {
|
||||
let cx = mk(data, stash)?;
|
||||
Some(Either::B(cx))
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a `Mapping` from `data`, or if the closure decides to, returns a
|
||||
/// different mapping.
|
||||
fn mk_or_other<F>(data: Mmap, mk: F) -> Option<Mapping>
|
||||
where
|
||||
F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Either<Mapping, Context<'a>>>,
|
||||
{
|
||||
let stash = Stash::new();
|
||||
let cx = match mk(&data, &stash)? {
|
||||
Either::A(mapping) => return Some(mapping),
|
||||
Either::B(cx) => cx,
|
||||
};
|
||||
Some(Mapping {
|
||||
// Convert to 'static lifetimes since the symbols should
|
||||
// only borrow `map` and `stash` and we're preserving them below.
|
||||
cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) },
|
||||
_map: data,
|
||||
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(&[]);
|
||||
Ok(EndianSlice::new(data, Endian))
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
if let Some(sup) = sup {
|
||||
sections
|
||||
.load_sup(|id| -> Result<_, ()> {
|
||||
let data = sup.section(stash, id.name()).unwrap_or(&[]);
|
||||
Ok(EndianSlice::new(data, Endian))
|
||||
})
|
||||
.ok()?;
|
||||
}
|
||||
let dwarf = addr2line::Context::from_dwarf(sections).ok()?;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mmap(path: &Path) -> Option<Mmap> {
|
||||
let file = File::open(path).ok()?;
|
||||
let len = file.metadata().ok()?.len().try_into().ok()?;
|
||||
unsafe { Mmap::map(&file, len) }
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(windows)] {
|
||||
mod coff;
|
||||
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::{handle_split_dwarf, Object};
|
||||
} else {
|
||||
mod elf;
|
||||
use self::elf::{handle_split_dwarf, Object};
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(windows)] {
|
||||
mod libs_windows;
|
||||
use libs_windows::native_libraries;
|
||||
} else if #[cfg(any(
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "tvos",
|
||||
target_os = "watchos",
|
||||
))] {
|
||||
mod libs_macos;
|
||||
use libs_macos::native_libraries;
|
||||
} else if #[cfg(target_os = "illumos")] {
|
||||
mod libs_illumos;
|
||||
use libs_illumos::native_libraries;
|
||||
} else if #[cfg(all(
|
||||
any(
|
||||
target_os = "linux",
|
||||
target_os = "fuchsia",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
all(target_os = "android", feature = "dl_iterate_phdr"),
|
||||
),
|
||||
not(target_env = "uclibc"),
|
||||
))] {
|
||||
mod libs_dl_iterate_phdr;
|
||||
use libs_dl_iterate_phdr::native_libraries;
|
||||
#[path = "gimli/parse_running_mmaps_unix.rs"]
|
||||
mod parse_running_mmaps;
|
||||
} else if #[cfg(target_env = "libnx")] {
|
||||
mod libs_libnx;
|
||||
use libs_libnx::native_libraries;
|
||||
} else if #[cfg(target_os = "haiku")] {
|
||||
mod libs_haiku;
|
||||
use libs_haiku::native_libraries;
|
||||
} else {
|
||||
// Everything else should doesn't know how to load native libraries.
|
||||
fn native_libraries() -> Vec<Library> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Cache {
|
||||
/// All known shared libraries that have been loaded.
|
||||
libraries: Vec<Library>,
|
||||
|
||||
/// Mappings cache where we retain parsed dwarf information.
|
||||
///
|
||||
/// This list has a fixed capacity for its entire lifetime which never
|
||||
/// increases. The `usize` element of each pair is an index into `libraries`
|
||||
/// above where `usize::max_value()` represents the current executable. The
|
||||
/// `Mapping` is corresponding parsed dwarf information.
|
||||
///
|
||||
/// Note that this is basically an LRU cache and we'll be shifting things
|
||||
/// around in here as we symbolize addresses.
|
||||
mappings: Vec<(usize, Mapping)>,
|
||||
}
|
||||
|
||||
struct Library {
|
||||
name: OsString,
|
||||
/// Segments of this library loaded into memory, and where they're loaded.
|
||||
segments: Vec<LibrarySegment>,
|
||||
/// The "bias" of this library, typically where it's loaded into memory.
|
||||
/// This value is added to each segment's stated address to get the actual
|
||||
/// virtual memory address that the segment is loaded into. Additionally
|
||||
/// this bias is subtracted from real virtual memory addresses to index into
|
||||
/// debuginfo and the symbol table.
|
||||
bias: usize,
|
||||
}
|
||||
|
||||
struct LibrarySegment {
|
||||
/// The stated address of this segment in the object file. This is not
|
||||
/// actually where the segment is loaded, but rather this address plus the
|
||||
/// containing library's `bias` is where to find it.
|
||||
stated_virtual_memory_address: usize,
|
||||
/// The size of this segment in memory.
|
||||
len: usize,
|
||||
}
|
||||
|
||||
// unsafe because this is required to be externally synchronized
|
||||
pub unsafe fn clear_symbol_cache() {
|
||||
Cache::with_global(|cache| cache.mappings.clear());
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
fn new() -> Cache {
|
||||
Cache {
|
||||
mappings: Vec::with_capacity(MAPPINGS_CACHE_SIZE),
|
||||
libraries: native_libraries(),
|
||||
}
|
||||
}
|
||||
|
||||
// unsafe because this is required to be externally synchronized
|
||||
unsafe fn with_global(f: impl FnOnce(&mut Self)) {
|
||||
// A very small, very simple LRU cache for debug info mappings.
|
||||
//
|
||||
// The hit rate should be very high, since the typical stack doesn't cross
|
||||
// between many shared libraries.
|
||||
//
|
||||
// The `addr2line::Context` structures are pretty expensive to create. Its
|
||||
// cost is expected to be amortized by subsequent `locate` queries, which
|
||||
// leverage the structures built when constructing `addr2line::Context`s to
|
||||
// get nice speedups. If we didn't have this cache, that amortization would
|
||||
// never happen, and symbolicating backtraces would be ssssllllooooowwww.
|
||||
static mut MAPPINGS_CACHE: Option<Cache> = None;
|
||||
|
||||
f(MAPPINGS_CACHE.get_or_insert_with(|| Cache::new()))
|
||||
}
|
||||
|
||||
fn avma_to_svma(&self, addr: *const u8) -> Option<(usize, *const u8)> {
|
||||
self.libraries
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, lib)| {
|
||||
// First up, test if this `lib` has any segment containing the
|
||||
// `addr` (handling relocation). If this check passes then we
|
||||
// can continue below and actually translate the address.
|
||||
//
|
||||
// Note that we're using `wrapping_add` here to avoid overflow
|
||||
// checks. It's been seen in the wild that the SVMA + bias
|
||||
// computation overflows. It seems a bit odd that would happen
|
||||
// but there's not a huge amount we can do about it other than
|
||||
// probably just ignore those segments since they're likely
|
||||
// pointing off into space. This originally came up in
|
||||
// rust-lang/backtrace-rs#329.
|
||||
if !lib.segments.iter().any(|s| {
|
||||
let svma = s.stated_virtual_memory_address;
|
||||
let start = svma.wrapping_add(lib.bias);
|
||||
let end = start.wrapping_add(s.len);
|
||||
let address = addr as usize;
|
||||
start <= address && address < end
|
||||
}) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Now that we know `lib` contains `addr`, we can offset with
|
||||
// the bias to find the stated virtual memory address.
|
||||
let svma = (addr as usize).wrapping_sub(lib.bias);
|
||||
Some((i, svma as *const u8))
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
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
|
||||
// from an error, the cache entry for this path is at index 0.
|
||||
|
||||
if let Some(idx) = idx {
|
||||
// When the mapping is already in the cache, move it to the front.
|
||||
if idx != 0 {
|
||||
let entry = self.mappings.remove(idx);
|
||||
self.mappings.insert(0, entry);
|
||||
}
|
||||
} else {
|
||||
// When the mapping is not in the cache, create a new mapping,
|
||||
// insert it into the front of the cache, and evict the oldest cache
|
||||
// entry if necessary.
|
||||
let name = &self.libraries[lib].name;
|
||||
let mapping = Mapping::new(name.as_ref())?;
|
||||
|
||||
if self.mappings.len() == MAPPINGS_CACHE_SIZE {
|
||||
self.mappings.pop();
|
||||
}
|
||||
|
||||
self.mappings.insert(0, (lib, mapping));
|
||||
}
|
||||
|
||||
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) },
|
||||
stash,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
|
||||
let addr = what.address_or_ip();
|
||||
let mut call = |sym: Symbol<'_>| {
|
||||
// Extend the lifetime of `sym` to `'static` since we are unfortunately
|
||||
// required to here, but it's only ever going out as a reference so no
|
||||
// reference to it should be persisted beyond this frame anyway.
|
||||
let sym = mem::transmute::<Symbol<'_>, Symbol<'static>>(sym);
|
||||
(cb)(&super::Symbol { inner: sym });
|
||||
};
|
||||
|
||||
Cache::with_global(|cache| {
|
||||
let (lib, addr) = match cache.avma_to_svma(addr as *const u8) {
|
||||
Some(pair) => pair,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// 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, 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(stash, addr as u64) {
|
||||
while let Ok(Some(frame)) = frames.next() {
|
||||
any_frames = true;
|
||||
let name = match frame.function {
|
||||
Some(f) => Some(f.name.slice()),
|
||||
None => cx.object.search_symtab(addr as u64),
|
||||
};
|
||||
call(Symbol::Frame {
|
||||
addr: addr as *mut c_void,
|
||||
location: frame.location,
|
||||
name,
|
||||
});
|
||||
}
|
||||
}
|
||||
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(stash, object_addr) {
|
||||
while let Ok(Some(frame)) = frames.next() {
|
||||
any_frames = true;
|
||||
call(Symbol::Frame {
|
||||
addr: addr as *mut c_void,
|
||||
location: frame.location,
|
||||
name: frame.function.map(|f| f.name.slice()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !any_frames {
|
||||
if let Some(name) = cx.object.search_symtab(addr as u64) {
|
||||
call(Symbol::Symtab {
|
||||
addr: addr as *mut c_void,
|
||||
name,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub enum Symbol<'a> {
|
||||
/// We were able to locate frame information for this symbol, and
|
||||
/// `addr2line`'s frame internally has all the nitty gritty details.
|
||||
Frame {
|
||||
addr: *mut c_void,
|
||||
location: Option<addr2line::Location<'a>>,
|
||||
name: Option<&'a [u8]>,
|
||||
},
|
||||
/// Couldn't find debug information, but we found it in the symbol table of
|
||||
/// the elf executable.
|
||||
Symtab { addr: *mut c_void, name: &'a [u8] },
|
||||
}
|
||||
|
||||
impl Symbol<'_> {
|
||||
pub fn name(&self) -> Option<SymbolName<'_>> {
|
||||
match self {
|
||||
Symbol::Frame { name, .. } => {
|
||||
let name = name.as_ref()?;
|
||||
Some(SymbolName::new(name))
|
||||
}
|
||||
Symbol::Symtab { name, .. } => Some(SymbolName::new(name)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> Option<*mut c_void> {
|
||||
match self {
|
||||
Symbol::Frame { addr, .. } => Some(*addr),
|
||||
Symbol::Symtab { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
|
||||
match self {
|
||||
Symbol::Frame { location, .. } => {
|
||||
let file = location.as_ref()?.file?;
|
||||
Some(BytesOrWideString::Bytes(file.as_bytes()))
|
||||
}
|
||||
Symbol::Symtab { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filename(&self) -> Option<&Path> {
|
||||
match self {
|
||||
Symbol::Frame { location, .. } => {
|
||||
let file = location.as_ref()?.file?;
|
||||
Some(Path::new(file))
|
||||
}
|
||||
Symbol::Symtab { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lineno(&self) -> Option<u32> {
|
||||
match self {
|
||||
Symbol::Frame { location, .. } => location.as_ref()?.line,
|
||||
Symbol::Symtab { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn colno(&self) -> Option<u32> {
|
||||
match self {
|
||||
Symbol::Frame { location, .. } => location.as_ref()?.column,
|
||||
Symbol::Symtab { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
118
third-party/vendor/backtrace/src/symbolize/gimli/coff.rs
vendored
Normal file
118
third-party/vendor/backtrace/src/symbolize/gimli/coff.rs
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
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::coff::ImageSymbol as _;
|
||||
use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable};
|
||||
use object::read::StringTable;
|
||||
use object::LittleEndian as LE;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
type Pe = object::pe::ImageNtHeaders32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
type Pe = object::pe::ImageNtHeaders64;
|
||||
|
||||
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, None)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Object<'a> {
|
||||
data: &'a [u8],
|
||||
sections: SectionTable<'a>,
|
||||
symbols: Vec<(usize, &'a ImageSymbol)>,
|
||||
strings: StringTable<'a>,
|
||||
}
|
||||
|
||||
pub fn get_image_base(data: &[u8]) -> Option<usize> {
|
||||
let dos_header = ImageDosHeader::parse(data).ok()?;
|
||||
let mut offset = dos_header.nt_headers_offset().into();
|
||||
let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
|
||||
usize::try_from(nt_headers.optional_header().image_base()).ok()
|
||||
}
|
||||
|
||||
impl<'a> Object<'a> {
|
||||
fn parse(data: &'a [u8]) -> Option<Object<'a>> {
|
||||
let dos_header = ImageDosHeader::parse(data).ok()?;
|
||||
let mut offset = dos_header.nt_headers_offset().into();
|
||||
let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
|
||||
let sections = nt_headers.sections(data, offset).ok()?;
|
||||
let symtab = nt_headers.symbols(data).ok()?;
|
||||
let strings = symtab.strings();
|
||||
let image_base = usize::try_from(nt_headers.optional_header().image_base()).ok()?;
|
||||
|
||||
// Collect all the symbols into a local vector which is sorted
|
||||
// by address and contains enough data to learn about the symbol
|
||||
// name. Note that we only look at function symbols and also
|
||||
// note that the sections are 1-indexed because the zero section
|
||||
// is special (apparently).
|
||||
let mut symbols = Vec::new();
|
||||
let mut i = 0;
|
||||
let len = symtab.len();
|
||||
while i < len {
|
||||
let sym = symtab.symbol(i).ok()?;
|
||||
i += 1 + sym.number_of_aux_symbols as usize;
|
||||
let section_number = sym.section_number.get(LE);
|
||||
if sym.derived_type() != object::pe::IMAGE_SYM_DTYPE_FUNCTION || section_number == 0 {
|
||||
continue;
|
||||
}
|
||||
let addr = usize::try_from(sym.value.get(LE)).ok()?;
|
||||
let section = sections
|
||||
.section(usize::try_from(section_number).ok()?)
|
||||
.ok()?;
|
||||
let va = usize::try_from(section.virtual_address.get(LE)).ok()?;
|
||||
symbols.push((addr + va + image_base, sym));
|
||||
}
|
||||
symbols.sort_unstable_by_key(|x| x.0);
|
||||
Some(Object {
|
||||
data,
|
||||
sections,
|
||||
strings,
|
||||
symbols,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> {
|
||||
Some(
|
||||
self.sections
|
||||
.section_by_name(self.strings, name.as_bytes())?
|
||||
.1
|
||||
.pe_data(self.data)
|
||||
.ok()?,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
|
||||
// Note that unlike other formats COFF doesn't embed the size of
|
||||
// each symbol. As a last ditch effort search for the *closest*
|
||||
// symbol to a particular address and return that one. This gets
|
||||
// really wonky once symbols start getting removed because the
|
||||
// symbols returned here can be totally incorrect, but we have
|
||||
// no idea of knowing how to detect that.
|
||||
let addr = usize::try_from(addr).ok()?;
|
||||
let i = match self.symbols.binary_search_by_key(&addr, |p| p.0) {
|
||||
Ok(i) => i,
|
||||
// typically `addr` isn't in the array, but `i` is where
|
||||
// we'd insert it, so the previous position must be the
|
||||
// greatest less than `addr`
|
||||
Err(i) => i.checked_sub(1)?,
|
||||
};
|
||||
self.symbols[i].1.name(self.strings).ok()
|
||||
}
|
||||
|
||||
pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
|
||||
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
|
||||
}
|
||||
495
third-party/vendor/backtrace/src/symbolize/gimli/elf.rs
vendored
Normal file
495
third-party/vendor/backtrace/src/symbolize/gimli/elf.rs
vendored
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
use super::mystd::ffi::{OsStr, OsString};
|
||||
use super::mystd::fs;
|
||||
use super::mystd::os::unix::ffi::{OsStrExt, OsStringExt};
|
||||
use super::mystd::path::{Path, PathBuf};
|
||||
use super::Either;
|
||||
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};
|
||||
use object::read::elf::{CompressionHeader, FileHeader, SectionHeader, SectionTable, Sym};
|
||||
use object::read::StringTable;
|
||||
use object::{BigEndian, Bytes, NativeEndian};
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
type Elf = object::elf::FileHeader32<NativeEndian>;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
type Elf = object::elf::FileHeader64<NativeEndian>;
|
||||
|
||||
impl Mapping {
|
||||
pub fn new(path: &Path) -> Option<Mapping> {
|
||||
let map = super::mmap(path)?;
|
||||
Mapping::mk_or_other(map, |map, stash| {
|
||||
let object = Object::parse(&map)?;
|
||||
|
||||
// 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, 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, path_debug, Some(crc)) {
|
||||
return Some(Either::A(mapping));
|
||||
}
|
||||
}
|
||||
|
||||
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(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)?;
|
||||
|
||||
if let Some(_crc) = crc {
|
||||
// TODO: check crc
|
||||
}
|
||||
|
||||
// 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.cache_mmap(map_sup);
|
||||
if let Some(sup_) = Object::parse(map_sup) {
|
||||
if sup_.build_id() == Some(build_id_sup) {
|
||||
sup = Some(sup_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
address: u64,
|
||||
size: u64,
|
||||
name: u32,
|
||||
}
|
||||
|
||||
pub struct Object<'a> {
|
||||
/// Zero-sized type representing the native endianness.
|
||||
///
|
||||
/// We could use a literal instead, but this helps ensure correctness.
|
||||
endian: NativeEndian,
|
||||
/// The entire file data.
|
||||
data: &'a [u8],
|
||||
sections: SectionTable<'a, Elf>,
|
||||
strings: StringTable<'a>,
|
||||
/// List of pre-parsed and sorted symbols by base address.
|
||||
syms: Vec<ParsedSym>,
|
||||
}
|
||||
|
||||
impl<'a> Object<'a> {
|
||||
fn parse(data: &'a [u8]) -> Option<Object<'a>> {
|
||||
let elf = Elf::parse(data).ok()?;
|
||||
let endian = elf.endian().ok()?;
|
||||
let sections = elf.sections(endian, data).ok()?;
|
||||
let mut syms = sections
|
||||
.symbols(endian, data, object::elf::SHT_SYMTAB)
|
||||
.ok()?;
|
||||
if syms.is_empty() {
|
||||
syms = sections
|
||||
.symbols(endian, data, object::elf::SHT_DYNSYM)
|
||||
.ok()?;
|
||||
}
|
||||
let strings = syms.strings();
|
||||
|
||||
let mut syms = syms
|
||||
.iter()
|
||||
// Only look at function/object symbols. This mirrors what
|
||||
// libbacktrace does and in general we're only symbolicating
|
||||
// function addresses in theory. Object symbols correspond
|
||||
// to data, and maybe someone's crazy enough to have a
|
||||
// function go into static data?
|
||||
.filter(|sym| {
|
||||
let st_type = sym.st_type();
|
||||
st_type == object::elf::STT_FUNC || st_type == object::elf::STT_OBJECT
|
||||
})
|
||||
// skip anything that's in an undefined section header,
|
||||
// since it means it's an imported function and we're only
|
||||
// symbolicating with locally defined functions.
|
||||
.filter(|sym| sym.st_shndx(endian) != object::elf::SHN_UNDEF)
|
||||
.map(|sym| {
|
||||
let address = sym.st_value(endian).into();
|
||||
let size = sym.st_size(endian).into();
|
||||
let name = sym.st_name(endian);
|
||||
ParsedSym {
|
||||
address,
|
||||
size,
|
||||
name,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
syms.sort_unstable_by_key(|s| s.address);
|
||||
Some(Object {
|
||||
endian,
|
||||
data,
|
||||
sections,
|
||||
strings,
|
||||
syms,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn section(&self, stash: &'a Stash, name: &str) -> Option<&'a [u8]> {
|
||||
if let Some(section) = self.section_header(name) {
|
||||
let mut data = Bytes(section.data(self.endian, self.data).ok()?);
|
||||
|
||||
// Check for DWARF-standard (gABI) compression, i.e., as generated
|
||||
// by ld's `--compress-debug-sections=zlib-gabi` flag.
|
||||
let flags: u64 = section.sh_flags(self.endian).into();
|
||||
if (flags & u64::from(SHF_COMPRESSED)) == 0 {
|
||||
// Not compressed.
|
||||
return Some(data.0);
|
||||
}
|
||||
|
||||
let header = data.read::<<Elf as FileHeader>::CompressionHeader>().ok()?;
|
||||
if header.ch_type(self.endian) != ELFCOMPRESS_ZLIB {
|
||||
// Zlib compression is the only known type.
|
||||
return None;
|
||||
}
|
||||
let size = usize::try_from(header.ch_size(self.endian)).ok()?;
|
||||
let buf = stash.allocate(size);
|
||||
decompress_zlib(data.0, buf)?;
|
||||
return Some(buf);
|
||||
}
|
||||
|
||||
// Check for the nonstandard GNU compression format, i.e., as generated
|
||||
// by ld's `--compress-debug-sections=zlib-gnu` flag. This means that if
|
||||
// we're actually asking for `.debug_info` then we need to look up a
|
||||
// section named `.zdebug_info`.
|
||||
if !name.starts_with(".debug_") {
|
||||
return None;
|
||||
}
|
||||
let debug_name = name[7..].as_bytes();
|
||||
let compressed_section = self
|
||||
.sections
|
||||
.iter()
|
||||
.filter_map(|header| {
|
||||
let name = self.sections.section_name(self.endian, header).ok()?;
|
||||
if name.starts_with(b".zdebug_") && &name[8..] == debug_name {
|
||||
Some(header)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()?;
|
||||
let mut data = Bytes(compressed_section.data(self.endian, self.data).ok()?);
|
||||
if data.read_bytes(8).ok()?.0 != b"ZLIB\0\0\0\0" {
|
||||
return None;
|
||||
}
|
||||
let size = usize::try_from(data.read::<object::U32Bytes<_>>().ok()?.get(BigEndian)).ok()?;
|
||||
let buf = stash.allocate(size);
|
||||
decompress_zlib(data.0, buf)?;
|
||||
Some(buf)
|
||||
}
|
||||
|
||||
fn section_header(&self, name: &str) -> Option<&<Elf as FileHeader>::SectionHeader> {
|
||||
self.sections
|
||||
.section_by_name(self.endian, name.as_bytes())
|
||||
.map(|(_index, section)| section)
|
||||
}
|
||||
|
||||
pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
|
||||
// Same sort of binary search as Windows above
|
||||
let i = match self.syms.binary_search_by_key(&addr, |sym| sym.address) {
|
||||
Ok(i) => i,
|
||||
Err(i) => i.checked_sub(1)?,
|
||||
};
|
||||
let sym = self.syms.get(i)?;
|
||||
if sym.address <= addr && addr <= sym.address + sym.size {
|
||||
self.strings.get(sym.name).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
|
||||
None
|
||||
}
|
||||
|
||||
fn build_id(&self) -> Option<&'a [u8]> {
|
||||
for section in self.sections.iter() {
|
||||
if let Ok(Some(mut notes)) = section.notes(self.endian, self.data) {
|
||||
while let Ok(Some(note)) = notes.next() {
|
||||
if note.name() == ELF_NOTE_GNU && note.n_type(self.endian) == NT_GNU_BUILD_ID {
|
||||
return Some(note.desc());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// The contents of the ".gnu_debuglink" section is documented at:
|
||||
// https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
|
||||
fn gnu_debuglink_path(&self, path: &Path) -> Option<(PathBuf, u32)> {
|
||||
let section = self.section_header(".gnu_debuglink")?;
|
||||
let data = section.data(self.endian, self.data).ok()?;
|
||||
let len = data.iter().position(|x| *x == 0)?;
|
||||
let filename = &data[..len];
|
||||
let offset = (len + 1 + 3) & !3;
|
||||
let crc_bytes = data
|
||||
.get(offset..offset + 4)
|
||||
.and_then(|bytes| bytes.try_into().ok())?;
|
||||
let crc = u32::from_ne_bytes(crc_bytes);
|
||||
let path_debug = locate_debuglink(path, filename)?;
|
||||
Some((path_debug, crc))
|
||||
}
|
||||
|
||||
// The format of the ".gnu_debugaltlink" section is based on gdb.
|
||||
fn gnu_debugaltlink_path(&self, path: &Path) -> Option<(PathBuf, &'a [u8])> {
|
||||
let section = self.section_header(".gnu_debugaltlink")?;
|
||||
let data = section.data(self.endian, self.data).ok()?;
|
||||
let len = data.iter().position(|x| *x == 0)?;
|
||||
let filename = &data[..len];
|
||||
let build_id = &data[len + 1..];
|
||||
let path_sup = locate_debugaltlink(path, filename, build_id)?;
|
||||
Some((path_sup, build_id))
|
||||
}
|
||||
}
|
||||
|
||||
fn decompress_zlib(input: &[u8], output: &mut [u8]) -> Option<()> {
|
||||
use miniz_oxide::inflate::core::inflate_flags::{
|
||||
TINFL_FLAG_PARSE_ZLIB_HEADER, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF,
|
||||
};
|
||||
use miniz_oxide::inflate::core::{decompress, DecompressorOxide};
|
||||
use miniz_oxide::inflate::TINFLStatus;
|
||||
|
||||
let (status, in_read, out_read) = decompress(
|
||||
&mut DecompressorOxide::new(),
|
||||
input,
|
||||
output,
|
||||
0,
|
||||
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER,
|
||||
);
|
||||
if status == TINFLStatus::Done && in_read == input.len() && out_read == output.len() {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
const DEBUG_PATH: &[u8] = b"/usr/lib/debug";
|
||||
|
||||
fn debug_path_exists() -> bool {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(target_os = "freebsd", target_os = "linux"))] {
|
||||
use core::sync::atomic::{AtomicU8, Ordering};
|
||||
static DEBUG_PATH_EXISTS: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
let mut exists = DEBUG_PATH_EXISTS.load(Ordering::Relaxed);
|
||||
if exists == 0 {
|
||||
exists = if Path::new(OsStr::from_bytes(DEBUG_PATH)).is_dir() {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
};
|
||||
DEBUG_PATH_EXISTS.store(exists, Ordering::Relaxed);
|
||||
}
|
||||
exists == 1
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Locate a debug file based on its build ID.
|
||||
///
|
||||
/// The format of build id paths is documented at:
|
||||
/// https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
|
||||
fn locate_build_id(build_id: &[u8]) -> Option<PathBuf> {
|
||||
const BUILD_ID_PATH: &[u8] = b"/usr/lib/debug/.build-id/";
|
||||
const BUILD_ID_SUFFIX: &[u8] = b".debug";
|
||||
|
||||
if build_id.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if !debug_path_exists() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut path =
|
||||
Vec::with_capacity(BUILD_ID_PATH.len() + BUILD_ID_SUFFIX.len() + build_id.len() * 2 + 1);
|
||||
path.extend(BUILD_ID_PATH);
|
||||
path.push(hex(build_id[0] >> 4));
|
||||
path.push(hex(build_id[0] & 0xf));
|
||||
path.push(b'/');
|
||||
for byte in &build_id[1..] {
|
||||
path.push(hex(byte >> 4));
|
||||
path.push(hex(byte & 0xf));
|
||||
}
|
||||
path.extend(BUILD_ID_SUFFIX);
|
||||
Some(PathBuf::from(OsString::from_vec(path)))
|
||||
}
|
||||
|
||||
fn hex(byte: u8) -> u8 {
|
||||
if byte < 10 {
|
||||
b'0' + byte
|
||||
} else {
|
||||
b'a' + byte - 10
|
||||
}
|
||||
}
|
||||
|
||||
/// Locate a file specified in a `.gnu_debuglink` section.
|
||||
///
|
||||
/// `path` is the file containing the section.
|
||||
/// `filename` is from the contents of the section.
|
||||
///
|
||||
/// Search order is based on gdb, documented at:
|
||||
/// https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
|
||||
///
|
||||
/// gdb also allows the user to customize the debug search path, but we don't.
|
||||
///
|
||||
/// gdb also supports debuginfod, but we don't yet.
|
||||
fn locate_debuglink(path: &Path, filename: &[u8]) -> Option<PathBuf> {
|
||||
let path = fs::canonicalize(path).ok()?;
|
||||
let parent = path.parent()?;
|
||||
let mut f = PathBuf::from(OsString::with_capacity(
|
||||
DEBUG_PATH.len() + parent.as_os_str().len() + filename.len() + 2,
|
||||
));
|
||||
let filename = Path::new(OsStr::from_bytes(filename));
|
||||
|
||||
// Try "/parent/filename" if it differs from "path"
|
||||
f.push(parent);
|
||||
f.push(filename);
|
||||
if f != path && f.is_file() {
|
||||
return Some(f);
|
||||
}
|
||||
|
||||
// Try "/parent/.debug/filename"
|
||||
let mut s = OsString::from(f);
|
||||
s.clear();
|
||||
f = PathBuf::from(s);
|
||||
f.push(parent);
|
||||
f.push(".debug");
|
||||
f.push(filename);
|
||||
if f.is_file() {
|
||||
return Some(f);
|
||||
}
|
||||
|
||||
if debug_path_exists() {
|
||||
// Try "/usr/lib/debug/parent/filename"
|
||||
let mut s = OsString::from(f);
|
||||
s.clear();
|
||||
f = PathBuf::from(s);
|
||||
f.push(OsStr::from_bytes(DEBUG_PATH));
|
||||
f.push(parent.strip_prefix("/").unwrap());
|
||||
f.push(filename);
|
||||
if f.is_file() {
|
||||
return Some(f);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Locate a file specified in a `.gnu_debugaltlink` section.
|
||||
///
|
||||
/// `path` is the file containing the section.
|
||||
/// `filename` and `build_id` are the contents of the section.
|
||||
///
|
||||
/// Search order is based on gdb:
|
||||
/// - filename, which is either absolute or relative to `path`
|
||||
/// - the build ID path under `BUILD_ID_PATH`
|
||||
///
|
||||
/// gdb also allows the user to customize the debug search path, but we don't.
|
||||
///
|
||||
/// gdb also supports debuginfod, but we don't yet.
|
||||
fn locate_debugaltlink(path: &Path, filename: &[u8], build_id: &[u8]) -> Option<PathBuf> {
|
||||
let filename = Path::new(OsStr::from_bytes(filename));
|
||||
if filename.is_absolute() {
|
||||
if filename.is_file() {
|
||||
return Some(filename.into());
|
||||
}
|
||||
} else {
|
||||
let path = fs::canonicalize(path).ok()?;
|
||||
let parent = path.parent()?;
|
||||
let mut f = PathBuf::from(parent);
|
||||
f.push(filename);
|
||||
if f.is_file() {
|
||||
return Some(f);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
71
third-party/vendor/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
vendored
Normal file
71
third-party/vendor/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// Other Unix (e.g. Linux) platforms use ELF as an object file format
|
||||
// and typically implement an API called `dl_iterate_phdr` to load
|
||||
// native libraries.
|
||||
|
||||
use super::mystd::borrow::ToOwned;
|
||||
use super::mystd::env;
|
||||
use super::mystd::ffi::{CStr, OsStr};
|
||||
use super::mystd::os::unix::prelude::*;
|
||||
use super::{Library, LibrarySegment, OsString, Vec};
|
||||
use core::slice;
|
||||
|
||||
pub(super) fn native_libraries() -> Vec<Library> {
|
||||
let mut ret = Vec::new();
|
||||
unsafe {
|
||||
libc::dl_iterate_phdr(Some(callback), &mut ret as *mut Vec<_> as *mut _);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn infer_current_exe(base_addr: usize) -> OsString {
|
||||
if let Ok(entries) = super::parse_running_mmaps::parse_maps() {
|
||||
let opt_path = entries
|
||||
.iter()
|
||||
.find(|e| e.ip_matches(base_addr) && e.pathname().len() > 0)
|
||||
.map(|e| e.pathname())
|
||||
.cloned();
|
||||
if let Some(path) = opt_path {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
env::current_exe().map(|e| e.into()).unwrap_or_default()
|
||||
}
|
||||
|
||||
// `info` should be a valid pointers.
|
||||
// `vec` should be a valid pointer to a `std::Vec`.
|
||||
unsafe extern "C" fn callback(
|
||||
info: *mut libc::dl_phdr_info,
|
||||
_size: libc::size_t,
|
||||
vec: *mut libc::c_void,
|
||||
) -> libc::c_int {
|
||||
let info = &*info;
|
||||
let libs = &mut *(vec as *mut Vec<Library>);
|
||||
let is_main_prog = info.dlpi_name.is_null() || *info.dlpi_name == 0;
|
||||
let name = if is_main_prog {
|
||||
// The man page for dl_iterate_phdr says that the first object visited by
|
||||
// callback is the main program; so the first time we encounter a
|
||||
// nameless entry, we can assume its the main program and try to infer its path.
|
||||
// After that, we cannot continue that assumption, and we use an empty string.
|
||||
if libs.is_empty() {
|
||||
infer_current_exe(info.dlpi_addr as usize)
|
||||
} else {
|
||||
OsString::new()
|
||||
}
|
||||
} else {
|
||||
let bytes = CStr::from_ptr(info.dlpi_name).to_bytes();
|
||||
OsStr::from_bytes(bytes).to_owned()
|
||||
};
|
||||
let headers = slice::from_raw_parts(info.dlpi_phdr, info.dlpi_phnum as usize);
|
||||
libs.push(Library {
|
||||
name,
|
||||
segments: headers
|
||||
.iter()
|
||||
.map(|header| LibrarySegment {
|
||||
len: (*header).p_memsz as usize,
|
||||
stated_virtual_memory_address: (*header).p_vaddr as usize,
|
||||
})
|
||||
.collect(),
|
||||
bias: info.dlpi_addr as usize,
|
||||
});
|
||||
0
|
||||
}
|
||||
48
third-party/vendor/backtrace/src/symbolize/gimli/libs_haiku.rs
vendored
Normal file
48
third-party/vendor/backtrace/src/symbolize/gimli/libs_haiku.rs
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Haiku implements the image_info struct and the get_next_image_info()
|
||||
// functions to iterate through the loaded executable images. The
|
||||
// image_info struct contains a pointer to the start of the .text
|
||||
// section within the virtual address space, as well as the size of
|
||||
// that section. All the read-only segments of the ELF-binary are in
|
||||
// that part of the address space.
|
||||
|
||||
use super::mystd::borrow::ToOwned;
|
||||
use super::mystd::ffi::{CStr, OsStr};
|
||||
use super::mystd::mem::MaybeUninit;
|
||||
use super::mystd::os::unix::prelude::*;
|
||||
use super::{Library, LibrarySegment, Vec};
|
||||
|
||||
pub(super) fn native_libraries() -> Vec<Library> {
|
||||
let mut libraries: Vec<Library> = Vec::new();
|
||||
|
||||
unsafe {
|
||||
let mut info = MaybeUninit::<libc::image_info>::zeroed();
|
||||
let mut cookie: i32 = 0;
|
||||
// Load the first image to get a valid info struct
|
||||
let mut status =
|
||||
libc::get_next_image_info(libc::B_CURRENT_TEAM, &mut cookie, info.as_mut_ptr());
|
||||
if status != libc::B_OK {
|
||||
return libraries;
|
||||
}
|
||||
let mut info = info.assume_init();
|
||||
|
||||
while status == libc::B_OK {
|
||||
let mut segments = Vec::new();
|
||||
segments.push(LibrarySegment {
|
||||
stated_virtual_memory_address: 0,
|
||||
len: info.text_size as usize,
|
||||
});
|
||||
|
||||
let bytes = CStr::from_ptr(info.name.as_ptr()).to_bytes();
|
||||
let name = OsStr::from_bytes(bytes).to_owned();
|
||||
libraries.push(Library {
|
||||
name: name,
|
||||
segments: segments,
|
||||
bias: info.text as usize,
|
||||
});
|
||||
|
||||
status = libc::get_next_image_info(libc::B_CURRENT_TEAM, &mut cookie, &mut info);
|
||||
}
|
||||
}
|
||||
|
||||
libraries
|
||||
}
|
||||
99
third-party/vendor/backtrace/src/symbolize/gimli/libs_illumos.rs
vendored
Normal file
99
third-party/vendor/backtrace/src/symbolize/gimli/libs_illumos.rs
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
use super::mystd::borrow::ToOwned;
|
||||
use super::mystd::ffi::{CStr, OsStr};
|
||||
use super::mystd::os::unix::prelude::*;
|
||||
use super::{Library, LibrarySegment, Vec};
|
||||
use core::mem;
|
||||
use object::NativeEndian;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
use object::elf::{FileHeader64 as FileHeader, ProgramHeader64 as ProgramHeader};
|
||||
|
||||
type EHdr = FileHeader<NativeEndian>;
|
||||
type PHdr = ProgramHeader<NativeEndian>;
|
||||
|
||||
#[repr(C)]
|
||||
struct LinkMap {
|
||||
l_addr: libc::c_ulong,
|
||||
l_name: *const libc::c_char,
|
||||
l_ld: *const libc::c_void,
|
||||
l_next: *const LinkMap,
|
||||
l_prev: *const LinkMap,
|
||||
l_refname: *const libc::c_char,
|
||||
}
|
||||
|
||||
const RTLD_SELF: *const libc::c_void = -3isize as *const libc::c_void;
|
||||
const RTLD_DI_LINKMAP: libc::c_int = 2;
|
||||
|
||||
extern "C" {
|
||||
fn dlinfo(
|
||||
handle: *const libc::c_void,
|
||||
request: libc::c_int,
|
||||
p: *mut libc::c_void,
|
||||
) -> libc::c_int;
|
||||
}
|
||||
|
||||
pub(super) fn native_libraries() -> Vec<Library> {
|
||||
let mut libs = Vec::new();
|
||||
|
||||
// Request the current link map from the runtime linker:
|
||||
let map = unsafe {
|
||||
let mut map: *const LinkMap = mem::zeroed();
|
||||
if dlinfo(
|
||||
RTLD_SELF,
|
||||
RTLD_DI_LINKMAP,
|
||||
(&mut map) as *mut *const LinkMap as *mut libc::c_void,
|
||||
) != 0
|
||||
{
|
||||
return libs;
|
||||
}
|
||||
map
|
||||
};
|
||||
|
||||
// Each entry in the link map represents a loaded object:
|
||||
let mut l = map;
|
||||
while !l.is_null() {
|
||||
// Fetch the fully qualified path of the loaded object:
|
||||
let bytes = unsafe { CStr::from_ptr((*l).l_name) }.to_bytes();
|
||||
let name = OsStr::from_bytes(bytes).to_owned();
|
||||
|
||||
// The base address of the object loaded into memory:
|
||||
let addr = unsafe { (*l).l_addr };
|
||||
|
||||
// Use the ELF header for this object to locate the program
|
||||
// header:
|
||||
let e: *const EHdr = unsafe { (*l).l_addr as *const EHdr };
|
||||
let phoff = unsafe { (*e).e_phoff }.get(NativeEndian);
|
||||
let phnum = unsafe { (*e).e_phnum }.get(NativeEndian);
|
||||
let etype = unsafe { (*e).e_type }.get(NativeEndian);
|
||||
|
||||
let phdr: *const PHdr = (addr + phoff) as *const PHdr;
|
||||
let phdr = unsafe { core::slice::from_raw_parts(phdr, phnum as usize) };
|
||||
|
||||
libs.push(Library {
|
||||
name,
|
||||
segments: phdr
|
||||
.iter()
|
||||
.map(|p| {
|
||||
let memsz = p.p_memsz.get(NativeEndian);
|
||||
let vaddr = p.p_vaddr.get(NativeEndian);
|
||||
LibrarySegment {
|
||||
len: memsz as usize,
|
||||
stated_virtual_memory_address: vaddr as usize,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
bias: if etype == object::elf::ET_EXEC {
|
||||
// Program header addresses for the base executable are
|
||||
// already absolute.
|
||||
0
|
||||
} else {
|
||||
// Other addresses are relative to the object base.
|
||||
addr as usize
|
||||
},
|
||||
});
|
||||
|
||||
l = unsafe { (*l).l_next };
|
||||
}
|
||||
|
||||
libs
|
||||
}
|
||||
27
third-party/vendor/backtrace/src/symbolize/gimli/libs_libnx.rs
vendored
Normal file
27
third-party/vendor/backtrace/src/symbolize/gimli/libs_libnx.rs
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
use super::{Library, LibrarySegment, Vec};
|
||||
|
||||
// DevkitA64 doesn't natively support debug info, but the build system will
|
||||
// place debug info at the path `romfs:/debug_info.elf`.
|
||||
pub(super) fn native_libraries() -> Vec<Library> {
|
||||
extern "C" {
|
||||
static __start__: u8;
|
||||
}
|
||||
|
||||
let bias = unsafe { &__start__ } as *const u8 as usize;
|
||||
|
||||
let mut ret = Vec::new();
|
||||
let mut segments = Vec::new();
|
||||
segments.push(LibrarySegment {
|
||||
stated_virtual_memory_address: 0,
|
||||
len: usize::max_value() - bias,
|
||||
});
|
||||
|
||||
let path = "romfs:/debug_info.elf";
|
||||
ret.push(Library {
|
||||
name: path.into(),
|
||||
segments,
|
||||
bias,
|
||||
});
|
||||
|
||||
ret
|
||||
}
|
||||
146
third-party/vendor/backtrace/src/symbolize/gimli/libs_macos.rs
vendored
Normal file
146
third-party/vendor/backtrace/src/symbolize/gimli/libs_macos.rs
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#![allow(deprecated)]
|
||||
|
||||
use super::mystd::ffi::{CStr, OsStr};
|
||||
use super::mystd::os::unix::prelude::*;
|
||||
use super::mystd::prelude::v1::*;
|
||||
use super::{Library, LibrarySegment};
|
||||
use core::convert::TryInto;
|
||||
use core::mem;
|
||||
|
||||
pub(super) fn native_libraries() -> Vec<Library> {
|
||||
let mut ret = Vec::new();
|
||||
let images = unsafe { libc::_dyld_image_count() };
|
||||
for i in 0..images {
|
||||
ret.extend(native_library(i));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn native_library(i: u32) -> Option<Library> {
|
||||
use object::macho;
|
||||
use object::read::macho::{MachHeader, Segment};
|
||||
use object::NativeEndian;
|
||||
|
||||
// Fetch the name of this library which corresponds to the path of
|
||||
// where to load it as well.
|
||||
let name = unsafe {
|
||||
let name = libc::_dyld_get_image_name(i);
|
||||
if name.is_null() {
|
||||
return None;
|
||||
}
|
||||
CStr::from_ptr(name)
|
||||
};
|
||||
|
||||
// Load the image header of this library and delegate to `object` to
|
||||
// parse all the load commands so we can figure out all the segments
|
||||
// involved here.
|
||||
let (mut load_commands, endian) = unsafe {
|
||||
let header = libc::_dyld_get_image_header(i);
|
||||
if header.is_null() {
|
||||
return None;
|
||||
}
|
||||
match (*header).magic {
|
||||
macho::MH_MAGIC => {
|
||||
let endian = NativeEndian;
|
||||
let header = &*(header as *const macho::MachHeader32<NativeEndian>);
|
||||
let data = core::slice::from_raw_parts(
|
||||
header as *const _ as *const u8,
|
||||
mem::size_of_val(header) + header.sizeofcmds.get(endian) as usize,
|
||||
);
|
||||
(header.load_commands(endian, data, 0).ok()?, endian)
|
||||
}
|
||||
macho::MH_MAGIC_64 => {
|
||||
let endian = NativeEndian;
|
||||
let header = &*(header as *const macho::MachHeader64<NativeEndian>);
|
||||
let data = core::slice::from_raw_parts(
|
||||
header as *const _ as *const u8,
|
||||
mem::size_of_val(header) + header.sizeofcmds.get(endian) as usize,
|
||||
);
|
||||
(header.load_commands(endian, data, 0).ok()?, endian)
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate over the segments and register known regions for segments
|
||||
// that we find. Additionally record information bout text segments
|
||||
// for processing later, see comments below.
|
||||
let mut segments = Vec::new();
|
||||
let mut first_text = 0;
|
||||
let mut text_fileoff_zero = false;
|
||||
while let Some(cmd) = load_commands.next().ok()? {
|
||||
if let Some((seg, _)) = cmd.segment_32().ok()? {
|
||||
if seg.name() == b"__TEXT" {
|
||||
first_text = segments.len();
|
||||
if seg.fileoff(endian) == 0 && seg.filesize(endian) > 0 {
|
||||
text_fileoff_zero = true;
|
||||
}
|
||||
}
|
||||
segments.push(LibrarySegment {
|
||||
len: seg.vmsize(endian).try_into().ok()?,
|
||||
stated_virtual_memory_address: seg.vmaddr(endian).try_into().ok()?,
|
||||
});
|
||||
}
|
||||
if let Some((seg, _)) = cmd.segment_64().ok()? {
|
||||
if seg.name() == b"__TEXT" {
|
||||
first_text = segments.len();
|
||||
if seg.fileoff(endian) == 0 && seg.filesize(endian) > 0 {
|
||||
text_fileoff_zero = true;
|
||||
}
|
||||
}
|
||||
segments.push(LibrarySegment {
|
||||
len: seg.vmsize(endian).try_into().ok()?,
|
||||
stated_virtual_memory_address: seg.vmaddr(endian).try_into().ok()?,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the "slide" for this library which ends up being the
|
||||
// bias we use to figure out where in memory objects are loaded.
|
||||
// This is a bit of a weird computation though and is the result of
|
||||
// trying a few things in the wild and seeing what sticks.
|
||||
//
|
||||
// The general idea is that the `bias` plus a segment's
|
||||
// `stated_virtual_memory_address` is going to be where in the
|
||||
// actual address space the segment resides. The other thing we rely
|
||||
// on though is that a real address minus the `bias` is the index to
|
||||
// look up in the symbol table and debuginfo.
|
||||
//
|
||||
// It turns out, though, that for system loaded libraries these
|
||||
// calculations are incorrect. For native executables, however, it
|
||||
// appears correct. Lifting some logic from LLDB's source it has
|
||||
// some special-casing for the first `__TEXT` section loaded from
|
||||
// file offset 0 with a nonzero size. For whatever reason when this
|
||||
// is present it appears to mean that the symbol table is relative
|
||||
// to just the vmaddr slide for the library. If it's *not* present
|
||||
// then the symbol table is relative to the vmaddr slide plus the
|
||||
// segment's stated address.
|
||||
//
|
||||
// To handle this situation if we *don't* find a text section at
|
||||
// file offset zero then we increase the bias by the first text
|
||||
// sections's stated address and decrease all stated addresses by
|
||||
// that amount as well. That way the symbol table is always appears
|
||||
// relative to the library's bias amount. This appears to have the
|
||||
// right results for symbolizing via the symbol table.
|
||||
//
|
||||
// Honestly I'm not entirely sure whether this is right or if
|
||||
// there's something else that should indicate how to do this. For
|
||||
// now though this seems to work well enough (?) and we should
|
||||
// always be able to tweak this over time if necessary.
|
||||
//
|
||||
// For some more information see #318
|
||||
let mut slide = unsafe { libc::_dyld_get_image_vmaddr_slide(i) as usize };
|
||||
if !text_fileoff_zero {
|
||||
let adjust = segments[first_text].stated_virtual_memory_address;
|
||||
for segment in segments.iter_mut() {
|
||||
segment.stated_virtual_memory_address -= adjust;
|
||||
}
|
||||
slide += adjust;
|
||||
}
|
||||
|
||||
Some(Library {
|
||||
name: OsStr::from_bytes(name.to_bytes()).to_owned(),
|
||||
segments,
|
||||
bias: slide,
|
||||
})
|
||||
}
|
||||
89
third-party/vendor/backtrace/src/symbolize/gimli/libs_windows.rs
vendored
Normal file
89
third-party/vendor/backtrace/src/symbolize/gimli/libs_windows.rs
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use super::super::super::windows::*;
|
||||
use super::mystd::os::windows::prelude::*;
|
||||
use super::{coff, mmap, Library, LibrarySegment, OsString};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::mem;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
// For loading native libraries on Windows, see some discussion on
|
||||
// rust-lang/rust#71060 for the various strategies here.
|
||||
pub(super) fn native_libraries() -> Vec<Library> {
|
||||
let mut ret = Vec::new();
|
||||
unsafe {
|
||||
add_loaded_images(&mut ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsafe fn add_loaded_images(ret: &mut Vec<Library>) {
|
||||
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
|
||||
if snap == INVALID_HANDLE_VALUE {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut me = MaybeUninit::<MODULEENTRY32W>::zeroed().assume_init();
|
||||
me.dwSize = mem::size_of_val(&me) as DWORD;
|
||||
if Module32FirstW(snap, &mut me) == TRUE {
|
||||
loop {
|
||||
if let Some(lib) = load_library(&me) {
|
||||
ret.push(lib);
|
||||
}
|
||||
|
||||
if Module32NextW(snap, &mut me) != TRUE {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(snap);
|
||||
}
|
||||
|
||||
unsafe fn load_library(me: &MODULEENTRY32W) -> Option<Library> {
|
||||
let pos = me
|
||||
.szExePath
|
||||
.iter()
|
||||
.position(|i| *i == 0)
|
||||
.unwrap_or(me.szExePath.len());
|
||||
let name = OsString::from_wide(&me.szExePath[..pos]);
|
||||
|
||||
// MinGW libraries currently don't support ASLR
|
||||
// (rust-lang/rust#16514), but DLLs can still be relocated around in
|
||||
// the address space. It appears that addresses in debug info are
|
||||
// all as-if this library was loaded at its "image base", which is a
|
||||
// field in its COFF file headers. Since this is what debuginfo
|
||||
// seems to list we parse the symbol table and store addresses as if
|
||||
// the library was loaded at "image base" as well.
|
||||
//
|
||||
// The library may not be loaded at "image base", however.
|
||||
// (presumably something else may be loaded there?) This is where
|
||||
// the `bias` field comes into play, and we need to figure out the
|
||||
// value of `bias` here. Unfortunately though it's not clear how to
|
||||
// acquire this from a loaded module. What we do have, however, is
|
||||
// the actual load address (`modBaseAddr`).
|
||||
//
|
||||
// As a bit of a cop-out for now we mmap the file, read the file
|
||||
// header information, then drop the mmap. This is wasteful because
|
||||
// we'll probably reopen the mmap later, but this should work well
|
||||
// enough for now.
|
||||
//
|
||||
// Once we have the `image_base` (desired load location) and the
|
||||
// `base_addr` (actual load location) we can fill in the `bias`
|
||||
// (difference between the actual and desired) and then the stated
|
||||
// address of each segment is the `image_base` since that's what the
|
||||
// file says.
|
||||
//
|
||||
// For now it appears that unlike ELF/MachO we can make do with one
|
||||
// segment per library, using `modBaseSize` as the whole size.
|
||||
let mmap = mmap(name.as_ref())?;
|
||||
let image_base = coff::get_image_base(&mmap)?;
|
||||
let base_addr = me.modBaseAddr as usize;
|
||||
Some(Library {
|
||||
name,
|
||||
bias: base_addr.wrapping_sub(image_base),
|
||||
segments: vec![LibrarySegment {
|
||||
stated_virtual_memory_address: image_base,
|
||||
len: me.modBaseSize as usize,
|
||||
}],
|
||||
})
|
||||
}
|
||||
333
third-party/vendor/backtrace/src/symbolize/gimli/macho.rs
vendored
Normal file
333
third-party/vendor/backtrace/src/symbolize/gimli/macho.rs
vendored
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
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 _};
|
||||
use object::{Bytes, NativeEndian};
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
type Mach = object::macho::MachHeader32<NativeEndian>;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
type Mach = object::macho::MachHeader64<NativeEndian>;
|
||||
type MachSegment = <Mach as MachHeader>::Segment;
|
||||
type MachSection = <Mach as MachHeader>::Section;
|
||||
type MachNlist = <Mach as MachHeader>::Nlist;
|
||||
|
||||
impl Mapping {
|
||||
// The loading path for macOS is so different we just have a completely
|
||||
// different implementation of the function here. On macOS we need to go
|
||||
// probing the filesystem for a bunch of files.
|
||||
pub fn new(path: &Path) -> Option<Mapping> {
|
||||
// First up we need to load the unique UUID which is stored in the macho
|
||||
// header of the file we're reading, specified at `path`.
|
||||
let map = super::mmap(path)?;
|
||||
let (macho, data) = find_header(&map)?;
|
||||
let endian = macho.endian().ok()?;
|
||||
let uuid = macho.uuid(endian, data, 0).ok()?;
|
||||
|
||||
// Next we need to look for a `*.dSYM` file. For now we just probe the
|
||||
// containing directory and look around for something that matches
|
||||
// `*.dSYM`. Once it's found we root through the dwarf resources that it
|
||||
// contains and try to find a macho file which has a matching UUID as
|
||||
// the one of our own file. If we find a match that's the dwarf file we
|
||||
// want to return.
|
||||
if let Some(uuid) = uuid {
|
||||
if let Some(parent) = path.parent() {
|
||||
if let Some(mapping) = Mapping::load_dsym(parent, uuid) {
|
||||
return Some(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Looks like nothing matched our UUID, so let's at least return our own
|
||||
// file. This should have the symbol table for at least some
|
||||
// symbolication purposes.
|
||||
Mapping::mk(map, |data, stash| {
|
||||
let (macho, data) = find_header(data)?;
|
||||
let endian = macho.endian().ok()?;
|
||||
let obj = Object::parse(macho, endian, data)?;
|
||||
Context::new(stash, obj, None, None)
|
||||
})
|
||||
}
|
||||
|
||||
fn load_dsym(dir: &Path, uuid: [u8; 16]) -> Option<Mapping> {
|
||||
for entry in dir.read_dir().ok()? {
|
||||
let entry = entry.ok()?;
|
||||
let filename = match entry.file_name().into_string() {
|
||||
Ok(name) => name,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if !filename.ends_with(".dSYM") {
|
||||
continue;
|
||||
}
|
||||
let candidates = entry.path().join("Contents/Resources/DWARF");
|
||||
if let Some(mapping) = Mapping::try_dsym_candidate(&candidates, uuid) {
|
||||
return Some(mapping);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn try_dsym_candidate(dir: &Path, uuid: [u8; 16]) -> Option<Mapping> {
|
||||
// Look for files in the `DWARF` directory which have a matching uuid to
|
||||
// the original object file. If we find one then we found the debug
|
||||
// information.
|
||||
for entry in dir.read_dir().ok()? {
|
||||
let entry = entry.ok()?;
|
||||
let map = super::mmap(&entry.path())?;
|
||||
let candidate = Mapping::mk(map, |data, stash| {
|
||||
let (macho, data) = find_header(data)?;
|
||||
let endian = macho.endian().ok()?;
|
||||
let entry_uuid = macho.uuid(endian, data, 0).ok()??;
|
||||
if entry_uuid != uuid {
|
||||
return None;
|
||||
}
|
||||
let obj = Object::parse(macho, endian, data)?;
|
||||
Context::new(stash, obj, None, None)
|
||||
});
|
||||
if let Some(candidate) = candidate {
|
||||
return Some(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn find_header(data: &'_ [u8]) -> Option<(&'_ Mach, &'_ [u8])> {
|
||||
use object::endian::BigEndian;
|
||||
|
||||
let desired_cpu = || {
|
||||
if cfg!(target_arch = "x86") {
|
||||
Some(macho::CPU_TYPE_X86)
|
||||
} else if cfg!(target_arch = "x86_64") {
|
||||
Some(macho::CPU_TYPE_X86_64)
|
||||
} else if cfg!(target_arch = "arm") {
|
||||
Some(macho::CPU_TYPE_ARM)
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
Some(macho::CPU_TYPE_ARM64)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let mut data = Bytes(data);
|
||||
match data
|
||||
.clone()
|
||||
.read::<object::endian::U32<NativeEndian>>()
|
||||
.ok()?
|
||||
.get(NativeEndian)
|
||||
{
|
||||
macho::MH_MAGIC_64 | macho::MH_CIGAM_64 | macho::MH_MAGIC | macho::MH_CIGAM => {}
|
||||
|
||||
macho::FAT_MAGIC | macho::FAT_CIGAM => {
|
||||
let mut header_data = data;
|
||||
let endian = BigEndian;
|
||||
let header = header_data.read::<macho::FatHeader>().ok()?;
|
||||
let nfat = header.nfat_arch.get(endian);
|
||||
let arch = (0..nfat)
|
||||
.filter_map(|_| header_data.read::<macho::FatArch32>().ok())
|
||||
.find(|arch| desired_cpu() == Some(arch.cputype.get(endian)))?;
|
||||
let offset = arch.offset.get(endian);
|
||||
let size = arch.size.get(endian);
|
||||
data = data
|
||||
.read_bytes_at(offset.try_into().ok()?, size.try_into().ok()?)
|
||||
.ok()?;
|
||||
}
|
||||
|
||||
macho::FAT_MAGIC_64 | macho::FAT_CIGAM_64 => {
|
||||
let mut header_data = data;
|
||||
let endian = BigEndian;
|
||||
let header = header_data.read::<macho::FatHeader>().ok()?;
|
||||
let nfat = header.nfat_arch.get(endian);
|
||||
let arch = (0..nfat)
|
||||
.filter_map(|_| header_data.read::<macho::FatArch64>().ok())
|
||||
.find(|arch| desired_cpu() == Some(arch.cputype.get(endian)))?;
|
||||
let offset = arch.offset.get(endian);
|
||||
let size = arch.size.get(endian);
|
||||
data = data
|
||||
.read_bytes_at(offset.try_into().ok()?, size.try_into().ok()?)
|
||||
.ok()?;
|
||||
}
|
||||
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
Mach::parse(data.0, 0).ok().map(|h| (h, data.0))
|
||||
}
|
||||
|
||||
// This is used both for executables/libraries and source object files.
|
||||
pub struct Object<'a> {
|
||||
endian: NativeEndian,
|
||||
data: &'a [u8],
|
||||
dwarf: Option<&'a [MachSection]>,
|
||||
syms: Vec<(&'a [u8], u64)>,
|
||||
syms_sort_by_name: bool,
|
||||
// Only set for executables/libraries, and not the source object files.
|
||||
object_map: Option<object::ObjectMap<'a>>,
|
||||
// The outer Option is for lazy loading, and the inner Option allows load errors to be cached.
|
||||
object_mappings: Box<[Option<Option<Mapping>>]>,
|
||||
}
|
||||
|
||||
impl<'a> Object<'a> {
|
||||
fn parse(mach: &'a Mach, endian: NativeEndian, data: &'a [u8]) -> Option<Object<'a>> {
|
||||
let is_object = mach.filetype(endian) == object::macho::MH_OBJECT;
|
||||
let mut dwarf = None;
|
||||
let mut syms = Vec::new();
|
||||
let mut syms_sort_by_name = false;
|
||||
let mut commands = mach.load_commands(endian, data, 0).ok()?;
|
||||
let mut object_map = None;
|
||||
let mut object_mappings = Vec::new();
|
||||
while let Ok(Some(command)) = commands.next() {
|
||||
if let Some((segment, section_data)) = MachSegment::from_command(command).ok()? {
|
||||
// Object files should have all sections in a single unnamed segment load command.
|
||||
if segment.name() == b"__DWARF" || (is_object && segment.name() == b"") {
|
||||
dwarf = segment.sections(endian, section_data).ok();
|
||||
}
|
||||
} else if let Some(symtab) = command.symtab().ok()? {
|
||||
let symbols = symtab.symbols::<Mach, _>(endian, data).ok()?;
|
||||
syms = symbols
|
||||
.iter()
|
||||
.filter_map(|nlist: &MachNlist| {
|
||||
let name = nlist.name(endian, symbols.strings()).ok()?;
|
||||
if name.len() > 0 && nlist.is_definition() {
|
||||
Some((name, u64::from(nlist.n_value(endian))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if is_object {
|
||||
// We never search object file symbols by address.
|
||||
// Instead, we already know the symbol name from the executable, and we
|
||||
// need to search by name to find the matching symbol in the object file.
|
||||
syms.sort_unstable_by_key(|(name, _)| *name);
|
||||
syms_sort_by_name = true;
|
||||
} else {
|
||||
syms.sort_unstable_by_key(|(_, addr)| *addr);
|
||||
let map = symbols.object_map(endian);
|
||||
object_mappings.resize_with(map.objects().len(), || None);
|
||||
object_map = Some(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(Object {
|
||||
endian,
|
||||
data,
|
||||
dwarf,
|
||||
syms,
|
||||
syms_sort_by_name,
|
||||
object_map,
|
||||
object_mappings: object_mappings.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> {
|
||||
let name = name.as_bytes();
|
||||
let dwarf = self.dwarf?;
|
||||
let section = dwarf.into_iter().find(|section| {
|
||||
let section_name = section.name();
|
||||
section_name == name || {
|
||||
section_name.starts_with(b"__")
|
||||
&& name.starts_with(b".")
|
||||
&& §ion_name[2..] == &name[1..]
|
||||
}
|
||||
})?;
|
||||
Some(section.data(self.endian, self.data).ok()?)
|
||||
}
|
||||
|
||||
pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
|
||||
debug_assert!(!self.syms_sort_by_name);
|
||||
let i = match self.syms.binary_search_by_key(&addr, |(_, addr)| *addr) {
|
||||
Ok(i) => i,
|
||||
Err(i) => i.checked_sub(1)?,
|
||||
};
|
||||
let (sym, _addr) = self.syms.get(i)?;
|
||||
Some(sym)
|
||||
}
|
||||
|
||||
/// Try to load a context for an object file.
|
||||
///
|
||||
/// If dsymutil was not run, then the DWARF may be found in the source object files.
|
||||
pub(super) fn search_object_map<'b>(&'b mut self, addr: u64) -> Option<(&Context<'b>, u64)> {
|
||||
// `object_map` contains a map from addresses to symbols and object paths.
|
||||
// Look up the address and get a mapping for the object.
|
||||
let object_map = self.object_map.as_ref()?;
|
||||
let symbol = object_map.get(addr)?;
|
||||
let object_index = symbol.object_index();
|
||||
let mapping = self.object_mappings.get_mut(object_index)?;
|
||||
if mapping.is_none() {
|
||||
// No cached mapping, so create it.
|
||||
*mapping = Some(object_mapping(object_map.objects().get(object_index)?));
|
||||
}
|
||||
let cx: &'b Context<'static> = &mapping.as_ref()?.as_ref()?.cx;
|
||||
// Don't leak the `'static` lifetime, make sure it's scoped to just ourselves.
|
||||
let cx = unsafe { core::mem::transmute::<&'b Context<'static>, &'b Context<'b>>(cx) };
|
||||
|
||||
// We must translate the address in order to be able to look it up
|
||||
// in the DWARF in the object file.
|
||||
debug_assert!(cx.object.syms.is_empty() || cx.object.syms_sort_by_name);
|
||||
let i = cx
|
||||
.object
|
||||
.syms
|
||||
.binary_search_by_key(&symbol.name(), |(name, _)| *name)
|
||||
.ok()?;
|
||||
let object_symbol = cx.object.syms.get(i)?;
|
||||
let object_addr = addr
|
||||
.wrapping_sub(symbol.address())
|
||||
.wrapping_add(object_symbol.1);
|
||||
Some((cx, object_addr))
|
||||
}
|
||||
}
|
||||
|
||||
fn object_mapping(path: &[u8]) -> Option<Mapping> {
|
||||
use super::mystd::ffi::OsStr;
|
||||
use super::mystd::os::unix::prelude::*;
|
||||
|
||||
let map;
|
||||
|
||||
// `N_OSO` symbol names can be either `/path/to/object.o` or `/path/to/archive.a(object.o)`.
|
||||
let member_name = if let Some((archive_path, member_name)) = split_archive_path(path) {
|
||||
map = super::mmap(Path::new(OsStr::from_bytes(archive_path)))?;
|
||||
Some(member_name)
|
||||
} else {
|
||||
map = super::mmap(Path::new(OsStr::from_bytes(path)))?;
|
||||
None
|
||||
};
|
||||
Mapping::mk(map, |data, stash| {
|
||||
let data = match member_name {
|
||||
Some(member_name) => {
|
||||
let archive = object::read::archive::ArchiveFile::parse(data).ok()?;
|
||||
let member = archive
|
||||
.members()
|
||||
.filter_map(Result::ok)
|
||||
.find(|m| m.name() == member_name)?;
|
||||
member.data(data).ok()?
|
||||
}
|
||||
None => data,
|
||||
};
|
||||
let (macho, data) = find_header(data)?;
|
||||
let endian = macho.endian().ok()?;
|
||||
let obj = Object::parse(macho, endian, data)?;
|
||||
Context::new(stash, obj, None, None)
|
||||
})
|
||||
}
|
||||
|
||||
fn split_archive_path(path: &[u8]) -> Option<(&[u8], &[u8])> {
|
||||
let (last, path) = path.split_last()?;
|
||||
if *last != b')' {
|
||||
return None;
|
||||
}
|
||||
let index = path.iter().position(|&x| x == b'(')?;
|
||||
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
|
||||
}
|
||||
25
third-party/vendor/backtrace/src/symbolize/gimli/mmap_fake.rs
vendored
Normal file
25
third-party/vendor/backtrace/src/symbolize/gimli/mmap_fake.rs
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use super::{mystd::io::Read, File};
|
||||
use alloc::vec::Vec;
|
||||
use core::ops::Deref;
|
||||
|
||||
pub struct Mmap {
|
||||
vec: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Mmap {
|
||||
pub unsafe fn map(mut file: &File, len: usize) -> Option<Mmap> {
|
||||
let mut mmap = Mmap {
|
||||
vec: Vec::with_capacity(len),
|
||||
};
|
||||
file.read_to_end(&mut mmap.vec).ok()?;
|
||||
Some(mmap)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Mmap {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &[u8] {
|
||||
&self.vec[..]
|
||||
}
|
||||
}
|
||||
49
third-party/vendor/backtrace/src/symbolize/gimli/mmap_unix.rs
vendored
Normal file
49
third-party/vendor/backtrace/src/symbolize/gimli/mmap_unix.rs
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use super::mystd::fs::File;
|
||||
use super::mystd::os::unix::prelude::*;
|
||||
use core::ops::Deref;
|
||||
use core::ptr;
|
||||
use core::slice;
|
||||
|
||||
#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
|
||||
use libc::mmap as mmap64;
|
||||
#[cfg(all(target_os = "linux", target_env = "gnu"))]
|
||||
use libc::mmap64;
|
||||
|
||||
pub struct Mmap {
|
||||
ptr: *mut libc::c_void,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl Mmap {
|
||||
pub unsafe fn map(file: &File, len: usize) -> Option<Mmap> {
|
||||
let ptr = mmap64(
|
||||
ptr::null_mut(),
|
||||
len,
|
||||
libc::PROT_READ,
|
||||
libc::MAP_PRIVATE,
|
||||
file.as_raw_fd(),
|
||||
0,
|
||||
);
|
||||
if ptr == libc::MAP_FAILED {
|
||||
return None;
|
||||
}
|
||||
Some(Mmap { ptr, len })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Mmap {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.ptr as *const u8, self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Mmap {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let r = libc::munmap(self.ptr, self.len);
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
third-party/vendor/backtrace/src/symbolize/gimli/mmap_windows.rs
vendored
Normal file
57
third-party/vendor/backtrace/src/symbolize/gimli/mmap_windows.rs
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
use super::super::super::windows::*;
|
||||
use super::mystd::fs::File;
|
||||
use super::mystd::os::windows::prelude::*;
|
||||
use core::ops::Deref;
|
||||
use core::ptr;
|
||||
use core::slice;
|
||||
|
||||
pub struct Mmap {
|
||||
// keep the file alive to prevent it from being deleted which would cause
|
||||
// us to read bad data.
|
||||
_file: File,
|
||||
ptr: *mut c_void,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl Mmap {
|
||||
pub unsafe fn map(file: &File, len: usize) -> Option<Mmap> {
|
||||
let file = file.try_clone().ok()?;
|
||||
let mapping = CreateFileMappingA(
|
||||
file.as_raw_handle() as *mut _,
|
||||
ptr::null_mut(),
|
||||
PAGE_READONLY,
|
||||
0,
|
||||
0,
|
||||
ptr::null(),
|
||||
);
|
||||
if mapping.is_null() {
|
||||
return None;
|
||||
}
|
||||
let ptr = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, len);
|
||||
CloseHandle(mapping);
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(Mmap {
|
||||
_file: file,
|
||||
ptr,
|
||||
len,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl Deref for Mmap {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.ptr as *const u8, self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Mmap {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let r = UnmapViewOfFile(self.ptr);
|
||||
debug_assert!(r != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
295
third-party/vendor/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs
vendored
Normal file
295
third-party/vendor/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs
vendored
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
// Note: This file is only currently used on targets that call out to the code
|
||||
// in `mod libs_dl_iterate_phdr` (e.g. linux, freebsd, ...); it may be more
|
||||
// general purpose, but it hasn't been tested elsewhere.
|
||||
|
||||
use super::mystd::fs::File;
|
||||
use super::mystd::io::Read;
|
||||
use super::mystd::str::FromStr;
|
||||
use super::{OsString, String, Vec};
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub(super) struct MapsEntry {
|
||||
/// start (inclusive) and limit (exclusive) of address range.
|
||||
address: (usize, usize),
|
||||
/// The perms field are the permissions for the entry
|
||||
///
|
||||
/// r = read
|
||||
/// w = write
|
||||
/// x = execute
|
||||
/// s = shared
|
||||
/// p = private (copy on write)
|
||||
perms: [char; 4],
|
||||
/// Offset into the file (or "whatever").
|
||||
offset: usize,
|
||||
/// device (major, minor)
|
||||
dev: (usize, usize),
|
||||
/// inode on the device. 0 indicates that no inode is associated with the memory region (e.g. uninitalized data aka BSS).
|
||||
inode: usize,
|
||||
/// Usually the file backing the mapping.
|
||||
///
|
||||
/// Note: The man page for proc includes a note about "coordination" by
|
||||
/// using readelf to see the Offset field in ELF program headers. pnkfelix
|
||||
/// is not yet sure if that is intended to be a comment on pathname, or what
|
||||
/// form/purpose such coordination is meant to have.
|
||||
///
|
||||
/// There are also some pseudo-paths:
|
||||
/// "[stack]": The initial process's (aka main thread's) stack.
|
||||
/// "[stack:<tid>]": a specific thread's stack. (This was only present for a limited range of Linux verisons; it was determined to be too expensive to provide.)
|
||||
/// "[vdso]": Virtual dynamically linked shared object
|
||||
/// "[heap]": The process's heap
|
||||
///
|
||||
/// The pathname can be blank, which means it is an anonymous mapping
|
||||
/// obtained via mmap.
|
||||
///
|
||||
/// Newlines in pathname are replaced with an octal escape sequence.
|
||||
///
|
||||
/// The pathname may have "(deleted)" appended onto it if the file-backed
|
||||
/// path has been deleted.
|
||||
///
|
||||
/// Note that modifications like the latter two indicated above imply that
|
||||
/// in general the pathname may be ambiguous. (I.e. you cannot tell if the
|
||||
/// denoted filename actually ended with the text "(deleted)", or if that
|
||||
/// was added by the maps rendering.
|
||||
pathname: OsString,
|
||||
}
|
||||
|
||||
pub(super) fn parse_maps() -> Result<Vec<MapsEntry>, &'static str> {
|
||||
let mut v = Vec::new();
|
||||
let mut proc_self_maps =
|
||||
File::open("/proc/self/maps").map_err(|_| "Couldn't open /proc/self/maps")?;
|
||||
let mut buf = String::new();
|
||||
let _bytes_read = proc_self_maps
|
||||
.read_to_string(&mut buf)
|
||||
.map_err(|_| "Couldn't read /proc/self/maps")?;
|
||||
for line in buf.lines() {
|
||||
v.push(line.parse()?);
|
||||
}
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
impl MapsEntry {
|
||||
pub(super) fn pathname(&self) -> &OsString {
|
||||
&self.pathname
|
||||
}
|
||||
|
||||
pub(super) fn ip_matches(&self, ip: usize) -> bool {
|
||||
self.address.0 <= ip && ip < self.address.1
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for MapsEntry {
|
||||
type Err = &'static str;
|
||||
|
||||
// Format: address perms offset dev inode pathname
|
||||
// e.g.: "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]"
|
||||
// e.g.: "7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
|
||||
// e.g.: "35b1a21000-35b1a22000 rw-p 00000000 00:00 0"
|
||||
//
|
||||
// Note that paths may contain spaces, so we can't use `str::split` for parsing (until
|
||||
// Split::remainder is stabilized #77998).
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (range_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
|
||||
if range_str.is_empty() {
|
||||
return Err("Couldn't find address");
|
||||
}
|
||||
|
||||
let (perms_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
|
||||
if perms_str.is_empty() {
|
||||
return Err("Couldn't find permissions");
|
||||
}
|
||||
|
||||
let (offset_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
|
||||
if offset_str.is_empty() {
|
||||
return Err("Couldn't find offset");
|
||||
}
|
||||
|
||||
let (dev_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
|
||||
if dev_str.is_empty() {
|
||||
return Err("Couldn't find dev");
|
||||
}
|
||||
|
||||
let (inode_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
|
||||
if inode_str.is_empty() {
|
||||
return Err("Couldn't find inode");
|
||||
}
|
||||
|
||||
// Pathname may be omitted in which case it will be empty
|
||||
let pathname_str = s.trim_start();
|
||||
|
||||
let hex = |s| usize::from_str_radix(s, 16).map_err(|_| "Couldn't parse hex number");
|
||||
let address = if let Some((start, limit)) = range_str.split_once('-') {
|
||||
(hex(start)?, hex(limit)?)
|
||||
} else {
|
||||
return Err("Couldn't parse address range");
|
||||
};
|
||||
let perms: [char; 4] = {
|
||||
let mut chars = perms_str.chars();
|
||||
let mut c = || chars.next().ok_or("insufficient perms");
|
||||
let perms = [c()?, c()?, c()?, c()?];
|
||||
if chars.next().is_some() {
|
||||
return Err("too many perms");
|
||||
}
|
||||
perms
|
||||
};
|
||||
let offset = hex(offset_str)?;
|
||||
let dev = if let Some((major, minor)) = dev_str.split_once(':') {
|
||||
(hex(major)?, hex(minor)?)
|
||||
} else {
|
||||
return Err("Couldn't parse dev");
|
||||
};
|
||||
let inode = hex(inode_str)?;
|
||||
let pathname = pathname_str.into();
|
||||
|
||||
Ok(MapsEntry {
|
||||
address,
|
||||
perms,
|
||||
offset,
|
||||
dev,
|
||||
inode,
|
||||
pathname,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we can parse 64-bit sample output if we're on a 64-bit target.
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[test]
|
||||
fn check_maps_entry_parsing_64bit() {
|
||||
assert_eq!(
|
||||
"ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 \
|
||||
[vsyscall]"
|
||||
.parse::<MapsEntry>()
|
||||
.unwrap(),
|
||||
MapsEntry {
|
||||
address: (0xffffffffff600000, 0xffffffffff601000),
|
||||
perms: ['-', '-', 'x', 'p'],
|
||||
offset: 0x00000000,
|
||||
dev: (0x00, 0x00),
|
||||
inode: 0x0,
|
||||
pathname: "[vsyscall]".into(),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795 \
|
||||
/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
|
||||
.parse::<MapsEntry>()
|
||||
.unwrap(),
|
||||
MapsEntry {
|
||||
address: (0x7f5985f46000, 0x7f5985f48000),
|
||||
perms: ['r', 'w', '-', 'p'],
|
||||
offset: 0x00039000,
|
||||
dev: (0x103, 0x06),
|
||||
inode: 0x76021795,
|
||||
pathname: "/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2".into(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
"35b1a21000-35b1a22000 rw-p 00000000 00:00 0"
|
||||
.parse::<MapsEntry>()
|
||||
.unwrap(),
|
||||
MapsEntry {
|
||||
address: (0x35b1a21000, 0x35b1a22000),
|
||||
perms: ['r', 'w', '-', 'p'],
|
||||
offset: 0x00000000,
|
||||
dev: (0x00, 0x00),
|
||||
inode: 0x0,
|
||||
pathname: Default::default(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// (This output was taken from a 32-bit machine, but will work on any target)
|
||||
#[test]
|
||||
fn check_maps_entry_parsing_32bit() {
|
||||
/* Example snippet of output:
|
||||
08056000-08077000 rw-p 00000000 00:00 0 [heap]
|
||||
b7c79000-b7e02000 r--p 00000000 08:01 60662705 /usr/lib/locale/locale-archive
|
||||
b7e02000-b7e03000 rw-p 00000000 00:00 0
|
||||
*/
|
||||
assert_eq!(
|
||||
"08056000-08077000 rw-p 00000000 00:00 0 \
|
||||
[heap]"
|
||||
.parse::<MapsEntry>()
|
||||
.unwrap(),
|
||||
MapsEntry {
|
||||
address: (0x08056000, 0x08077000),
|
||||
perms: ['r', 'w', '-', 'p'],
|
||||
offset: 0x00000000,
|
||||
dev: (0x00, 0x00),
|
||||
inode: 0x0,
|
||||
pathname: "[heap]".into(),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"b7c79000-b7e02000 r--p 00000000 08:01 60662705 \
|
||||
/usr/lib/locale/locale-archive"
|
||||
.parse::<MapsEntry>()
|
||||
.unwrap(),
|
||||
MapsEntry {
|
||||
address: (0xb7c79000, 0xb7e02000),
|
||||
perms: ['r', '-', '-', 'p'],
|
||||
offset: 0x00000000,
|
||||
dev: (0x08, 0x01),
|
||||
inode: 0x60662705,
|
||||
pathname: "/usr/lib/locale/locale-archive".into(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
"b7e02000-b7e03000 rw-p 00000000 00:00 0"
|
||||
.parse::<MapsEntry>()
|
||||
.unwrap(),
|
||||
MapsEntry {
|
||||
address: (0xb7e02000, 0xb7e03000),
|
||||
perms: ['r', 'w', '-', 'p'],
|
||||
offset: 0x00000000,
|
||||
dev: (0x00, 0x00),
|
||||
inode: 0x0,
|
||||
pathname: Default::default(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
"b7c79000-b7e02000 r--p 00000000 08:01 60662705 \
|
||||
/executable/path/with some spaces"
|
||||
.parse::<MapsEntry>()
|
||||
.unwrap(),
|
||||
MapsEntry {
|
||||
address: (0xb7c79000, 0xb7e02000),
|
||||
perms: ['r', '-', '-', 'p'],
|
||||
offset: 0x00000000,
|
||||
dev: (0x08, 0x01),
|
||||
inode: 0x60662705,
|
||||
pathname: "/executable/path/with some spaces".into(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
"b7c79000-b7e02000 r--p 00000000 08:01 60662705 \
|
||||
/executable/path/with multiple-continuous spaces "
|
||||
.parse::<MapsEntry>()
|
||||
.unwrap(),
|
||||
MapsEntry {
|
||||
address: (0xb7c79000, 0xb7e02000),
|
||||
perms: ['r', '-', '-', 'p'],
|
||||
offset: 0x00000000,
|
||||
dev: (0x08, 0x01),
|
||||
inode: 0x60662705,
|
||||
pathname: "/executable/path/with multiple-continuous spaces ".into(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
" b7c79000-b7e02000 r--p 00000000 08:01 60662705 \
|
||||
/executable/path/starts-with-spaces"
|
||||
.parse::<MapsEntry>()
|
||||
.unwrap(),
|
||||
MapsEntry {
|
||||
address: (0xb7c79000, 0xb7e02000),
|
||||
perms: ['r', '-', '-', 'p'],
|
||||
offset: 0x00000000,
|
||||
dev: (0x08, 0x01),
|
||||
inode: 0x60662705,
|
||||
pathname: "/executable/path/starts-with-spaces".into(),
|
||||
}
|
||||
);
|
||||
}
|
||||
50
third-party/vendor/backtrace/src/symbolize/gimli/stash.rs
vendored
Normal file
50
third-party/vendor/backtrace/src/symbolize/gimli/stash.rs
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// only used on Linux right now, so allow dead code elsewhere
|
||||
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
|
||||
use super::Mmap;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::UnsafeCell;
|
||||
|
||||
/// A simple arena allocator for byte buffers.
|
||||
pub struct Stash {
|
||||
buffers: UnsafeCell<Vec<Vec<u8>>>,
|
||||
mmaps: UnsafeCell<Vec<Mmap>>,
|
||||
}
|
||||
|
||||
impl Stash {
|
||||
pub fn new() -> Stash {
|
||||
Stash {
|
||||
buffers: UnsafeCell::new(Vec::new()),
|
||||
mmaps: UnsafeCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a buffer of the specified size and returns a mutable reference
|
||||
/// to it.
|
||||
pub fn allocate(&self, size: usize) -> &mut [u8] {
|
||||
// SAFETY: this is the only function that ever constructs a mutable
|
||||
// reference to `self.buffers`.
|
||||
let buffers = unsafe { &mut *self.buffers.get() };
|
||||
let i = buffers.len();
|
||||
buffers.push(vec![0; size]);
|
||||
// SAFETY: we never remove elements from `self.buffers`, so a reference
|
||||
// to the data inside any buffer will live as long as `self` does.
|
||||
&mut buffers[i]
|
||||
}
|
||||
|
||||
/// Stores a `Mmap` for the lifetime of this `Stash`, returning a pointer
|
||||
/// which is scoped to just this lifetime.
|
||||
pub fn cache_mmap(&self, map: Mmap) -> &[u8] {
|
||||
// SAFETY: this is the only location for a mutable pointer to
|
||||
// `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 mmaps = &mut *self.mmaps.get();
|
||||
mmaps.push(map);
|
||||
mmaps.last().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
56
third-party/vendor/backtrace/src/symbolize/miri.rs
vendored
Normal file
56
third-party/vendor/backtrace/src/symbolize/miri.rs
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use core::ffi::c_void;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use super::super::backtrace::miri::{resolve_addr, Frame};
|
||||
use super::BytesOrWideString;
|
||||
use super::{ResolveWhat, SymbolName};
|
||||
|
||||
pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
|
||||
let sym = match what {
|
||||
ResolveWhat::Address(addr) => Symbol {
|
||||
inner: resolve_addr(addr),
|
||||
_unused: PhantomData,
|
||||
},
|
||||
ResolveWhat::Frame(frame) => Symbol {
|
||||
inner: frame.inner.clone(),
|
||||
_unused: PhantomData,
|
||||
},
|
||||
};
|
||||
cb(&super::Symbol { inner: sym })
|
||||
}
|
||||
|
||||
pub struct Symbol<'a> {
|
||||
inner: Frame,
|
||||
_unused: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> Symbol<'a> {
|
||||
pub fn name(&self) -> Option<SymbolName<'_>> {
|
||||
Some(SymbolName::new(&self.inner.inner.name))
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> Option<*mut c_void> {
|
||||
Some(self.inner.addr)
|
||||
}
|
||||
|
||||
pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
|
||||
Some(BytesOrWideString::Bytes(&self.inner.inner.filename))
|
||||
}
|
||||
|
||||
pub fn lineno(&self) -> Option<u32> {
|
||||
Some(self.inner.inner.lineno)
|
||||
}
|
||||
|
||||
pub fn colno(&self) -> Option<u32> {
|
||||
Some(self.inner.inner.colno)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn filename(&self) -> Option<&std::path::Path> {
|
||||
Some(std::path::Path::new(
|
||||
core::str::from_utf8(&self.inner.inner.filename).unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn clear_symbol_cache() {}
|
||||
485
third-party/vendor/backtrace/src/symbolize/mod.rs
vendored
Normal file
485
third-party/vendor/backtrace/src/symbolize/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,485 @@
|
|||
use core::{fmt, str};
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "std")] {
|
||||
use std::path::Path;
|
||||
use std::prelude::v1::*;
|
||||
}
|
||||
}
|
||||
|
||||
use super::backtrace::Frame;
|
||||
use super::types::BytesOrWideString;
|
||||
use core::ffi::c_void;
|
||||
use rustc_demangle::{try_demangle, Demangle};
|
||||
|
||||
/// Resolve an address to a symbol, passing the symbol to the specified
|
||||
/// closure.
|
||||
///
|
||||
/// This function will look up the given address in areas such as the local
|
||||
/// symbol table, dynamic symbol table, or DWARF debug info (depending on the
|
||||
/// activated implementation) to find symbols to yield.
|
||||
///
|
||||
/// The closure may not be called if resolution could not be performed, and it
|
||||
/// also may be called more than once in the case of inlined functions.
|
||||
///
|
||||
/// Symbols yielded represent the execution at the specified `addr`, returning
|
||||
/// file/line pairs for that address (if available).
|
||||
///
|
||||
/// Note that if you have a `Frame` then it's recommended to use the
|
||||
/// `resolve_frame` function instead of this one.
|
||||
///
|
||||
/// # Required features
|
||||
///
|
||||
/// This function requires the `std` feature of the `backtrace` crate to be
|
||||
/// enabled, and the `std` feature is enabled by default.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function strives to never panic, but if the `cb` provided panics then
|
||||
/// some platforms will force a double panic to abort the process. Some
|
||||
/// platforms use a C library which internally uses callbacks which cannot be
|
||||
/// unwound through, so panicking from `cb` may trigger a process abort.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// extern crate backtrace;
|
||||
///
|
||||
/// fn main() {
|
||||
/// backtrace::trace(|frame| {
|
||||
/// let ip = frame.ip();
|
||||
///
|
||||
/// backtrace::resolve(ip, |symbol| {
|
||||
/// // ...
|
||||
/// });
|
||||
///
|
||||
/// false // only look at the top frame
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "std")]
|
||||
pub fn resolve<F: FnMut(&Symbol)>(addr: *mut c_void, cb: F) {
|
||||
let _guard = crate::lock::lock();
|
||||
unsafe { resolve_unsynchronized(addr, cb) }
|
||||
}
|
||||
|
||||
/// Resolve a previously capture frame to a symbol, passing the symbol to the
|
||||
/// specified closure.
|
||||
///
|
||||
/// This function performs the same function as `resolve` except that it takes a
|
||||
/// `Frame` as an argument instead of an address. This can allow some platform
|
||||
/// implementations of backtracing to provide more accurate symbol information
|
||||
/// or information about inline frames for example. It's recommended to use this
|
||||
/// if you can.
|
||||
///
|
||||
/// # Required features
|
||||
///
|
||||
/// This function requires the `std` feature of the `backtrace` crate to be
|
||||
/// enabled, and the `std` feature is enabled by default.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function strives to never panic, but if the `cb` provided panics then
|
||||
/// some platforms will force a double panic to abort the process. Some
|
||||
/// platforms use a C library which internally uses callbacks which cannot be
|
||||
/// unwound through, so panicking from `cb` may trigger a process abort.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// extern crate backtrace;
|
||||
///
|
||||
/// fn main() {
|
||||
/// backtrace::trace(|frame| {
|
||||
/// backtrace::resolve_frame(frame, |symbol| {
|
||||
/// // ...
|
||||
/// });
|
||||
///
|
||||
/// false // only look at the top frame
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "std")]
|
||||
pub fn resolve_frame<F: FnMut(&Symbol)>(frame: &Frame, cb: F) {
|
||||
let _guard = crate::lock::lock();
|
||||
unsafe { resolve_frame_unsynchronized(frame, cb) }
|
||||
}
|
||||
|
||||
pub enum ResolveWhat<'a> {
|
||||
Address(*mut c_void),
|
||||
Frame(&'a Frame),
|
||||
}
|
||||
|
||||
impl<'a> ResolveWhat<'a> {
|
||||
#[allow(dead_code)]
|
||||
fn address_or_ip(&self) -> *mut c_void {
|
||||
match self {
|
||||
ResolveWhat::Address(a) => adjust_ip(*a),
|
||||
ResolveWhat::Frame(f) => adjust_ip(f.ip()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IP values from stack frames are typically (always?) the instruction
|
||||
// *after* the call that's the actual stack trace. Symbolizing this on
|
||||
// causes the filename/line number to be one ahead and perhaps into
|
||||
// the void if it's near the end of the function.
|
||||
//
|
||||
// This appears to basically always be the case on all platforms, so we always
|
||||
// subtract one from a resolved ip to resolve it to the previous call
|
||||
// instruction instead of the instruction being returned to.
|
||||
//
|
||||
// Ideally we would not do this. Ideally we would require callers of the
|
||||
// `resolve` APIs here to manually do the -1 and account that they want location
|
||||
// information for the *previous* instruction, not the current. Ideally we'd
|
||||
// also expose on `Frame` if we are indeed the address of the next instruction
|
||||
// or the current.
|
||||
//
|
||||
// For now though this is a pretty niche concern so we just internally always
|
||||
// subtract one. Consumers should keep working and getting pretty good results,
|
||||
// so we should be good enough.
|
||||
fn adjust_ip(a: *mut c_void) -> *mut c_void {
|
||||
if a.is_null() {
|
||||
a
|
||||
} else {
|
||||
(a as usize - 1) as *mut c_void
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `resolve`, only unsafe as it's unsynchronized.
|
||||
///
|
||||
/// This function does not have synchronization guarantees but is available when
|
||||
/// the `std` feature of this crate isn't compiled in. See the `resolve`
|
||||
/// function for more documentation and examples.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// See information on `resolve` for caveats on `cb` panicking.
|
||||
pub unsafe fn resolve_unsynchronized<F>(addr: *mut c_void, mut cb: F)
|
||||
where
|
||||
F: FnMut(&Symbol),
|
||||
{
|
||||
imp::resolve(ResolveWhat::Address(addr), &mut cb)
|
||||
}
|
||||
|
||||
/// Same as `resolve_frame`, only unsafe as it's unsynchronized.
|
||||
///
|
||||
/// This function does not have synchronization guarantees but is available
|
||||
/// when the `std` feature of this crate isn't compiled in. See the
|
||||
/// `resolve_frame` function for more documentation and examples.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// See information on `resolve_frame` for caveats on `cb` panicking.
|
||||
pub unsafe fn resolve_frame_unsynchronized<F>(frame: &Frame, mut cb: F)
|
||||
where
|
||||
F: FnMut(&Symbol),
|
||||
{
|
||||
imp::resolve(ResolveWhat::Frame(frame), &mut cb)
|
||||
}
|
||||
|
||||
/// A trait representing the resolution of a symbol in a file.
|
||||
///
|
||||
/// This trait is yielded as a trait object to the closure given to the
|
||||
/// `backtrace::resolve` function, and it is virtually dispatched as it's
|
||||
/// unknown which implementation is behind it.
|
||||
///
|
||||
/// A symbol can give contextual information about a function, for example the
|
||||
/// name, filename, line number, precise address, etc. Not all information is
|
||||
/// always available in a symbol, however, so all methods return an `Option`.
|
||||
pub struct Symbol {
|
||||
// TODO: this lifetime bound needs to be persisted eventually to `Symbol`,
|
||||
// but that's currently a breaking change. For now this is safe since
|
||||
// `Symbol` is only ever handed out by reference and can't be cloned.
|
||||
inner: imp::Symbol<'static>,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
/// Returns the name of this function.
|
||||
///
|
||||
/// The returned structure can be used to query various properties about the
|
||||
/// symbol name:
|
||||
///
|
||||
/// * The `Display` implementation will print out the demangled symbol.
|
||||
/// * The raw `str` value of the symbol can be accessed (if it's valid
|
||||
/// utf-8).
|
||||
/// * The raw bytes for the symbol name can be accessed.
|
||||
pub fn name(&self) -> Option<SymbolName<'_>> {
|
||||
self.inner.name()
|
||||
}
|
||||
|
||||
/// Returns the starting address of this function.
|
||||
pub fn addr(&self) -> Option<*mut c_void> {
|
||||
self.inner.addr().map(|p| p as *mut _)
|
||||
}
|
||||
|
||||
/// Returns the raw filename as a slice. This is mainly useful for `no_std`
|
||||
/// environments.
|
||||
pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
|
||||
self.inner.filename_raw()
|
||||
}
|
||||
|
||||
/// Returns the column number for where this symbol is currently executing.
|
||||
///
|
||||
/// Only gimli currently provides a value here and even then only if `filename`
|
||||
/// returns `Some`, and so it is then consequently subject to similar caveats.
|
||||
pub fn colno(&self) -> Option<u32> {
|
||||
self.inner.colno()
|
||||
}
|
||||
|
||||
/// Returns the line number for where this symbol is currently executing.
|
||||
///
|
||||
/// This return value is typically `Some` if `filename` returns `Some`, and
|
||||
/// is consequently subject to similar caveats.
|
||||
pub fn lineno(&self) -> Option<u32> {
|
||||
self.inner.lineno()
|
||||
}
|
||||
|
||||
/// Returns the file name where this function was defined.
|
||||
///
|
||||
/// This is currently only available when libbacktrace or gimli is being
|
||||
/// used (e.g. unix platforms other) and when a binary is compiled with
|
||||
/// debuginfo. If neither of these conditions is met then this will likely
|
||||
/// return `None`.
|
||||
///
|
||||
/// # Required features
|
||||
///
|
||||
/// This function requires the `std` feature of the `backtrace` crate to be
|
||||
/// enabled, and the `std` feature is enabled by default.
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(unreachable_code)]
|
||||
pub fn filename(&self) -> Option<&Path> {
|
||||
self.inner.filename()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Symbol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("Symbol");
|
||||
if let Some(name) = self.name() {
|
||||
d.field("name", &name);
|
||||
}
|
||||
if let Some(addr) = self.addr() {
|
||||
d.field("addr", &addr);
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
if let Some(filename) = self.filename() {
|
||||
d.field("filename", &filename);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(lineno) = self.lineno() {
|
||||
d.field("lineno", &lineno);
|
||||
}
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "cpp_demangle")] {
|
||||
// Maybe a parsed C++ symbol, if parsing the mangled symbol as Rust
|
||||
// failed.
|
||||
struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>);
|
||||
|
||||
impl<'a> OptionCppSymbol<'a> {
|
||||
fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> {
|
||||
OptionCppSymbol(::cpp_demangle::BorrowedSymbol::new(input).ok())
|
||||
}
|
||||
|
||||
fn none() -> OptionCppSymbol<'a> {
|
||||
OptionCppSymbol(None)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
// Make sure to keep this zero-sized, so that the `cpp_demangle` feature
|
||||
// has no cost when disabled.
|
||||
struct OptionCppSymbol<'a>(PhantomData<&'a ()>);
|
||||
|
||||
impl<'a> OptionCppSymbol<'a> {
|
||||
fn parse(_: &'a [u8]) -> OptionCppSymbol<'a> {
|
||||
OptionCppSymbol(PhantomData)
|
||||
}
|
||||
|
||||
fn none() -> OptionCppSymbol<'a> {
|
||||
OptionCppSymbol(PhantomData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around a symbol name to provide ergonomic accessors to the
|
||||
/// demangled name, the raw bytes, the raw string, etc.
|
||||
// Allow dead code for when the `cpp_demangle` feature is not enabled.
|
||||
#[allow(dead_code)]
|
||||
pub struct SymbolName<'a> {
|
||||
bytes: &'a [u8],
|
||||
demangled: Option<Demangle<'a>>,
|
||||
cpp_demangled: OptionCppSymbol<'a>,
|
||||
}
|
||||
|
||||
impl<'a> SymbolName<'a> {
|
||||
/// Creates a new symbol name from the raw underlying bytes.
|
||||
pub fn new(bytes: &'a [u8]) -> SymbolName<'a> {
|
||||
let str_bytes = str::from_utf8(bytes).ok();
|
||||
let demangled = str_bytes.and_then(|s| try_demangle(s).ok());
|
||||
|
||||
let cpp = if demangled.is_none() {
|
||||
OptionCppSymbol::parse(bytes)
|
||||
} else {
|
||||
OptionCppSymbol::none()
|
||||
};
|
||||
|
||||
SymbolName {
|
||||
bytes: bytes,
|
||||
demangled: demangled,
|
||||
cpp_demangled: cpp,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the raw (mangled) symbol name as a `str` if the symbol is valid utf-8.
|
||||
///
|
||||
/// Use the `Display` implementation if you want the demangled version.
|
||||
pub fn as_str(&self) -> Option<&'a str> {
|
||||
self.demangled
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.or_else(|| str::from_utf8(self.bytes).ok())
|
||||
}
|
||||
|
||||
/// Returns the raw symbol name as a list of bytes
|
||||
pub fn as_bytes(&self) -> &'a [u8] {
|
||||
self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
fn format_symbol_name(
|
||||
fmt: fn(&str, &mut fmt::Formatter<'_>) -> fmt::Result,
|
||||
mut bytes: &[u8],
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
) -> fmt::Result {
|
||||
while bytes.len() > 0 {
|
||||
match str::from_utf8(bytes) {
|
||||
Ok(name) => {
|
||||
fmt(name, f)?;
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
fmt("\u{FFFD}", f)?;
|
||||
|
||||
match err.error_len() {
|
||||
Some(len) => bytes = &bytes[err.valid_up_to() + len..],
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "cpp_demangle")] {
|
||||
impl<'a> fmt::Display for SymbolName<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(ref s) = self.demangled {
|
||||
s.fmt(f)
|
||||
} else if let Some(ref cpp) = self.cpp_demangled.0 {
|
||||
cpp.fmt(f)
|
||||
} else {
|
||||
format_symbol_name(fmt::Display::fmt, self.bytes, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
impl<'a> fmt::Display for SymbolName<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(ref s) = self.demangled {
|
||||
s.fmt(f)
|
||||
} else {
|
||||
format_symbol_name(fmt::Display::fmt, self.bytes, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "std", feature = "cpp_demangle"))] {
|
||||
impl<'a> fmt::Debug for SymbolName<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use std::fmt::Write;
|
||||
|
||||
if let Some(ref s) = self.demangled {
|
||||
return s.fmt(f)
|
||||
}
|
||||
|
||||
// This may to print if the demangled symbol isn't actually
|
||||
// valid, so handle the error here gracefully by not propagating
|
||||
// it outwards.
|
||||
if let Some(ref cpp) = self.cpp_demangled.0 {
|
||||
let mut s = String::new();
|
||||
if write!(s, "{}", cpp).is_ok() {
|
||||
return s.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
format_symbol_name(fmt::Debug::fmt, self.bytes, f)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
impl<'a> fmt::Debug for SymbolName<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(ref s) = self.demangled {
|
||||
s.fmt(f)
|
||||
} else {
|
||||
format_symbol_name(fmt::Debug::fmt, self.bytes, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to reclaim that cached memory used to symbolicate addresses.
|
||||
///
|
||||
/// This method will attempt to release any global data structures that have
|
||||
/// otherwise been cached globally or in the thread which typically represent
|
||||
/// parsed DWARF information or similar.
|
||||
///
|
||||
/// # Caveats
|
||||
///
|
||||
/// While this function is always available it doesn't actually do anything on
|
||||
/// most implementations. Libraries like dbghelp or libbacktrace do not provide
|
||||
/// facilities to deallocate state and manage the allocated memory. For now the
|
||||
/// `gimli-symbolize` feature of this crate is the only feature where this
|
||||
/// function has any effect.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn clear_symbol_cache() {
|
||||
let _guard = crate::lock::lock();
|
||||
unsafe {
|
||||
imp::clear_symbol_cache();
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(miri)] {
|
||||
mod miri;
|
||||
use miri as imp;
|
||||
} else if #[cfg(all(windows, target_env = "msvc", not(target_vendor = "uwp")))] {
|
||||
mod dbghelp;
|
||||
use dbghelp as imp;
|
||||
} else if #[cfg(all(
|
||||
any(unix, all(windows, target_env = "gnu")),
|
||||
not(target_vendor = "uwp"),
|
||||
not(target_os = "emscripten"),
|
||||
any(not(backtrace_in_libstd), feature = "backtrace"),
|
||||
))] {
|
||||
mod gimli;
|
||||
use gimli as imp;
|
||||
} else {
|
||||
mod noop;
|
||||
use noop as imp;
|
||||
}
|
||||
}
|
||||
41
third-party/vendor/backtrace/src/symbolize/noop.rs
vendored
Normal file
41
third-party/vendor/backtrace/src/symbolize/noop.rs
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! Empty symbolication strategy used to compile for platforms that have no
|
||||
//! support.
|
||||
|
||||
use super::{BytesOrWideString, ResolveWhat, SymbolName};
|
||||
use core::ffi::c_void;
|
||||
use core::marker;
|
||||
|
||||
pub unsafe fn resolve(_addr: ResolveWhat<'_>, _cb: &mut dyn FnMut(&super::Symbol)) {}
|
||||
|
||||
pub struct Symbol<'a> {
|
||||
_marker: marker::PhantomData<&'a i32>,
|
||||
}
|
||||
|
||||
impl Symbol<'_> {
|
||||
pub fn name(&self) -> Option<SymbolName<'_>> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> Option<*mut c_void> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn filename(&self) -> Option<&::std::path::Path> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn lineno(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn colno(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn clear_symbol_cache() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue