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
18 #![allow(bad_style)]
19
20 use super::super::{backtrace::StackFrame, dbghelp, windows::*};
21 use super::{BytesOrWideString, ResolveWhat, SymbolName};
22 use core::char;
23 use core::ffi::c_void;
24 use core::marker;
25 use core::mem;
26 use core::slice;
27
28 // Store an OsString on std so we can provide the symbol name and filename.
29 pub struct Symbol<'a> {
30 name: *const [u8],
31 addr: *mut c_void,
32 line: Option<u32>,
33 filename: Option<*const [u16]>,
34 #[cfg(feature = "std")]
35 _filename_cache: Option<::std::ffi::OsString>,
36 #[cfg(not(feature = "std"))]
37 _filename_cache: (),
38 _marker: marker::PhantomData<&'a i32>,
39 }
40
41 impl Symbol<'_> {
name(&self) -> Option<SymbolName<'_>>42 pub fn name(&self) -> Option<SymbolName<'_>> {
43 Some(SymbolName::new(unsafe { &*self.name }))
44 }
45
addr(&self) -> Option<*mut c_void>46 pub fn addr(&self) -> Option<*mut c_void> {
47 Some(self.addr as *mut _)
48 }
49
filename_raw(&self) -> Option<BytesOrWideString<'_>>50 pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
51 self.filename
52 .map(|slice| unsafe { BytesOrWideString::Wide(&*slice) })
53 }
54
colno(&self) -> Option<u32>55 pub fn colno(&self) -> Option<u32> {
56 None
57 }
58
lineno(&self) -> Option<u32>59 pub fn lineno(&self) -> Option<u32> {
60 self.line
61 }
62
63 #[cfg(feature = "std")]
filename(&self) -> Option<&::std::path::Path>64 pub fn filename(&self) -> Option<&::std::path::Path> {
65 use std::path::Path;
66
67 self._filename_cache.as_ref().map(Path::new)
68 }
69 }
70
71 #[repr(C, align(8))]
72 struct Aligned8<T>(T);
73
resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol))74 pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
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
81 match what {
82 ResolveWhat::Address(_) => resolve_without_inline(&dbghelp, what.address_or_ip(), cb),
83 ResolveWhat::Frame(frame) => match &frame.inner.stack_frame {
84 StackFrame::New(frame) => resolve_with_inline(&dbghelp, frame, cb),
85 StackFrame::Old(_) => resolve_without_inline(&dbghelp, frame.ip(), cb),
86 },
87 }
88 }
89
resolve_with_inline( dbghelp: &dbghelp::Init, frame: &STACKFRAME_EX, cb: &mut dyn FnMut(&super::Symbol), )90 unsafe fn resolve_with_inline(
91 dbghelp: &dbghelp::Init,
92 frame: &STACKFRAME_EX,
93 cb: &mut dyn FnMut(&super::Symbol),
94 ) {
95 do_resolve(
96 |info| {
97 dbghelp.SymFromInlineContextW()(
98 GetCurrentProcess(),
99 super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
100 frame.InlineFrameContext,
101 &mut 0,
102 info,
103 )
104 },
105 |line| {
106 dbghelp.SymGetLineFromInlineContextW()(
107 GetCurrentProcess(),
108 super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
109 frame.InlineFrameContext,
110 0,
111 &mut 0,
112 line,
113 )
114 },
115 cb,
116 )
117 }
118
resolve_without_inline( dbghelp: &dbghelp::Init, addr: *mut c_void, cb: &mut dyn FnMut(&super::Symbol), )119 unsafe fn resolve_without_inline(
120 dbghelp: &dbghelp::Init,
121 addr: *mut c_void,
122 cb: &mut dyn FnMut(&super::Symbol),
123 ) {
124 do_resolve(
125 |info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr as DWORD64, &mut 0, info),
126 |line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr as DWORD64, &mut 0, line),
127 cb,
128 )
129 }
130
do_resolve( sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL, get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL, cb: &mut dyn FnMut(&super::Symbol), )131 unsafe fn do_resolve(
132 sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL,
133 get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL,
134 cb: &mut dyn FnMut(&super::Symbol),
135 ) {
136 const SIZE: usize = 2 * MAX_SYM_NAME + mem::size_of::<SYMBOL_INFOW>();
137 let mut data = Aligned8([0u8; SIZE]);
138 let data = &mut data.0;
139 let info = &mut *(data.as_mut_ptr() as *mut SYMBOL_INFOW);
140 info.MaxNameLen = MAX_SYM_NAME as ULONG;
141 // the struct size in C. the value is different to
142 // `size_of::<SYMBOL_INFOW>() - MAX_SYM_NAME + 1` (== 81)
143 // due to struct alignment.
144 info.SizeOfStruct = 88;
145
146 if sym_from_addr(info) != TRUE {
147 return;
148 }
149
150 // If the symbol name is greater than MaxNameLen, SymFromAddrW will
151 // give a buffer of (MaxNameLen - 1) characters and set NameLen to
152 // the real value.
153 let name_len = ::core::cmp::min(info.NameLen as usize, info.MaxNameLen as usize - 1);
154 let name_ptr = info.Name.as_ptr() as *const u16;
155 let name = slice::from_raw_parts(name_ptr, name_len);
156
157 // Reencode the utf-16 symbol to utf-8 so we can use `SymbolName::new` like
158 // all other platforms
159 let mut name_len = 0;
160 let mut name_buffer = [0; 256];
161 {
162 let mut remaining = &mut name_buffer[..];
163 for c in char::decode_utf16(name.iter().cloned()) {
164 let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
165 let len = c.len_utf8();
166 if len < remaining.len() {
167 c.encode_utf8(remaining);
168 let tmp = remaining;
169 remaining = &mut tmp[len..];
170 name_len += len;
171 } else {
172 break;
173 }
174 }
175 }
176 let name = &name_buffer[..name_len] as *const [u8];
177
178 let mut line = mem::zeroed::<IMAGEHLP_LINEW64>();
179 line.SizeOfStruct = mem::size_of::<IMAGEHLP_LINEW64>() as DWORD;
180
181 let mut filename = None;
182 let mut lineno = None;
183 if get_line_from_addr(&mut line) == TRUE {
184 lineno = Some(line.LineNumber as u32);
185
186 let base = line.FileName;
187 let mut len = 0;
188 while *base.offset(len) != 0 {
189 len += 1;
190 }
191
192 let len = len as usize;
193
194 filename = Some(slice::from_raw_parts(base, len) as *const [u16]);
195 }
196
197 cb(&super::Symbol {
198 inner: Symbol {
199 name,
200 addr: info.Address as *mut _,
201 line: lineno,
202 filename,
203 _filename_cache: cache(filename),
204 _marker: marker::PhantomData,
205 },
206 })
207 }
208
209 #[cfg(feature = "std")]
cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString>210 unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> {
211 use std::os::windows::ffi::OsStringExt;
212 filename.map(|f| ::std::ffi::OsString::from_wide(&*f))
213 }
214
215 #[cfg(not(feature = "std"))]
cache(_filename: Option<*const [u16]>)216 unsafe fn cache(_filename: Option<*const [u16]>) {}
217
clear_symbol_cache()218 pub unsafe fn clear_symbol_cache() {}
219