summaryrefslogtreecommitdiffstats
path: root/library/backtrace/src/symbolize/dbghelp.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/backtrace/src/symbolize/dbghelp.rs218
1 files changed, 218 insertions, 0 deletions
diff --git a/library/backtrace/src/symbolize/dbghelp.rs b/library/backtrace/src/symbolize/dbghelp.rs
new file mode 100644
index 000000000..181dba731
--- /dev/null
+++ b/library/backtrace/src/symbolize/dbghelp.rs
@@ -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() {}