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
symbol_address(&self) -> *mut c_void45 pub fn symbol_address(&self) -> *mut c_void {
46 self.ip()
47 }
48
addr_pc(&self) -> &ADDRESS6449 fn addr_pc(&self) -> &ADDRESS64 {
50 match self {
51 Frame::New(new) => &new.AddrPC,
52 Frame::Old(old) => &old.AddrPC,
53 }
54 }
55
addr_pc_mut(&mut self) -> &mut ADDRESS6456 fn addr_pc_mut(&mut self) -> &mut ADDRESS64 {
57 match self {
58 Frame::New(new) => &mut new.AddrPC,
59 Frame::Old(old) => &mut old.AddrPC,
60 }
61 }
62
addr_frame_mut(&mut self) -> &mut ADDRESS6463 fn addr_frame_mut(&mut self) -> &mut ADDRESS64 {
64 match self {
65 Frame::New(new) => &mut new.AddrFrame,
66 Frame::Old(old) => &mut old.AddrFrame,
67 }
68 }
69
addr_stack_mut(&mut self) -> &mut ADDRESS6470 fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
71 match self {
72 Frame::New(new) => &mut new.AddrStack,
73 Frame::Old(old) => &mut old.AddrStack,
74 }
75 }
76 }
77
78 #[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in winapi right now
79 struct MyContext(CONTEXT);
80
81 #[inline(always)]
trace(cb: &mut FnMut(&super::Frame) -> bool)82 pub unsafe fn trace(cb: &mut FnMut(&super::Frame) -> bool) {
83 // Allocate necessary structures for doing the stack walk
84 let process = GetCurrentProcess();
85 let thread = GetCurrentThread();
86
87 let mut context = mem::zeroed::<MyContext>();
88 RtlCaptureContext(&mut context.0);
89
90 // Ensure this process's symbols are initialized
91 let dbghelp = match dbghelp::init() {
92 Ok(dbghelp) => dbghelp,
93 Err(()) => return, // oh well...
94 };
95
96 // On x86_64 and ARM64 we opt to not use the default `Sym*` functions from
97 // dbghelp for getting the function table and module base. Instead we use
98 // the `RtlLookupFunctionEntry` function in kernel32 which will account for
99 // JIT compiler frames as well. These should be equivalent, but using
100 // `Rtl*` allows us to backtrace through JIT frames.
101 //
102 // Note that `RtlLookupFunctionEntry` only works for in-process backtraces,
103 // but that's all we support anyway, so it all lines up well.
104 cfg_if::cfg_if! {
105 if #[cfg(target_pointer_width = "64")] {
106 use core::ptr;
107
108 unsafe extern "system" fn function_table_access(_process: HANDLE, addr: DWORD64) -> PVOID {
109 let mut base = 0;
110 RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut()).cast()
111 }
112
113 unsafe extern "system" fn get_module_base(_process: HANDLE, addr: DWORD64) -> DWORD64 {
114 let mut base = 0;
115 RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut());
116 base
117 }
118 } else {
119 let function_table_access = dbghelp.SymFunctionTableAccess64();
120 let get_module_base = dbghelp.SymGetModuleBase64();
121 }
122 }
123
124 // Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64`
125 // since it's in theory supported on more systems.
126 match (*dbghelp.dbghelp()).StackWalkEx() {
127 Some(StackWalkEx) => {
128 let mut frame = super::Frame {
129 inner: Frame::New(mem::zeroed()),
130 };
131 let image = init_frame(&mut frame.inner, &context.0);
132 let frame_ptr = match &mut frame.inner {
133 Frame::New(ptr) => ptr as *mut STACKFRAME_EX,
134 _ => unreachable!(),
135 };
136
137 while StackWalkEx(
138 image as DWORD,
139 process,
140 thread,
141 frame_ptr,
142 &mut context.0 as *mut CONTEXT as *mut _,
143 None,
144 Some(function_table_access),
145 Some(get_module_base),
146 None,
147 0,
148 ) == TRUE
149 {
150 if !cb(&frame) {
151 break;
152 }
153 }
154 }
155 None => {
156 let mut frame = super::Frame {
157 inner: Frame::Old(mem::zeroed()),
158 };
159 let image = init_frame(&mut frame.inner, &context.0);
160 let frame_ptr = match &mut frame.inner {
161 Frame::Old(ptr) => ptr as *mut STACKFRAME64,
162 _ => unreachable!(),
163 };
164
165 while dbghelp.StackWalk64()(
166 image as DWORD,
167 process,
168 thread,
169 frame_ptr,
170 &mut context.0 as *mut CONTEXT as *mut _,
171 None,
172 Some(function_table_access),
173 Some(get_module_base),
174 None,
175 ) == TRUE
176 {
177 if !cb(&frame) {
178 break;
179 }
180 }
181 }
182 }
183 }
184
185 #[cfg(target_arch = "x86_64")]
init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD186 fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
187 frame.addr_pc_mut().Offset = ctx.Rip as u64;
188 frame.addr_pc_mut().Mode = AddrModeFlat;
189 frame.addr_stack_mut().Offset = ctx.Rsp as u64;
190 frame.addr_stack_mut().Mode = AddrModeFlat;
191 frame.addr_frame_mut().Offset = ctx.Rbp as u64;
192 frame.addr_frame_mut().Mode = AddrModeFlat;
193
194 IMAGE_FILE_MACHINE_AMD64
195 }
196
197 #[cfg(target_arch = "x86")]
init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD198 fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
199 frame.addr_pc_mut().Offset = ctx.Eip as u64;
200 frame.addr_pc_mut().Mode = AddrModeFlat;
201 frame.addr_stack_mut().Offset = ctx.Esp as u64;
202 frame.addr_stack_mut().Mode = AddrModeFlat;
203 frame.addr_frame_mut().Offset = ctx.Ebp as u64;
204 frame.addr_frame_mut().Mode = AddrModeFlat;
205
206 IMAGE_FILE_MACHINE_I386
207 }
208
209 #[cfg(target_arch = "aarch64")]
init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD210 fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
211 frame.addr_pc_mut().Offset = ctx.Pc as u64;
212 frame.addr_pc_mut().Mode = AddrModeFlat;
213 frame.addr_stack_mut().Offset = ctx.Sp as u64;
214 frame.addr_stack_mut().Mode = AddrModeFlat;
215 unsafe {
216 frame.addr_frame_mut().Offset = ctx.u.s().Fp as u64;
217 }
218 frame.addr_frame_mut().Mode = AddrModeFlat;
219 IMAGE_FILE_MACHINE_ARM64
220 }
221
222 #[cfg(target_arch = "arm")]
init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD223 fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
224 frame.addr_pc_mut().Offset = ctx.Pc as u64;
225 frame.addr_pc_mut().Mode = AddrModeFlat;
226 frame.addr_stack_mut().Offset = ctx.Sp as u64;
227 frame.addr_stack_mut().Mode = AddrModeFlat;
228 unsafe {
229 frame.addr_frame_mut().Offset = ctx.R11 as u64;
230 }
231 frame.addr_frame_mut().Mode = AddrModeFlat;
232 IMAGE_FILE_MACHINE_ARMNT
233 }
234