]> git.proxmox.com Git - rustc.git/blob - vendor/backtrace/src/dbghelp.rs
New upstream version 1.38.0+dfsg1
[rustc.git] / vendor / backtrace / src / dbghelp.rs
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 crate::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, SymCleanup, SymFromAddrW, SymFunctionTableAccess64, SymGetLineFromAddrW64,
38 SymGetModuleBase64, SymInitializeW,
39 };
40
41 extern "system" {
42 // Not defined in winapi yet
43 pub fn SymGetOptions() -> u32;
44 pub fn SymSetOptions(_: u32);
45
46 // This is defined in winapi, but it's incorrect (FIXME winapi-rs#768)
47 pub fn StackWalkEx(
48 MachineType: DWORD,
49 hProcess: HANDLE,
50 hThread: HANDLE,
51 StackFrame: LPSTACKFRAME_EX,
52 ContextRecord: PVOID,
53 ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64,
54 FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64,
55 GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64,
56 TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64,
57 Flags: DWORD,
58 ) -> BOOL;
59
60 // Not defined in winapi yet
61 pub fn SymFromInlineContextW(
62 hProcess: HANDLE,
63 Address: DWORD64,
64 InlineContext: ULONG,
65 Displacement: PDWORD64,
66 Symbol: PSYMBOL_INFOW,
67 ) -> BOOL;
68 pub fn SymGetLineFromInlineContextW(
69 hProcess: HANDLE,
70 dwAddr: DWORD64,
71 InlineContext: ULONG,
72 qwModuleBaseAddress: DWORD64,
73 pdwDisplacement: PDWORD,
74 Line: PIMAGEHLP_LINEW64,
75 ) -> BOOL;
76 }
77
78 pub fn assert_equal_types<T>(a: T, _b: T) -> T {
79 a
80 }
81 }
82
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 {
86 (extern "system" {
87 $(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)*
88 }) => (
89 pub struct Dbghelp {
90 /// The loaded DLL for `dbghelp.dll`
91 dll: HMODULE,
92
93 // Each function pointer for each function we might use
94 $($name: usize,)*
95 }
96
97 static mut DBGHELP: Dbghelp = Dbghelp {
98 // Initially we haven't loaded the DLL
99 dll: 0 as *mut _,
100 // Initiall all functions are set to zero to say they need to be
101 // dynamically loaded.
102 $($name: 0,)*
103 };
104
105 // Convenience typedef for each function type.
106 $(pub type $name = unsafe extern "system" fn($($argty),*) -> $ret;)*
107
108 impl Dbghelp {
109 /// Attempts to open `dbghelp.dll`. Returns success if it works or
110 /// error if `LoadLibraryW` fails.
111 ///
112 /// Panics if library is already loaded.
113 fn ensure_open(&mut self) -> Result<(), ()> {
114 if !self.dll.is_null() {
115 return Ok(())
116 }
117 let lib = b"dbghelp.dll\0";
118 unsafe {
119 self.dll = LoadLibraryA(lib.as_ptr() as *const i8);
120 if self.dll.is_null() {
121 Err(())
122 } else {
123 Ok(())
124 }
125 }
126 }
127
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> {
132 unsafe {
133 if self.$name == 0 {
134 let name = concat!(stringify!($name), "\0");
135 self.$name = self.symbol(name.as_bytes())?;
136 }
137 let ret = mem::transmute::<usize, $name>(self.$name);
138 #[cfg(feature = "verify-winapi")]
139 dbghelp::assert_equal_types(ret, dbghelp::$name);
140 Some(ret)
141 }
142 })*
143
144 fn symbol(&self, symbol: &[u8]) -> Option<usize> {
145 unsafe {
146 match GetProcAddress(self.dll, symbol.as_ptr() as *const _) as usize {
147 0 => None,
148 n => Some(n),
149 }
150 }
151 }
152 }
153
154 // Convenience proxy to use the cleanup locks to reference dbghelp
155 // functions.
156 #[allow(dead_code)]
157 impl Init {
158 $(pub fn $name(&self) -> $name {
159 unsafe {
160 DBGHELP.$name().unwrap()
161 }
162 })*
163
164 pub fn dbghelp(&self) -> *mut Dbghelp {
165 unsafe {
166 &mut DBGHELP
167 }
168 }
169 }
170 )
171
172 }
173
174 const SYMOPT_DEFERRED_LOADS: DWORD = 0x00000004;
175
176 dbghelp! {
177 extern "system" {
178 fn SymGetOptions() -> DWORD;
179 fn SymSetOptions(options: DWORD) -> ();
180 fn SymInitializeW(
181 handle: HANDLE,
182 path: PCWSTR,
183 invade: BOOL
184 ) -> BOOL;
185 fn SymCleanup(handle: HANDLE) -> BOOL;
186 fn StackWalk64(
187 MachineType: DWORD,
188 hProcess: HANDLE,
189 hThread: HANDLE,
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
196 ) -> BOOL;
197 fn SymFunctionTableAccess64(
198 hProcess: HANDLE,
199 AddrBase: DWORD64
200 ) -> PVOID;
201 fn SymGetModuleBase64(
202 hProcess: HANDLE,
203 AddrBase: DWORD64
204 ) -> DWORD64;
205 fn SymFromAddrW(
206 hProcess: HANDLE,
207 Address: DWORD64,
208 Displacement: PDWORD64,
209 Symbol: PSYMBOL_INFOW
210 ) -> BOOL;
211 fn SymGetLineFromAddrW64(
212 hProcess: HANDLE,
213 dwAddr: DWORD64,
214 pdwDisplacement: PDWORD,
215 Line: PIMAGEHLP_LINEW64
216 ) -> BOOL;
217 fn StackWalkEx(
218 MachineType: DWORD,
219 hProcess: HANDLE,
220 hThread: HANDLE,
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,
227 Flags: DWORD
228 ) -> BOOL;
229 fn SymFromInlineContextW(
230 hProcess: HANDLE,
231 Address: DWORD64,
232 InlineContext: ULONG,
233 Displacement: PDWORD64,
234 Symbol: PSYMBOL_INFOW
235 ) -> BOOL;
236 fn SymGetLineFromInlineContextW(
237 hProcess: HANDLE,
238 dwAddr: DWORD64,
239 InlineContext: ULONG,
240 qwModuleBaseAddress: DWORD64,
241 pdwDisplacement: PDWORD,
242 Line: PIMAGEHLP_LINEW64
243 ) -> BOOL;
244 }
245 }
246
247 pub struct Init;
248
249 /// Unsafe because this requires external synchronization, must be done
250 /// inside of the same lock as all other backtrace operations.
251 ///
252 /// Note that the `Dbghelp` returned must also be dropped within the same
253 /// lock.
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
257 // process.
258 static mut INITIALIZED: bool = false;
259 if INITIALIZED {
260 return Ok(Init);
261 }
262
263 // Actually load `dbghelp.dll` into the process here, returning an error if
264 // that fails.
265 DBGHELP.ensure_open()?;
266
267 let orig = DBGHELP.SymGetOptions().unwrap()();
268
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);
273
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.
279 //
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
285 // initialization.
286 DBGHELP.SymInitializeW().unwrap()(GetCurrentProcess(), ptr::null_mut(), TRUE);
287 Ok(Init)
288 }