Skip to main content

std\backtrace\src\backtrace/
win32.rs

1//! Backtrace strategy for Windows platforms.
2//!
3//! This module contains the ability to generate a backtrace on Windows using one
4//! of two possible methods. The `StackWalkEx` function is primarily used if
5//! possible, but not all systems have that. Failing that the `StackWalk64`
6//! function is used instead. Note that `StackWalkEx` is favored because it
7//! handles debuginfo internally and returns inline frame information.
8//!
9//! Note that all dbghelp support is loaded dynamically, see `src/dbghelp.rs`
10//! for more information about that.
11#![deny(unsafe_op_in_unsafe_fn)]
12
13use super::super::{dbghelp, windows_sys::*};
14use core::ffi::c_void;
15use core::mem;
16
17#[derive(Clone, Copy)]
18pub enum StackFrame {
19    New(STACKFRAME_EX),
20    Old(STACKFRAME64),
21}
22
23#[derive(Clone, Copy)]
24pub struct Frame {
25    pub(crate) stack_frame: StackFrame,
26    base_address: *mut c_void,
27}
28
29// we're just sending around raw pointers and reading them, never interpreting
30// them so this should be safe to both send and share across threads.
31unsafe impl Send for Frame {}
32unsafe impl Sync for Frame {}
33
34impl Frame {
35    pub fn ip(&self) -> *mut c_void {
36        self.addr_pc().Offset as *mut _
37    }
38
39    pub fn sp(&self) -> *mut c_void {
40        self.addr_stack().Offset as *mut _
41    }
42
43    pub fn symbol_address(&self) -> *mut c_void {
44        self.ip()
45    }
46
47    pub fn module_base_address(&self) -> Option<*mut c_void> {
48        Some(self.base_address)
49    }
50
51    #[cfg(not(target_env = "gnu"))]
52    pub fn inline_context(&self) -> Option<u32> {
53        match self.stack_frame {
54            StackFrame::New(ref new) => Some(new.InlineFrameContext),
55            StackFrame::Old(_) => None,
56        }
57    }
58
59    fn addr_pc(&self) -> &ADDRESS64 {
60        match self.stack_frame {
61            StackFrame::New(ref new) => &new.AddrPC,
62            StackFrame::Old(ref old) => &old.AddrPC,
63        }
64    }
65
66    fn addr_pc_mut(&mut self) -> &mut ADDRESS64 {
67        match self.stack_frame {
68            StackFrame::New(ref mut new) => &mut new.AddrPC,
69            StackFrame::Old(ref mut old) => &mut old.AddrPC,
70        }
71    }
72
73    fn addr_frame_mut(&mut self) -> &mut ADDRESS64 {
74        match self.stack_frame {
75            StackFrame::New(ref mut new) => &mut new.AddrFrame,
76            StackFrame::Old(ref mut old) => &mut old.AddrFrame,
77        }
78    }
79
80    fn addr_stack(&self) -> &ADDRESS64 {
81        match self.stack_frame {
82            StackFrame::New(ref new) => &new.AddrStack,
83            StackFrame::Old(ref old) => &old.AddrStack,
84        }
85    }
86
87    fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
88        match self.stack_frame {
89            StackFrame::New(ref mut new) => &mut new.AddrStack,
90            StackFrame::Old(ref mut old) => &mut old.AddrStack,
91        }
92    }
93}
94
95#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in windows metadata right now
96struct MyContext(CONTEXT);
97
98#[inline(always)]
99pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
100    // Allocate necessary structures for doing the stack walk
101    let process = unsafe { GetCurrentProcess() };
102    let thread = unsafe { GetCurrentThread() };
103
104    // This is a classic C-style out-ptr struct. Zero it to start.
105    let mut context = unsafe { mem::zeroed::<MyContext>() };
106    unsafe { RtlCaptureContext(&mut context.0) };
107
108    // Ensure this process's symbols are initialized
109    let dbghelp = match dbghelp::init() {
110        Ok(dbghelp) => dbghelp,
111        Err(()) => return, // oh well...
112    };
113
114    let function_table_access = dbghelp.SymFunctionTableAccess64();
115    let get_module_base = dbghelp.SymGetModuleBase64();
116
117    // Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64`
118    // since it's in theory supported on more systems.
119    match unsafe { (*dbghelp.dbghelp()).StackWalkEx() } {
120        #[allow(non_snake_case)]
121        Some(StackWalkEx) => {
122            // This is a classic C-style out-ptr struct. Zero it to start.
123            let mut inner: STACKFRAME_EX = unsafe { mem::zeroed() };
124            inner.StackFrameSize = mem::size_of::<STACKFRAME_EX>() as u32;
125            let mut frame = super::Frame {
126                inner: Frame {
127                    stack_frame: StackFrame::New(inner),
128                    base_address: 0 as _,
129                },
130            };
131            let image = init_frame(&mut frame.inner, &context.0);
132            let frame_ptr = match &mut frame.inner.stack_frame {
133                StackFrame::New(ptr) => ptr as *mut STACKFRAME_EX,
134                _ => unreachable!(),
135            };
136
137            while unsafe {
138                StackWalkEx(
139                    image as u32,
140                    process,
141                    thread,
142                    frame_ptr,
143                    &mut context.0 as *mut CONTEXT as *mut _,
144                    None,
145                    Some(function_table_access),
146                    Some(get_module_base),
147                    None,
148                    0,
149                ) == TRUE
150            } {
151                frame.inner.base_address =
152                    unsafe { get_module_base(process, frame.ip() as _) as _ };
153
154                if !cb(&frame) {
155                    break;
156                }
157            }
158        }
159        None => {
160            let mut frame = super::Frame {
161                inner: Frame {
162                    // This is a classic C-style out-ptr struct. Zero it to start.
163                    stack_frame: StackFrame::Old(unsafe { mem::zeroed() }),
164                    base_address: 0 as _,
165                },
166            };
167            let image = init_frame(&mut frame.inner, &context.0);
168            let frame_ptr = match &mut frame.inner.stack_frame {
169                StackFrame::Old(ptr) => ptr as *mut STACKFRAME64,
170                _ => unreachable!(),
171            };
172
173            while unsafe {
174                dbghelp.StackWalk64()(
175                    image as u32,
176                    process,
177                    thread,
178                    frame_ptr,
179                    &mut context.0 as *mut CONTEXT as *mut _,
180                    None,
181                    Some(function_table_access),
182                    Some(get_module_base),
183                    None,
184                ) == TRUE
185            } {
186                frame.inner.base_address =
187                    unsafe { get_module_base(process, frame.ip() as _) as _ };
188
189                if !cb(&frame) {
190                    break;
191                }
192            }
193        }
194    }
195}
196
197#[cfg(target_arch = "x86")]
198fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> u16 {
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 = "arm")]
210fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> u16 {
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.R11 as u64;
217    }
218    frame.addr_frame_mut().Mode = AddrModeFlat;
219    IMAGE_FILE_MACHINE_ARMNT
220}