1 //! A module to assist in managing dbghelp bindings on Windows
3 //! Backtraces on Windows (at least for MSVC) are largely powered through
4 //! `dbghelp.dll` and the various functions that it contains. These functions
5 //! are currently loaded *dynamically* rather than linking to `dbghelp.dll`
6 //! statically. This is currently done by the standard library (and is in theory
7 //! required there), but is an effort to help reduce the static dll dependencies
8 //! of a library since backtraces are typically pretty optional. That being
9 //! said, `dbghelp.dll` almost always successfully loads on Windows.
11 //! Note though that since we're loading all this support dynamically we can't
12 //! actually use the raw definitions in `winapi`, but rather we need to define
13 //! the function pointer types ourselves and use that. We don't really want to
14 //! be in the business of duplicating winapi, so we have a Cargo feature
15 //! `verify-winapi` which asserts that all bindings match those in winapi and
16 //! this feature is enabled on CI.
18 //! Finally, you'll note here that the dll for `dbghelp.dll` is never unloaded,
19 //! and that's currently intentional. The thinking is that we can globally cache
20 //! it and use it between calls to the API, avoiding expensive loads/unloads. If
21 //! this is a problem for leak detectors or something like that we can cross the
22 //! bridge when we get there.
24 #![allow(non_snake_case)]
26 use crate::windows
::*;
30 // Work around `SymGetOptions` and `SymSetOptions` not being present in winapi
31 // itself. Otherwise this is only used when we're double-checking types against
33 #[cfg(feature = "verify-winapi")]
35 use crate::windows
::*;
36 pub use winapi
::um
::dbghelp
::{
37 StackWalk64
, SymCleanup
, SymFromAddrW
, SymFunctionTableAccess64
, SymGetLineFromAddrW64
,
38 SymGetModuleBase64
, SymInitializeW
,
42 // Not defined in winapi yet
43 pub fn SymGetOptions() -> u32;
44 pub fn SymSetOptions(_
: u32);
46 // This is defined in winapi, but it's incorrect (FIXME winapi-rs#768)
51 StackFrame
: LPSTACKFRAME_EX
,
53 ReadMemoryRoutine
: PREAD_PROCESS_MEMORY_ROUTINE64
,
54 FunctionTableAccessRoutine
: PFUNCTION_TABLE_ACCESS_ROUTINE64
,
55 GetModuleBaseRoutine
: PGET_MODULE_BASE_ROUTINE64
,
56 TranslateAddress
: PTRANSLATE_ADDRESS_ROUTINE64
,
60 // Not defined in winapi yet
61 pub fn SymFromInlineContextW(
65 Displacement
: PDWORD64
,
66 Symbol
: PSYMBOL_INFOW
,
68 pub fn SymGetLineFromInlineContextW(
72 qwModuleBaseAddress
: DWORD64
,
73 pdwDisplacement
: PDWORD
,
74 Line
: PIMAGEHLP_LINEW64
,
78 pub fn assert_equal_types
<T
>(a
: T
, _b
: T
) -> T
{
83 // This macro is used to define a `Dbghelp` structure which internally contains
84 // all the function pointers that we might load.
85 macro_rules
! dbghelp
{
87 $
(fn $name
:ident($
($arg
:ident
: $argty
:ty
),*) -> $ret
: ty
;)*
90 /// The loaded DLL for `dbghelp.dll`
93 // Each function pointer for each function we might use
97 static mut DBGHELP
: Dbghelp
= Dbghelp
{
98 // Initially we haven't loaded the DLL
100 // Initiall all functions are set to zero to say they need to be
101 // dynamically loaded.
105 // Convenience typedef for each function type.
106 $
(pub type $name
= unsafe extern "system" fn($
($argty
),*) -> $ret
;)*
109 /// Attempts to open `dbghelp.dll`. Returns success if it works or
110 /// error if `LoadLibraryW` fails.
112 /// Panics if library is already loaded.
113 fn ensure_open(&mut self) -> Result
<(), ()> {
114 if !self.dll
.is_null() {
117 let lib
= b
"dbghelp.dll\0";
119 self.dll
= LoadLibraryA(lib
.as_ptr() as *const i8);
120 if self.dll
.is_null() {
128 // Function for each method we'd like to use. When called it will
129 // either read the cached function pointer or load it and return the
130 // loaded value. Loads are asserted to succeed.
131 $
(pub fn $
name(&mut self) -> Option
<$name
> {
134 let name
= concat
!(stringify
!($name
), "\0");
135 self.$name
= self.symbol(name
.as_bytes())?
;
137 let ret
= mem
::transmute
::<usize, $name
>(self.$name
);
138 #[cfg(feature = "verify-winapi")]
139 dbghelp
::assert_equal_types(ret
, dbghelp
::$name
);
144 fn symbol(&self, symbol
: &[u8]) -> Option
<usize> {
146 match GetProcAddress(self.dll
, symbol
.as_ptr() as *const _
) as usize {
154 // Convenience proxy to use the cleanup locks to reference dbghelp
158 $
(pub fn $
name(&self) -> $name
{
160 DBGHELP
.$
name().unwrap()
164 pub fn dbghelp(&self) -> *mut Dbghelp
{
174 const SYMOPT_DEFERRED_LOADS
: DWORD
= 0x00000004;
178 fn SymGetOptions() -> DWORD
;
179 fn SymSetOptions(options
: DWORD
) -> ();
185 fn SymCleanup(handle
: HANDLE
) -> BOOL
;
190 StackFrame
: LPSTACKFRAME64
,
191 ContextRecord
: PVOID
,
192 ReadMemoryRoutine
: PREAD_PROCESS_MEMORY_ROUTINE64
,
193 FunctionTableAccessRoutine
: PFUNCTION_TABLE_ACCESS_ROUTINE64
,
194 GetModuleBaseRoutine
: PGET_MODULE_BASE_ROUTINE64
,
195 TranslateAddress
: PTRANSLATE_ADDRESS_ROUTINE64
197 fn SymFunctionTableAccess64(
201 fn SymGetModuleBase64(
208 Displacement
: PDWORD64
,
209 Symbol
: PSYMBOL_INFOW
211 fn SymGetLineFromAddrW64(
214 pdwDisplacement
: PDWORD
,
215 Line
: PIMAGEHLP_LINEW64
221 StackFrame
: LPSTACKFRAME_EX
,
222 ContextRecord
: PVOID
,
223 ReadMemoryRoutine
: PREAD_PROCESS_MEMORY_ROUTINE64
,
224 FunctionTableAccessRoutine
: PFUNCTION_TABLE_ACCESS_ROUTINE64
,
225 GetModuleBaseRoutine
: PGET_MODULE_BASE_ROUTINE64
,
226 TranslateAddress
: PTRANSLATE_ADDRESS_ROUTINE64
,
229 fn SymFromInlineContextW(
232 InlineContext
: ULONG
,
233 Displacement
: PDWORD64
,
234 Symbol
: PSYMBOL_INFOW
236 fn SymGetLineFromInlineContextW(
239 InlineContext
: ULONG
,
240 qwModuleBaseAddress
: DWORD64
,
241 pdwDisplacement
: PDWORD
,
242 Line
: PIMAGEHLP_LINEW64
249 /// Unsafe because this requires external synchronization, must be done
250 /// inside of the same lock as all other backtrace operations.
252 /// Note that the `Dbghelp` returned must also be dropped within the same
254 #[cfg(all(windows, feature = "dbghelp"))]
255 pub unsafe fn init() -> Result
<Init
, ()> {
256 // Calling `SymInitializeW` is quite expensive, so we only do so once per
258 static mut INITIALIZED
: bool
= false;
263 // Actually load `dbghelp.dll` into the process here, returning an error if
265 DBGHELP
.ensure_open()?
;
267 let orig
= DBGHELP
.SymGetOptions().unwrap()();
269 // Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because
270 // according to MSVC's own docs about this: "This is the fastest, most
271 // efficient way to use the symbol handler.", so let's do that!
272 DBGHELP
.SymSetOptions().unwrap()(orig
| SYMOPT_DEFERRED_LOADS
);
274 // Actually initialize symbols with MSVC. Note that this can fail, but we
275 // ignore it. There's not a ton of prior art for this per se, but LLVM
276 // internally seems to ignore the return value here and one of the
277 // sanitizer libraries in LLVM prints a scary warning if this fails but
278 // basically ignores it in the long run.
280 // One case this comes up a lot for Rust is that the standard library and
281 // this crate on crates.io both want to compete for `SymInitializeW`. The
282 // standard library historically wanted to initialize then cleanup most of
283 // the time, but now that it's using this crate it means that someone will
284 // get to initialization first and the other will pick up that
286 DBGHELP
.SymInitializeW().unwrap()(GetCurrentProcess(), ptr
::null_mut(), TRUE
);