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 //
main()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 {
41     pub fn ip(&self) -> *mut c_void {
42         self.addr_pc().Offset as *mut _
43     }
44 
45     pub fn symbol_address(&self) -> *mut c_void {
46         self.ip()
47     }
48 
49     fn addr_pc(&self) -> &ADDRESS64 {
50         match self {
51             Frame::New(new) => &new.AddrPC,
52             Frame::Old(old) => &old.AddrPC,
53         }
54     }
55 
56     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 
63     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 
70     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)]
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")]
186 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")]
198 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")]
210 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")]
223 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