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
12use super::super::{dbghelp, windows_sys::*};
13use core::ffi::c_void;
14use core::mem;
15
16#[derive(Clone, Copy)]
17pub enum StackFrame {
18    New(STACKFRAME_EX),
19    Old(STACKFRAME64),
20}
21
22#[derive(Clone, Copy)]
23pub struct Frame {
24    pub(crate) stack_frame: StackFrame,
25    base_address: *mut c_void,
26}
27
28// we're just sending around raw pointers and reading them, never interpreting
29// them so this should be safe to both send and share across threads.
30unsafe impl Send for Frame {}
31unsafe impl Sync for Frame {}
32
33impl Frame {
34    pub fn ip(&self) -> *mut c_void {
35        self.addr_pc().Offset as *mut _
36    }
37
38    pub fn sp(&self) -> *mut c_void {
39        self.addr_stack().Offset as *mut _
40    }
41
42    pub fn symbol_address(&self) -> *mut c_void {
43        self.ip()
44    }
45
46    pub fn module_base_address(&self) -> Option<*mut c_void> {
47        Some(self.base_address)
48    }
49
50    #[cfg(not(target_env = "gnu"))]
51    pub fn inline_context(&self) -> Option<u32> {
52        match self.stack_frame {
53            StackFrame::New(ref new) => Some(new.InlineFrameContext),
54            StackFrame::Old(_) => None,
55        }
56    }
57
58    fn addr_pc(&self) -> &ADDRESS64 {
59        match self.stack_frame {
60            StackFrame::New(ref new) => &new.AddrPC,
61            StackFrame::Old(ref old) => &old.AddrPC,
62        }
63    }
64
65    fn addr_pc_mut(&mut self) -> &mut ADDRESS64 {
66        match self.stack_frame {
67            StackFrame::New(ref mut new) => &mut new.AddrPC,
68            StackFrame::Old(ref mut old) => &mut old.AddrPC,
69        }
70    }
71
72    fn addr_frame_mut(&mut self) -> &mut ADDRESS64 {
73        match self.stack_frame {
74            StackFrame::New(ref mut new) => &mut new.AddrFrame,
75            StackFrame::Old(ref mut old) => &mut old.AddrFrame,
76        }
77    }
78
79    fn addr_stack(&self) -> &ADDRESS64 {
80        match self.stack_frame {
81            StackFrame::New(ref new) => &new.AddrStack,
82            StackFrame::Old(ref old) => &old.AddrStack,
83        }
84    }
85
86    fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
87        match self.stack_frame {
88            StackFrame::New(ref mut new) => &mut new.AddrStack,
89            StackFrame::Old(ref mut old) => &mut old.AddrStack,
90        }
91    }
92}
93
94#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in windows metadata right now
95struct MyContext(CONTEXT);
96
97#[inline(always)]
98pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
99    // Allocate necessary structures for doing the stack walk
100    let process = GetCurrentProcess();
101    let thread = GetCurrentThread();
102
103    let mut context = mem::zeroed::<MyContext>();
104    RtlCaptureContext(&mut context.0);
105
106    // Ensure this process's symbols are initialized
107    let dbghelp = match dbghelp::init() {
108        Ok(dbghelp) => dbghelp,
109        Err(()) => return, // oh well...
110    };
111
112    let function_table_access = dbghelp.SymFunctionTableAccess64();
113    let get_module_base = dbghelp.SymGetModuleBase64();
114
115    let process_handle = GetCurrentProcess();
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 (*dbghelp.dbghelp()).StackWalkEx() {
120        #[allow(non_snake_case)]
121        Some(StackWalkEx) => {
122            let mut inner: STACKFRAME_EX = mem::zeroed();
123            inner.StackFrameSize = mem::size_of::<STACKFRAME_EX>() as u32;
124            let mut frame = super::Frame {
125                inner: Frame {
126                    stack_frame: StackFrame::New(inner),
127                    base_address: 0 as _,
128                },
129            };
130            let image = init_frame(&mut frame.inner, &context.0);
131            let frame_ptr = match &mut frame.inner.stack_frame {
132                StackFrame::New(ptr) => ptr as *mut STACKFRAME_EX,
133                _ => unreachable!(),
134            };
135
136            while StackWalkEx(
137                image as u32,
138                process,
139                thread,
140                frame_ptr,
141                &mut context.0 as *mut CONTEXT as *mut _,
142                None,
143                Some(function_table_access),
144                Some(get_module_base),
145                None,
146                0,
147            ) == TRUE
148            {
149                frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
150
151                if !cb(&frame) {
152                    break;
153                }
154            }
155        }
156        None => {
157            let mut frame = super::Frame {
158                inner: Frame {
159                    stack_frame: StackFrame::Old(mem::zeroed()),
160                    base_address: 0 as _,
161                },
162            };
163            let image = init_frame(&mut frame.inner, &context.0);
164            let frame_ptr = match &mut frame.inner.stack_frame {
165                StackFrame::Old(ptr) => ptr as *mut STACKFRAME64,
166                _ => unreachable!(),
167            };
168
169            while dbghelp.StackWalk64()(
170                image as u32,
171                process,
172                thread,
173                frame_ptr,
174                &mut context.0 as *mut CONTEXT as *mut _,
175                None,
176                Some(function_table_access),
177                Some(get_module_base),
178                None,
179            ) == TRUE
180            {
181                frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
182
183                if !cb(&frame) {
184                    break;
185                }
186            }
187        }
188    }
189}
190
191#[cfg(target_arch = "x86")]
192fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> u16 {
193    frame.addr_pc_mut().Offset = ctx.Eip as u64;
194    frame.addr_pc_mut().Mode = AddrModeFlat;
195    frame.addr_stack_mut().Offset = ctx.Esp as u64;
196    frame.addr_stack_mut().Mode = AddrModeFlat;
197    frame.addr_frame_mut().Offset = ctx.Ebp as u64;
198    frame.addr_frame_mut().Mode = AddrModeFlat;
199
200    IMAGE_FILE_MACHINE_I386
201}
202
203#[cfg(target_arch = "arm")]
204fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> u16 {
205    frame.addr_pc_mut().Offset = ctx.Pc as u64;
206    frame.addr_pc_mut().Mode = AddrModeFlat;
207    frame.addr_stack_mut().Offset = ctx.Sp as u64;
208    frame.addr_stack_mut().Mode = AddrModeFlat;
209    unsafe {
210        frame.addr_frame_mut().Offset = ctx.R11 as u64;
211    }
212    frame.addr_frame_mut().Mode = AddrModeFlat;
213    IMAGE_FILE_MACHINE_ARMNT
214}