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