diff options
Diffstat (limited to 'vendor/backtrace/src/dbghelp.rs')
-rw-r--r-- | vendor/backtrace/src/dbghelp.rs | 365 |
1 files changed, 0 insertions, 365 deletions
diff --git a/vendor/backtrace/src/dbghelp.rs b/vendor/backtrace/src/dbghelp.rs deleted file mode 100644 index c81766b..0000000 --- a/vendor/backtrace/src/dbghelp.rs +++ /dev/null @@ -1,365 +0,0 @@ -//! A module to assist in managing dbghelp bindings on Windows -//! -//! Backtraces on Windows (at least for MSVC) are largely powered through -//! `dbghelp.dll` and the various functions that it contains. These functions -//! are currently loaded *dynamically* rather than linking to `dbghelp.dll` -//! statically. This is currently done by the standard library (and is in theory -//! required there), but is an effort to help reduce the static dll dependencies -//! of a library since backtraces are typically pretty optional. That being -//! said, `dbghelp.dll` almost always successfully loads on Windows. -//! -//! Note though that since we're loading all this support dynamically we can't -//! actually use the raw definitions in `winapi`, but rather we need to define -//! the function pointer types ourselves and use that. We don't really want to -//! be in the business of duplicating winapi, so we have a Cargo feature -//! `verify-winapi` which asserts that all bindings match those in winapi and -//! this feature is enabled on CI. -//! -//! Finally, you'll note here that the dll for `dbghelp.dll` is never unloaded, -//! and that's currently intentional. The thinking is that we can globally cache -//! it and use it between calls to the API, avoiding expensive loads/unloads. If -//! this is a problem for leak detectors or something like that we can cross the -//! bridge when we get there. - -#![allow(non_snake_case)] - -use super::windows::*; -use core::mem; -use core::ptr; - -// Work around `SymGetOptions` and `SymSetOptions` not being present in winapi -// itself. Otherwise this is only used when we're double-checking types against -// winapi. -#[cfg(feature = "verify-winapi")] -mod dbghelp { - use crate::windows::*; - pub use winapi::um::dbghelp::{ - StackWalk64, StackWalkEx, SymCleanup, SymFromAddrW, SymFunctionTableAccess64, - SymGetLineFromAddrW64, SymGetModuleBase64, SymGetOptions, SymInitializeW, SymSetOptions, - }; - - extern "system" { - // Not defined in winapi yet - pub fn SymFromInlineContextW( - hProcess: HANDLE, - Address: DWORD64, - InlineContext: ULONG, - Displacement: PDWORD64, - Symbol: PSYMBOL_INFOW, - ) -> BOOL; - pub fn SymGetLineFromInlineContextW( - hProcess: HANDLE, - dwAddr: DWORD64, - InlineContext: ULONG, - qwModuleBaseAddress: DWORD64, - pdwDisplacement: PDWORD, - Line: PIMAGEHLP_LINEW64, - ) -> BOOL; - } - - pub fn assert_equal_types<T>(a: T, _b: T) -> T { - a - } -} - -// This macro is used to define a `Dbghelp` structure which internally contains -// all the function pointers that we might load. -macro_rules! dbghelp { - (extern "system" { - $(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)* - }) => ( - pub struct Dbghelp { - /// The loaded DLL for `dbghelp.dll` - dll: HMODULE, - - // Each function pointer for each function we might use - $($name: usize,)* - } - - static mut DBGHELP: Dbghelp = Dbghelp { - // Initially we haven't loaded the DLL - dll: 0 as *mut _, - // Initially all functions are set to zero to say they need to be - // dynamically loaded. - $($name: 0,)* - }; - - // Convenience typedef for each function type. - $(pub type $name = unsafe extern "system" fn($($argty),*) -> $ret;)* - - impl Dbghelp { - /// Attempts to open `dbghelp.dll`. Returns success if it works or - /// error if `LoadLibraryW` fails. - /// - /// Panics if library is already loaded. - fn ensure_open(&mut self) -> Result<(), ()> { - if !self.dll.is_null() { - return Ok(()) - } - let lib = b"dbghelp.dll\0"; - unsafe { - self.dll = LoadLibraryA(lib.as_ptr() as *const i8); - if self.dll.is_null() { - Err(()) - } else { - Ok(()) - } - } - } - - // Function for each method we'd like to use. When called it will - // either read the cached function pointer or load it and return the - // loaded value. Loads are asserted to succeed. - $(pub fn $name(&mut self) -> Option<$name> { - unsafe { - if self.$name == 0 { - let name = concat!(stringify!($name), "\0"); - self.$name = self.symbol(name.as_bytes())?; - } - let ret = mem::transmute::<usize, $name>(self.$name); - #[cfg(feature = "verify-winapi")] - dbghelp::assert_equal_types(ret, dbghelp::$name); - Some(ret) - } - })* - - fn symbol(&self, symbol: &[u8]) -> Option<usize> { - unsafe { - match GetProcAddress(self.dll, symbol.as_ptr() as *const _) as usize { - 0 => None, - n => Some(n), - } - } - } - } - - // Convenience proxy to use the cleanup locks to reference dbghelp - // functions. - #[allow(dead_code)] - impl Init { - $(pub fn $name(&self) -> $name { - unsafe { - DBGHELP.$name().unwrap() - } - })* - - pub fn dbghelp(&self) -> *mut Dbghelp { - unsafe { - &mut DBGHELP - } - } - } - ) - -} - -const SYMOPT_DEFERRED_LOADS: DWORD = 0x00000004; - -dbghelp! { - extern "system" { - fn SymGetOptions() -> DWORD; - fn SymSetOptions(options: DWORD) -> DWORD; - fn SymInitializeW( - handle: HANDLE, - path: PCWSTR, - invade: BOOL - ) -> BOOL; - fn SymCleanup(handle: HANDLE) -> BOOL; - fn StackWalk64( - MachineType: DWORD, - hProcess: HANDLE, - hThread: HANDLE, - StackFrame: LPSTACKFRAME64, - ContextRecord: PVOID, - ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64, - FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64, - GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64, - TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64 - ) -> BOOL; - fn SymFunctionTableAccess64( - hProcess: HANDLE, - AddrBase: DWORD64 - ) -> PVOID; - fn SymGetModuleBase64( - hProcess: HANDLE, - AddrBase: DWORD64 - ) -> DWORD64; - fn SymFromAddrW( - hProcess: HANDLE, - Address: DWORD64, - Displacement: PDWORD64, - Symbol: PSYMBOL_INFOW - ) -> BOOL; - fn SymGetLineFromAddrW64( - hProcess: HANDLE, - dwAddr: DWORD64, - pdwDisplacement: PDWORD, - Line: PIMAGEHLP_LINEW64 - ) -> BOOL; - fn StackWalkEx( - MachineType: DWORD, - hProcess: HANDLE, - hThread: HANDLE, - StackFrame: LPSTACKFRAME_EX, - ContextRecord: PVOID, - ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64, - FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64, - GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64, - TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64, - Flags: DWORD - ) -> BOOL; - fn SymFromInlineContextW( - hProcess: HANDLE, - Address: DWORD64, - InlineContext: ULONG, - Displacement: PDWORD64, - Symbol: PSYMBOL_INFOW - ) -> BOOL; - fn SymGetLineFromInlineContextW( - hProcess: HANDLE, - dwAddr: DWORD64, - InlineContext: ULONG, - qwModuleBaseAddress: DWORD64, - pdwDisplacement: PDWORD, - Line: PIMAGEHLP_LINEW64 - ) -> BOOL; - } -} - -pub struct Init { - lock: HANDLE, -} - -/// Initialize all support necessary to access `dbghelp` API functions from this -/// crate. -/// -/// Note that this function is **safe**, it internally has its own -/// synchronization. Also note that it is safe to call this function multiple -/// times recursively. -pub fn init() -> Result<Init, ()> { - use core::sync::atomic::{AtomicUsize, Ordering::SeqCst}; - - // Helper function for generating a name that's unique to the process. - fn mutex_name() -> [u8; 33] { - let mut name: [u8; 33] = *b"Local\\RustBacktraceMutex00000000\0"; - let mut id = unsafe { GetCurrentProcessId() }; - // Quick and dirty no alloc u32 to hex. - let mut index = name.len() - 1; - while id > 0 { - name[index - 1] = match (id & 0xF) as u8 { - h @ 0..=9 => b'0' + h, - h => b'A' + (h - 10), - }; - id >>= 4; - index -= 1; - } - name - } - - unsafe { - // First thing we need to do is to synchronize this function. This can - // be called concurrently from other threads or recursively within one - // thread. Note that it's trickier than that though because what we're - // using here, `dbghelp`, *also* needs to be synchronized with all other - // callers to `dbghelp` in this process. - // - // Typically there aren't really that many calls to `dbghelp` within the - // same process and we can probably safely assume that we're the only - // ones accessing it. There is, however, one primary other user we have - // to worry about which is ironically ourselves, but in the standard - // library. The Rust standard library depends on this crate for - // backtrace support, and this crate also exists on crates.io. This - // means that if the standard library is printing a panic backtrace it - // may race with this crate coming from crates.io, causing segfaults. - // - // To help solve this synchronization problem we employ a - // Windows-specific trick here (it is, after all, a Windows-specific - // restriction about synchronization). We create a *session-local* named - // mutex to protect this call. The intention here is that the standard - // library and this crate don't have to share Rust-level APIs to - // synchronize here but can instead work behind the scenes to make sure - // they're synchronizing with one another. That way when this function - // is called through the standard library or through crates.io we can be - // sure that the same mutex is being acquired. - // - // So all of that is to say that the first thing we do here is we - // atomically create a `HANDLE` which is a named mutex on Windows. We - // synchronize a bit with other threads sharing this function - // specifically and ensure that only one handle is created per instance - // of this function. Note that the handle is never closed once it's - // stored in the global. - // - // After we've actually go the lock we simply acquire it, and our `Init` - // handle we hand out will be responsible for dropping it eventually. - static LOCK: AtomicUsize = AtomicUsize::new(0); - let mut lock = LOCK.load(SeqCst); - if lock == 0 { - let name = mutex_name(); - lock = CreateMutexA(ptr::null_mut(), 0, name.as_ptr().cast::<i8>()) as usize; - if lock == 0 { - return Err(()); - } - if let Err(other) = LOCK.compare_exchange(0, lock, SeqCst, SeqCst) { - debug_assert!(other != 0); - CloseHandle(lock as HANDLE); - lock = other; - } - } - debug_assert!(lock != 0); - let lock = lock as HANDLE; - let r = WaitForSingleObjectEx(lock, INFINITE, FALSE); - debug_assert_eq!(r, 0); - let ret = Init { lock }; - - // Ok, phew! Now that we're all safely synchronized, let's actually - // start processing everything. First up we need to ensure that - // `dbghelp.dll` is actually loaded in this process. We do this - // dynamically to avoid a static dependency. This has historically been - // done to work around weird linking issues and is intended at making - // binaries a bit more portable since this is largely just a debugging - // utility. - // - // Once we've opened `dbghelp.dll` we need to call some initialization - // functions in it, and that's detailed more below. We only do this - // once, though, so we've got a global boolean indicating whether we're - // done yet or not. - DBGHELP.ensure_open()?; - - static mut INITIALIZED: bool = false; - if INITIALIZED { - return Ok(ret); - } - - let orig = DBGHELP.SymGetOptions().unwrap()(); - - // Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because - // according to MSVC's own docs about this: "This is the fastest, most - // efficient way to use the symbol handler.", so let's do that! - DBGHELP.SymSetOptions().unwrap()(orig | SYMOPT_DEFERRED_LOADS); - - // Actually initialize symbols with MSVC. Note that this can fail, but we - // ignore it. There's not a ton of prior art for this per se, but LLVM - // internally seems to ignore the return value here and one of the - // sanitizer libraries in LLVM prints a scary warning if this fails but - // basically ignores it in the long run. - // - // One case this comes up a lot for Rust is that the standard library and - // this crate on crates.io both want to compete for `SymInitializeW`. The - // standard library historically wanted to initialize then cleanup most of - // the time, but now that it's using this crate it means that someone will - // get to initialization first and the other will pick up that - // initialization. - DBGHELP.SymInitializeW().unwrap()(GetCurrentProcess(), ptr::null_mut(), TRUE); - INITIALIZED = true; - Ok(ret) - } -} - -impl Drop for Init { - fn drop(&mut self) { - unsafe { - let r = ReleaseMutex(self.lock); - debug_assert!(r != 0); - } - } -} |