1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Backtrace strategy for MSVC platforms.
12 //!
13 //! This module contains the ability to generate a backtrace on MSVC using one
14 //! of two possible methods. The `StackWalkEx` function is primarily used if
15 //! possible, but not all systems have that. Failing that the `StackWalk64`
16 //! function is used instead. Note that `StackWalkEx` is favored because it
17 //! handles debuginfo internally and returns inline frame information.
18 //!
19 //! Note that all dbghelp support is loaded dynamically, see `src/dbghelp.rs`
20 //! for more information about that.
21
22 #![allow(bad_style)]
23
24 use crate::dbghelp;
25 use crate::windows::*;
26 use core::ffi::c_void;
27 use core::mem;
28
29 #[derive(Clone, Copy)]
30 pub enum Frame {
31 New(STACKFRAME_EX),
32 Old(STACKFRAME64),
33 }
34
35 // we're just sending around raw pointers and reading them, never interpreting
36 // them so this should be safe to both send and share across threads.
37 unsafe impl Send for Frame {}
38 unsafe impl Sync for Frame {}
39
40 impl Frame {
ip(&self) -> *mut c_void41 pub fn ip(&self) -> *mut c_void {
42 self.addr_pc().Offset as *mut _
43 }
44
sp(&self) -> *mut c_void45 pub fn sp(&self) -> *mut c_void {
46 self.addr_stack().Offset as *mut _
47 }
48
symbol_address(&self) -> *mut c_void49 pub fn symbol_address(&self) -> *mut c_void {
50 self.ip()
51 }
52
addr_pc(&self) -> &ADDRESS6453 fn addr_pc(&self) -> &ADDRESS64 {
54 match self {
55 Frame::New(new) => &new.AddrPC,
56 Frame::Old(old) => &old.AddrPC,
57 }
58 }
59
addr_pc_mut(&mut self) -> &mut ADDRESS6460 fn addr_pc_mut(&mut self) -> &mut ADDRESS64 {
61 match self {
62 Frame::New(new) => &mut new.AddrPC,
63 Frame::Old(old) => &mut old.AddrPC,
64 }
65 }
66
addr_frame_mut(&mut self) -> &mut ADDRESS6467 fn addr_frame_mut(&mut self) -> &mut ADDRESS64 {
68 match self {
69 Frame::New(new) => &mut new.AddrFrame,
70 Frame::Old(old) => &mut old.AddrFrame,
71 }
72 }
73
addr_stack(&self) -> &ADDRESS6474 fn addr_stack(&self) -> &ADDRESS64 {
75 match self {
76 Frame::New(new) => &new.AddrStack,
77 Frame::Old(old) => &old.AddrStack,
78 }
79 }
80
addr_stack_mut(&mut self) -> &mut ADDRESS6481 fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
82 match self {
83 Frame::New(new) => &mut new.AddrStack,
84 Frame::Old(old) => &mut old.AddrStack,
85 }
86 }
87 }
88
89 #[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in winapi right now
90 struct MyContext(CONTEXT);
91
92 #[inline(always)]
trace(cb: &mut dyn FnMut(&super::Frame) -> bool)93 pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
94 // Allocate necessary structures for doing the stack walk
95 let process = GetCurrentProcess();
96 let thread = GetCurrentThread();
97
98 let mut context = mem::zeroed::<MyContext>();
99 RtlCaptureContext(&mut context.0);
100
101 // Ensure this process's symbols are initialized
102 let dbghelp = match dbghelp::init() {
103 Ok(dbghelp) => dbghelp,
104 Err(()) => return, // oh well...
105 };
106
107 // On x86_64 and ARM64 we opt to not use the default `Sym*` functions from
108 // dbghelp for getting the function table and module base. Instead we use
109 // the `RtlLookupFunctionEntry` function in kernel32 which will account for
110 // JIT compiler frames as well. These should be equivalent, but using
111 // `Rtl*` allows us to backtrace through JIT frames.
112 //
113 // Note that `RtlLookupFunctionEntry` only works for in-process backtraces,
114 // but that's all we support anyway, so it all lines up well.
115 cfg_if::cfg_if! {
116 if #[cfg(target_pointer_width = "64")] {
117 use core::ptr;
118
119 unsafe extern "system" fn function_table_access(_process: HANDLE, addr: DWORD64) -> PVOID {
120 let mut base = 0;
121 RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut()).cast()
122 }
123
124 unsafe extern "system" fn get_module_base(_process: HANDLE, addr: DWORD64) -> DWORD64 {
125 let mut base = 0;
126 RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut());
127 base
128 }
129 } else {
130 let function_table_access = dbghelp.SymFunctionTableAccess64();
131 let get_module_base = dbghelp.SymGetModuleBase64();
132 }
133 }
134
135 // Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64`
136 // since it's in theory supported on more systems.
137 match (*dbghelp.dbghelp()).StackWalkEx() {
138 Some(StackWalkEx) => {
139 let mut frame = super::Frame {
140 inner: Frame::New(mem::zeroed()),
141 };
142 let image = init_frame(&mut frame.inner, &context.0);
143 let frame_ptr = match &mut frame.inner {
144 Frame::New(ptr) => ptr as *mut STACKFRAME_EX,
145 _ => unreachable!(),
146 };
147
148 while StackWalkEx(
149 image as DWORD,
150 process,
151 thread,
152 frame_ptr,
153 &mut context.0 as *mut CONTEXT as *mut _,
154 None,
155 Some(function_table_access),
156 Some(get_module_base),
157 None,
158 0,
159 ) == TRUE
160 {
161 if !cb(&frame) {
162 break;
163 }
164 }
165 }
166 None => {
167 let mut frame = super::Frame {
168 inner: Frame::Old(mem::zeroed()),
169 };
170 let image = init_frame(&mut frame.inner, &context.0);
171 let frame_ptr = match &mut frame.inner {
172 Frame::Old(ptr) => ptr as *mut STACKFRAME64,
173 _ => unreachable!(),
174 };
175
176 while dbghelp.StackWalk64()(
177 image as DWORD,
178 process,
179 thread,
180 frame_ptr,
181 &mut context.0 as *mut CONTEXT as *mut _,
182 None,
183 Some(function_table_access),
184 Some(get_module_base),
185 None,
186 ) == TRUE
187 {
188 if !cb(&frame) {
189 break;
190 }
191 }
192 }
193 }
194 }
195
196 #[cfg(target_arch = "x86_64")]
init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD197 fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
198 frame.addr_pc_mut().Offset = ctx.Rip as u64;
199 frame.addr_pc_mut().Mode = AddrModeFlat;
200 frame.addr_stack_mut().Offset = ctx.Rsp as u64;
201 frame.addr_stack_mut().Mode = AddrModeFlat;
202 frame.addr_frame_mut().Offset = ctx.Rbp as u64;
203 frame.addr_frame_mut().Mode = AddrModeFlat;
204
205 IMAGE_FILE_MACHINE_AMD64
206 }
207
208 #[cfg(target_arch = "x86")]
init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD209 fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
210 frame.addr_pc_mut().Offset = ctx.Eip as u64;
211 frame.addr_pc_mut().Mode = AddrModeFlat;
212 frame.addr_stack_mut().Offset = ctx.Esp as u64;
213 frame.addr_stack_mut().Mode = AddrModeFlat;
214 frame.addr_frame_mut().Offset = ctx.Ebp as u64;
215 frame.addr_frame_mut().Mode = AddrModeFlat;
216
217 IMAGE_FILE_MACHINE_I386
218 }
219
220 #[cfg(target_arch = "aarch64")]
init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD221 fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
222 frame.addr_pc_mut().Offset = ctx.Pc as u64;
223 frame.addr_pc_mut().Mode = AddrModeFlat;
224 frame.addr_stack_mut().Offset = ctx.Sp as u64;
225 frame.addr_stack_mut().Mode = AddrModeFlat;
226 unsafe {
227 frame.addr_frame_mut().Offset = ctx.u.s().Fp as u64;
228 }
229 frame.addr_frame_mut().Mode = AddrModeFlat;
230 IMAGE_FILE_MACHINE_ARM64
231 }
232
233 #[cfg(target_arch = "arm")]
init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD234 fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
235 frame.addr_pc_mut().Offset = ctx.Pc as u64;
236 frame.addr_pc_mut().Mode = AddrModeFlat;
237 frame.addr_stack_mut().Offset = ctx.Sp as u64;
238 frame.addr_stack_mut().Mode = AddrModeFlat;
239 unsafe {
240 frame.addr_frame_mut().Offset = ctx.R11 as u64;
241 }
242 frame.addr_frame_mut().Mode = AddrModeFlat;
243 IMAGE_FILE_MACHINE_ARMNT
244 }
245