]> git.proxmox.com Git - rustc.git/blame - library/backtrace/src/symbolize/dbghelp.rs
New upstream version 1.75.0+dfsg1
[rustc.git] / library / backtrace / src / symbolize / dbghelp.rs
CommitLineData
dc9dc135
XL
1//! Symbolication strategy using `dbghelp.dll` on Windows, only used for MSVC
2//!
3//! This symbolication strategy, like with backtraces, uses dynamically loaded
4//! information from `dbghelp.dll`. (see `src/dbghelp.rs` for info about why
5//! it's dynamically loaded).
6//!
7//! This API selects its resolution strategy based on the frame provided or the
8//! information we have at hand. If a frame from `StackWalkEx` is given to us
9//! then we use similar APIs to generate correct information about inlined
10//! functions. Otherwise if all we have is an address or an older stack frame
11//! from `StackWalk64` we use the older APIs for symbolication.
12//!
13//! There's a good deal of support in this module, but a good chunk of it is
14//! converting back and forth between Windows types and Rust types. For example
15//! symbols come to us as wide strings which we then convert to utf-8 strings if
16//! we can.
17
7cac9316
XL
18#![allow(bad_style)]
19
ed00b5ec 20use super::super::{dbghelp, windows::*};
3dfed10e 21use super::{BytesOrWideString, ResolveWhat, SymbolName};
0731742a 22use core::char;
416331ca
XL
23use core::ffi::c_void;
24use core::marker;
dc9dc135
XL
25use core::mem;
26use core::slice;
7cac9316 27
0731742a 28// Store an OsString on std so we can provide the symbol name and filename.
416331ca 29pub struct Symbol<'a> {
0731742a 30 name: *const [u8],
7cac9316
XL
31 addr: *mut c_void,
32 line: Option<u32>,
0731742a
XL
33 filename: Option<*const [u16]>,
34 #[cfg(feature = "std")]
35 _filename_cache: Option<::std::ffi::OsString>,
36 #[cfg(not(feature = "std"))]
37 _filename_cache: (),
416331ca 38 _marker: marker::PhantomData<&'a i32>,
7cac9316
XL
39}
40
416331ca 41impl Symbol<'_> {
f035d41b 42 pub fn name(&self) -> Option<SymbolName<'_>> {
0731742a 43 Some(SymbolName::new(unsafe { &*self.name }))
7cac9316
XL
44 }
45
46 pub fn addr(&self) -> Option<*mut c_void> {
47 Some(self.addr as *mut _)
48 }
49
f035d41b 50 pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
dc9dc135
XL
51 self.filename
52 .map(|slice| unsafe { BytesOrWideString::Wide(&*slice) })
7cac9316
XL
53 }
54
29967ef6
XL
55 pub fn colno(&self) -> Option<u32> {
56 None
57 }
58
7cac9316
XL
59 pub fn lineno(&self) -> Option<u32> {
60 self.line
61 }
0731742a
XL
62
63 #[cfg(feature = "std")]
dc9dc135
XL
64 pub fn filename(&self) -> Option<&::std::path::Path> {
65 use std::path::Path;
66
67 self._filename_cache.as_ref().map(Path::new)
0731742a 68 }
7cac9316
XL
69}
70
0731742a
XL
71#[repr(C, align(8))]
72struct Aligned8<T>(T);
73
f035d41b 74pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
dc9dc135
XL
75 // Ensure this process's symbols are initialized
76 let dbghelp = match dbghelp::init() {
77 Ok(dbghelp) => dbghelp,
78 Err(()) => return, // oh well...
79 };
80
ed00b5ec
FG
81 let resolve_inner = if (*dbghelp.dbghelp()).SymAddrIncludeInlineTrace().is_some() {
82 // We are on a version of dbghelp 6.2+, which contains the more modern
83 // Inline APIs.
84 resolve_with_inline
85 } else {
86 // We are on an older version of dbghelp which doesn't contain the Inline
87 // APIs.
88 resolve_legacy
89 };
dc9dc135 90 match what {
ed00b5ec
FG
91 ResolveWhat::Address(_) => resolve_inner(&dbghelp, what.address_or_ip(), None, cb),
92 ResolveWhat::Frame(frame) => {
93 resolve_inner(&dbghelp, frame.ip(), frame.inner.inline_context(), cb)
94 }
dc9dc135
XL
95 }
96}
97
ed00b5ec
FG
98/// Resolve the address using the legacy dbghelp API.
99///
100/// This should work all the way down to Windows XP. The inline context is
101/// ignored, since this concept was only introduced in dbghelp 6.2+.
102unsafe fn resolve_legacy(
416331ca 103 dbghelp: &dbghelp::Init,
ed00b5ec
FG
104 addr: *mut c_void,
105 _inline_context: Option<DWORD>,
f035d41b 106 cb: &mut dyn FnMut(&super::Symbol),
dc9dc135 107) {
ed00b5ec 108 let addr = super::adjust_ip(addr) as DWORD64;
dc9dc135 109 do_resolve(
ed00b5ec
FG
110 |info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr, &mut 0, info),
111 |line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr, &mut 0, line),
dc9dc135
XL
112 cb,
113 )
114}
115
ed00b5ec
FG
116/// Resolve the address using the modern dbghelp APIs.
117///
118/// Note that calling this function requires having dbghelp 6.2+ loaded - and
119/// will panic otherwise.
120unsafe fn resolve_with_inline(
416331ca 121 dbghelp: &dbghelp::Init,
dc9dc135 122 addr: *mut c_void,
ed00b5ec 123 inline_context: Option<DWORD>,
f035d41b 124 cb: &mut dyn FnMut(&super::Symbol),
dc9dc135 125) {
ed00b5ec
FG
126 let current_process = GetCurrentProcess();
127
128 let addr = super::adjust_ip(addr) as DWORD64;
129
130 let (inlined_frame_count, inline_context) = if let Some(ic) = inline_context {
131 (0, ic)
132 } else {
133 let mut inlined_frame_count = dbghelp.SymAddrIncludeInlineTrace()(current_process, addr);
134
135 let mut inline_context = 0;
136
137 // If there is are inlined frames but we can't load them for some reason OR if there are no
138 // inlined frames, then we disregard inlined_frame_count and inline_context.
139 if (inlined_frame_count > 0
140 && dbghelp.SymQueryInlineTrace()(
141 current_process,
142 addr,
143 0,
144 addr,
145 addr,
146 &mut inline_context,
147 &mut 0,
148 ) != TRUE)
149 || inlined_frame_count == 0
150 {
151 inlined_frame_count = 0;
152 inline_context = 0;
153 }
154
155 (inlined_frame_count, inline_context)
156 };
157
158 let last_inline_context = inline_context + 1 + inlined_frame_count;
159
160 for inline_context in inline_context..last_inline_context {
161 do_resolve(
162 |info| {
163 dbghelp.SymFromInlineContextW()(current_process, addr, inline_context, &mut 0, info)
164 },
165 |line| {
166 dbghelp.SymGetLineFromInlineContextW()(
167 current_process,
168 addr,
169 inline_context,
170 0,
171 &mut 0,
172 line,
173 )
174 },
175 cb,
176 );
177 }
dc9dc135
XL
178}
179
180unsafe fn do_resolve(
181 sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL,
182 get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL,
f035d41b 183 cb: &mut dyn FnMut(&super::Symbol),
dc9dc135 184) {
0731742a
XL
185 const SIZE: usize = 2 * MAX_SYM_NAME + mem::size_of::<SYMBOL_INFOW>();
186 let mut data = Aligned8([0u8; SIZE]);
187 let data = &mut data.0;
188 let info = &mut *(data.as_mut_ptr() as *mut SYMBOL_INFOW);
189 info.MaxNameLen = MAX_SYM_NAME as ULONG;
190 // the struct size in C. the value is different to
191 // `size_of::<SYMBOL_INFOW>() - MAX_SYM_NAME + 1` (== 81)
192 // due to struct alignment.
193 info.SizeOfStruct = 88;
194
dc9dc135
XL
195 if sym_from_addr(info) != TRUE {
196 return;
0731742a 197 }
ea8adc8c 198
0731742a
XL
199 // If the symbol name is greater than MaxNameLen, SymFromAddrW will
200 // give a buffer of (MaxNameLen - 1) characters and set NameLen to
201 // the real value.
dc9dc135 202 let name_len = ::core::cmp::min(info.NameLen as usize, info.MaxNameLen as usize - 1);
0731742a
XL
203 let name_ptr = info.Name.as_ptr() as *const u16;
204 let name = slice::from_raw_parts(name_ptr, name_len);
205
206 // Reencode the utf-16 symbol to utf-8 so we can use `SymbolName::new` like
207 // all other platforms
208 let mut name_len = 0;
209 let mut name_buffer = [0; 256];
210 {
211 let mut remaining = &mut name_buffer[..];
212 for c in char::decode_utf16(name.iter().cloned()) {
213 let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
214 let len = c.len_utf8();
215 if len < remaining.len() {
216 c.encode_utf8(remaining);
217 let tmp = remaining;
218 remaining = &mut tmp[len..];
219 name_len += len;
220 } else {
dc9dc135 221 break;
7cac9316 222 }
0731742a
XL
223 }
224 }
225 let name = &name_buffer[..name_len] as *const [u8];
226
227 let mut line = mem::zeroed::<IMAGEHLP_LINEW64>();
228 line.SizeOfStruct = mem::size_of::<IMAGEHLP_LINEW64>() as DWORD;
0731742a
XL
229
230 let mut filename = None;
231 let mut lineno = None;
dc9dc135 232 if get_line_from_addr(&mut line) == TRUE {
0731742a
XL
233 lineno = Some(line.LineNumber as u32);
234
235 let base = line.FileName;
236 let mut len = 0;
237 while *base.offset(len) != 0 {
238 len += 1;
7cac9316
XL
239 }
240
0731742a
XL
241 let len = len as usize;
242
243 filename = Some(slice::from_raw_parts(base, len) as *const [u16]);
7cac9316 244 }
0731742a 245
0731742a
XL
246 cb(&super::Symbol {
247 inner: Symbol {
248 name,
249 addr: info.Address as *mut _,
250 line: lineno,
251 filename,
252 _filename_cache: cache(filename),
416331ca 253 _marker: marker::PhantomData,
0731742a
XL
254 },
255 })
256}
257
258#[cfg(feature = "std")]
259unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> {
260 use std::os::windows::ffi::OsStringExt;
dc9dc135 261 filename.map(|f| ::std::ffi::OsString::from_wide(&*f))
0731742a
XL
262}
263
264#[cfg(not(feature = "std"))]
dc9dc135 265unsafe fn cache(_filename: Option<*const [u16]>) {}
fc512014
XL
266
267pub unsafe fn clear_symbol_cache() {}