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