1 //! A module to assist in managing dbghelp bindings on Windows
2 //!
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.
10 //!
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.
17 //!
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.
23 
24 #![allow(non_snake_case)]
25 
26 use super::windows::*;
27 use core::mem;
28 use core::ptr;
29 
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
32 // winapi.
33 #[cfg(feature = "verify-winapi")]
34 mod dbghelp {
35     use crate::windows::*;
36     pub use winapi::um::dbghelp::{
37         StackWalk64, StackWalkEx, SymCleanup, SymFromAddrW, SymFunctionTableAccess64,
38         SymGetLineFromAddrW64, SymGetModuleBase64, SymGetOptions, SymInitializeW, SymSetOptions,
39     };
40 
41     extern "system" {
42         // Not defined in winapi yet
SymFromInlineContextW( hProcess: HANDLE, Address: DWORD64, InlineContext: ULONG, Displacement: PDWORD64, Symbol: PSYMBOL_INFOW, ) -> BOOL43         pub fn SymFromInlineContextW(
44             hProcess: HANDLE,
45             Address: DWORD64,
46             InlineContext: ULONG,
47             Displacement: PDWORD64,
48             Symbol: PSYMBOL_INFOW,
49         ) -> BOOL;
SymGetLineFromInlineContextW( hProcess: HANDLE, dwAddr: DWORD64, InlineContext: ULONG, qwModuleBaseAddress: DWORD64, pdwDisplacement: PDWORD, Line: PIMAGEHLP_LINEW64, ) -> BOOL50         pub fn SymGetLineFromInlineContextW(
51             hProcess: HANDLE,
52             dwAddr: DWORD64,
53             InlineContext: ULONG,
54             qwModuleBaseAddress: DWORD64,
55             pdwDisplacement: PDWORD,
56             Line: PIMAGEHLP_LINEW64,
57         ) -> BOOL;
58     }
59 
assert_equal_types<T>(a: T, _b: T) -> T60     pub fn assert_equal_types<T>(a: T, _b: T) -> T {
61         a
62     }
63 }
64 
65 // This macro is used to define a `Dbghelp` structure which internally contains
66 // all the function pointers that we might load.
67 macro_rules! dbghelp {
68     (extern "system" {
69         $(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)*
70     }) => (
71         pub struct Dbghelp {
72             /// The loaded DLL for `dbghelp.dll`
73             dll: HMODULE,
74 
75             // Each function pointer for each function we might use
76             $($name: usize,)*
77         }
78 
79         static mut DBGHELP: Dbghelp = Dbghelp {
80             // Initially we haven't loaded the DLL
81             dll: 0 as *mut _,
82             // Initiall all functions are set to zero to say they need to be
83             // dynamically loaded.
84             $($name: 0,)*
85         };
86 
87         // Convenience typedef for each function type.
88         $(pub type $name = unsafe extern "system" fn($($argty),*) -> $ret;)*
89 
90         impl Dbghelp {
91             /// Attempts to open `dbghelp.dll`. Returns success if it works or
92             /// error if `LoadLibraryW` fails.
93             ///
94             /// Panics if library is already loaded.
95             fn ensure_open(&mut self) -> Result<(), ()> {
96                 if !self.dll.is_null() {
97                     return Ok(())
98                 }
99                 let lib = b"dbghelp.dll\0";
100                 unsafe {
101                     self.dll = LoadLibraryA(lib.as_ptr() as *const i8);
102                     if self.dll.is_null() {
103                         Err(())
104                     }  else {
105                         Ok(())
106                     }
107                 }
108             }
109 
110             // Function for each method we'd like to use. When called it will
111             // either read the cached function pointer or load it and return the
112             // loaded value. Loads are asserted to succeed.
113             $(pub fn $name(&mut self) -> Option<$name> {
114                 unsafe {
115                     if self.$name == 0 {
116                         let name = concat!(stringify!($name), "\0");
117                         self.$name = self.symbol(name.as_bytes())?;
118                     }
119                     let ret = mem::transmute::<usize, $name>(self.$name);
120                     #[cfg(feature = "verify-winapi")]
121                     dbghelp::assert_equal_types(ret, dbghelp::$name);
122                     Some(ret)
123                 }
124             })*
125 
126             fn symbol(&self, symbol: &[u8]) -> Option<usize> {
127                 unsafe {
128                     match GetProcAddress(self.dll, symbol.as_ptr() as *const _) as usize {
129                         0 => None,
130                         n => Some(n),
131                     }
132                 }
133             }
134         }
135 
136         // Convenience proxy to use the cleanup locks to reference dbghelp
137         // functions.
138         #[allow(dead_code)]
139         impl Init {
140             $(pub fn $name(&self) -> $name {
141                 unsafe {
142                     DBGHELP.$name().unwrap()
143                 }
144             })*
145 
146             pub fn dbghelp(&self) -> *mut Dbghelp {
147                 unsafe {
148                     &mut DBGHELP
149                 }
150             }
151         }
152     )
153 
154 }
155 
156 const SYMOPT_DEFERRED_LOADS: DWORD = 0x00000004;
157 
158 dbghelp! {
159     extern "system" {
160         fn SymGetOptions() -> DWORD;
161         fn SymSetOptions(options: DWORD) -> DWORD;
162         fn SymInitializeW(
163             handle: HANDLE,
164             path: PCWSTR,
165             invade: BOOL
166         ) -> BOOL;
167         fn SymCleanup(handle: HANDLE) -> BOOL;
168         fn StackWalk64(
169             MachineType: DWORD,
170             hProcess: HANDLE,
171             hThread: HANDLE,
172             StackFrame: LPSTACKFRAME64,
173             ContextRecord: PVOID,
174             ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64,
175             FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64,
176             GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64,
177             TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64
178         ) -> BOOL;
179         fn SymFunctionTableAccess64(
180             hProcess: HANDLE,
181             AddrBase: DWORD64
182         ) -> PVOID;
183         fn SymGetModuleBase64(
184             hProcess: HANDLE,
185             AddrBase: DWORD64
186         ) -> DWORD64;
187         fn SymFromAddrW(
188             hProcess: HANDLE,
189             Address: DWORD64,
190             Displacement: PDWORD64,
191             Symbol: PSYMBOL_INFOW
192         ) -> BOOL;
193         fn SymGetLineFromAddrW64(
194             hProcess: HANDLE,
195             dwAddr: DWORD64,
196             pdwDisplacement: PDWORD,
197             Line: PIMAGEHLP_LINEW64
198         ) -> BOOL;
199         fn StackWalkEx(
200             MachineType: DWORD,
201             hProcess: HANDLE,
202             hThread: HANDLE,
203             StackFrame: LPSTACKFRAME_EX,
204             ContextRecord: PVOID,
205             ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64,
206             FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64,
207             GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64,
208             TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64,
209             Flags: DWORD
210         ) -> BOOL;
211         fn SymFromInlineContextW(
212             hProcess: HANDLE,
213             Address: DWORD64,
214             InlineContext: ULONG,
215             Displacement: PDWORD64,
216             Symbol: PSYMBOL_INFOW
217         ) -> BOOL;
218         fn SymGetLineFromInlineContextW(
219             hProcess: HANDLE,
220             dwAddr: DWORD64,
221             InlineContext: ULONG,
222             qwModuleBaseAddress: DWORD64,
223             pdwDisplacement: PDWORD,
224             Line: PIMAGEHLP_LINEW64
225         ) -> BOOL;
226     }
227 }
228 
229 pub struct Init {
230     lock: HANDLE,
231 }
232 
233 /// Initialize all support necessary to access `dbghelp` API functions from this
234 /// crate.
235 ///
236 /// Note that this function is **safe**, it internally has its own
237 /// synchronization. Also note that it is safe to call this function multiple
238 /// times recursively.
init() -> Result<Init, ()>239 pub fn init() -> Result<Init, ()> {
240     use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
241 
242     unsafe {
243         // First thing we need to do is to synchronize this function. This can
244         // be called concurrently from other threads or recursively within one
245         // thread. Note that it's trickier than that though because what we're
246         // using here, `dbghelp`, *also* needs to be synchronized with all other
247         // callers to `dbghelp` in this process.
248         //
249         // Typically there aren't really that many calls to `dbghelp` within the
250         // same process and we can probably safely assume that we're the only
251         // ones accessing it. There is, however, one primary other user we have
252         // to worry about which is ironically ourselves, but in the standard
253         // library. The Rust standard library depends on this crate for
254         // backtrace support, and this crate also exists on crates.io. This
255         // means that if the standard library is printing a panic backtrace it
256         // may race with this crate coming from crates.io, causing segfaults.
257         //
258         // To help solve this synchronization problem we employ a
259         // Windows-specific trick here (it is, after all, a Windows-specific
260         // restriction about synchronization). We create a *session-local* named
261         // mutex to protect this call. The intention here is that the standard
262         // library and this crate don't have to share Rust-level APIs to
263         // synchronize here but can instead work behind the scenes to make sure
264         // they're synchronizing with one another. That way when this function
265         // is called through the standard library or through crates.io we can be
266         // sure that the same mutex is being acquired.
267         //
268         // So all of that is to say that the first thing we do here is we
269         // atomically create a `HANDLE` which is a named mutex on Windows. We
270         // synchronize a bit with other threads sharing this function
271         // specifically and ensure that only one handle is created per instance
272         // of this function. Note that the handle is never closed once it's
273         // stored in the global.
274         //
275         // After we've actually go the lock we simply acquire it, and our `Init`
276         // handle we hand out will be responsible for dropping it eventually.
277         static LOCK: AtomicUsize = AtomicUsize::new(0);
278         let mut lock = LOCK.load(SeqCst);
279         if lock == 0 {
280             lock = CreateMutexA(
281                 ptr::null_mut(),
282                 0,
283                 "Local\\RustBacktraceMutex\0".as_ptr() as _,
284             ) as usize;
285             if lock == 0 {
286                 return Err(());
287             }
288             if let Err(other) = LOCK.compare_exchange(0, lock, SeqCst, SeqCst) {
289                 debug_assert!(other != 0);
290                 CloseHandle(lock as HANDLE);
291                 lock = other;
292             }
293         }
294         debug_assert!(lock != 0);
295         let lock = lock as HANDLE;
296         let r = WaitForSingleObjectEx(lock, INFINITE, FALSE);
297         debug_assert_eq!(r, 0);
298         let ret = Init { lock };
299 
300         // Ok, phew! Now that we're all safely synchronized, let's actually
301         // start processing everything. First up we need to ensure that
302         // `dbghelp.dll` is actually loaded in this process. We do this
303         // dynamically to avoid a static dependency. This has historically been
304         // done to work around weird linking issues and is intended at making
305         // binaries a bit more portable since this is largely just a debugging
306         // utility.
307         //
308         // Once we've opened `dbghelp.dll` we need to call some initialization
309         // functions in it, and that's detailed more below. We only do this
310         // once, though, so we've got a global boolean indicating whether we're
311         // done yet or not.
312         DBGHELP.ensure_open()?;
313 
314         static mut INITIALIZED: bool = false;
315         if INITIALIZED {
316             return Ok(ret);
317         }
318 
319         let orig = DBGHELP.SymGetOptions().unwrap()();
320 
321         // Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because
322         // according to MSVC's own docs about this: "This is the fastest, most
323         // efficient way to use the symbol handler.", so let's do that!
324         DBGHELP.SymSetOptions().unwrap()(orig | SYMOPT_DEFERRED_LOADS);
325 
326         // Actually initialize symbols with MSVC. Note that this can fail, but we
327         // ignore it. There's not a ton of prior art for this per se, but LLVM
328         // internally seems to ignore the return value here and one of the
329         // sanitizer libraries in LLVM prints a scary warning if this fails but
330         // basically ignores it in the long run.
331         //
332         // One case this comes up a lot for Rust is that the standard library and
333         // this crate on crates.io both want to compete for `SymInitializeW`. The
334         // standard library historically wanted to initialize then cleanup most of
335         // the time, but now that it's using this crate it means that someone will
336         // get to initialization first and the other will pick up that
337         // initialization.
338         DBGHELP.SymInitializeW().unwrap()(GetCurrentProcess(), ptr::null_mut(), TRUE);
339         INITIALIZED = true;
340         Ok(ret)
341     }
342 }
343 
344 impl Drop for Init {
drop(&mut self)345     fn drop(&mut self) {
346         unsafe {
347             let r = ReleaseMutex(self.lock);
348             debug_assert!(r != 0);
349         }
350     }
351 }
352