Skip to content

Commit a53ae08

Browse files
committed
Revert dbghelp 32-bit to known good version
1 parent b256de4 commit a53ae08

File tree

3 files changed

+266
-1
lines changed

3 files changed

+266
-1
lines changed

src/backtrace/dbghelp32.rs

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
//! Backtrace strategy for MSVC platforms.
2+
//!
3+
//! This module contains the ability to generate a backtrace on MSVC 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+
12+
#![allow(bad_style)]
13+
14+
use super::super::{dbghelp, windows::*};
15+
use core::ffi::c_void;
16+
use core::mem;
17+
18+
#[derive(Clone, Copy)]
19+
pub enum StackFrame {
20+
New(STACKFRAME_EX),
21+
Old(STACKFRAME64),
22+
}
23+
24+
#[derive(Clone, Copy)]
25+
pub struct Frame {
26+
pub(crate) stack_frame: StackFrame,
27+
base_address: *mut c_void,
28+
}
29+
30+
// we're just sending around raw pointers and reading them, never interpreting
31+
// them so this should be safe to both send and share across threads.
32+
unsafe impl Send for Frame {}
33+
unsafe impl Sync for Frame {}
34+
35+
impl Frame {
36+
pub fn ip(&self) -> *mut c_void {
37+
self.addr_pc().Offset as *mut _
38+
}
39+
40+
pub fn sp(&self) -> *mut c_void {
41+
self.addr_stack().Offset as *mut _
42+
}
43+
44+
pub fn symbol_address(&self) -> *mut c_void {
45+
self.ip()
46+
}
47+
48+
pub fn module_base_address(&self) -> Option<*mut c_void> {
49+
Some(self.base_address)
50+
}
51+
52+
fn addr_pc(&self) -> &ADDRESS64 {
53+
match self.stack_frame {
54+
StackFrame::New(ref new) => &new.AddrPC,
55+
StackFrame::Old(ref old) => &old.AddrPC,
56+
}
57+
}
58+
59+
fn addr_pc_mut(&mut self) -> &mut ADDRESS64 {
60+
match self.stack_frame {
61+
StackFrame::New(ref mut new) => &mut new.AddrPC,
62+
StackFrame::Old(ref mut old) => &mut old.AddrPC,
63+
}
64+
}
65+
66+
fn addr_frame_mut(&mut self) -> &mut ADDRESS64 {
67+
match self.stack_frame {
68+
StackFrame::New(ref mut new) => &mut new.AddrFrame,
69+
StackFrame::Old(ref mut old) => &mut old.AddrFrame,
70+
}
71+
}
72+
73+
fn addr_stack(&self) -> &ADDRESS64 {
74+
match self.stack_frame {
75+
StackFrame::New(ref new) => &new.AddrStack,
76+
StackFrame::Old(ref old) => &old.AddrStack,
77+
}
78+
}
79+
80+
fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
81+
match self.stack_frame {
82+
StackFrame::New(ref mut new) => &mut new.AddrStack,
83+
StackFrame::Old(ref mut old) => &mut old.AddrStack,
84+
}
85+
}
86+
}
87+
88+
#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in winapi right now
89+
struct MyContext(CONTEXT);
90+
91+
#[inline(always)]
92+
pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
93+
// Allocate necessary structures for doing the stack walk
94+
let process = GetCurrentProcess();
95+
let thread = GetCurrentThread();
96+
97+
let mut context = mem::zeroed::<MyContext>();
98+
RtlCaptureContext(&mut context.0);
99+
100+
// Ensure this process's symbols are initialized
101+
let dbghelp = match dbghelp::init() {
102+
Ok(dbghelp) => dbghelp,
103+
Err(()) => return, // oh well...
104+
};
105+
106+
// On x86_64 and ARM64 we opt to not use the default `Sym*` functions from
107+
// dbghelp for getting the function table and module base. Instead we use
108+
// the `RtlLookupFunctionEntry` function in kernel32 which will account for
109+
// JIT compiler frames as well. These should be equivalent, but using
110+
// `Rtl*` allows us to backtrace through JIT frames.
111+
//
112+
// Note that `RtlLookupFunctionEntry` only works for in-process backtraces,
113+
// but that's all we support anyway, so it all lines up well.
114+
cfg_if::cfg_if! {
115+
if #[cfg(target_pointer_width = "64")] {
116+
use core::ptr;
117+
118+
unsafe extern "system" fn function_table_access(_process: HANDLE, addr: DWORD64) -> PVOID {
119+
let mut base = 0;
120+
RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut()).cast()
121+
}
122+
123+
unsafe extern "system" fn get_module_base(_process: HANDLE, addr: DWORD64) -> DWORD64 {
124+
let mut base = 0;
125+
RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut());
126+
base
127+
}
128+
} else {
129+
let function_table_access = dbghelp.SymFunctionTableAccess64();
130+
let get_module_base = dbghelp.SymGetModuleBase64();
131+
}
132+
}
133+
134+
let process_handle = GetCurrentProcess();
135+
136+
// Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64`
137+
// since it's in theory supported on more systems.
138+
match (*dbghelp.dbghelp()).StackWalkEx() {
139+
Some(StackWalkEx) => {
140+
let mut inner: STACKFRAME_EX = mem::zeroed();
141+
inner.StackFrameSize = mem::size_of::<STACKFRAME_EX>() as DWORD;
142+
let mut frame = super::Frame {
143+
inner: Frame {
144+
stack_frame: StackFrame::New(inner),
145+
base_address: 0 as _,
146+
},
147+
};
148+
let image = init_frame(&mut frame.inner, &context.0);
149+
let frame_ptr = match &mut frame.inner.stack_frame {
150+
StackFrame::New(ptr) => ptr as *mut STACKFRAME_EX,
151+
_ => unreachable!(),
152+
};
153+
154+
while StackWalkEx(
155+
image as DWORD,
156+
process,
157+
thread,
158+
frame_ptr,
159+
&mut context.0 as *mut CONTEXT as *mut _,
160+
None,
161+
Some(function_table_access),
162+
Some(get_module_base),
163+
None,
164+
0,
165+
) == TRUE
166+
{
167+
frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
168+
169+
if !cb(&frame) {
170+
break;
171+
}
172+
}
173+
}
174+
None => {
175+
let mut frame = super::Frame {
176+
inner: Frame {
177+
stack_frame: StackFrame::Old(mem::zeroed()),
178+
base_address: 0 as _,
179+
},
180+
};
181+
let image = init_frame(&mut frame.inner, &context.0);
182+
let frame_ptr = match &mut frame.inner.stack_frame {
183+
StackFrame::Old(ptr) => ptr as *mut STACKFRAME64,
184+
_ => unreachable!(),
185+
};
186+
187+
while dbghelp.StackWalk64()(
188+
image as DWORD,
189+
process,
190+
thread,
191+
frame_ptr,
192+
&mut context.0 as *mut CONTEXT as *mut _,
193+
None,
194+
Some(function_table_access),
195+
Some(get_module_base),
196+
None,
197+
) == TRUE
198+
{
199+
frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
200+
201+
if !cb(&frame) {
202+
break;
203+
}
204+
}
205+
}
206+
}
207+
}
208+
209+
#[cfg(target_arch = "x86_64")]
210+
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
211+
frame.addr_pc_mut().Offset = ctx.Rip as u64;
212+
frame.addr_pc_mut().Mode = AddrModeFlat;
213+
frame.addr_stack_mut().Offset = ctx.Rsp as u64;
214+
frame.addr_stack_mut().Mode = AddrModeFlat;
215+
frame.addr_frame_mut().Offset = ctx.Rbp as u64;
216+
frame.addr_frame_mut().Mode = AddrModeFlat;
217+
218+
IMAGE_FILE_MACHINE_AMD64
219+
}
220+
221+
#[cfg(target_arch = "x86")]
222+
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
223+
frame.addr_pc_mut().Offset = ctx.Eip as u64;
224+
frame.addr_pc_mut().Mode = AddrModeFlat;
225+
frame.addr_stack_mut().Offset = ctx.Esp as u64;
226+
frame.addr_stack_mut().Mode = AddrModeFlat;
227+
frame.addr_frame_mut().Offset = ctx.Ebp as u64;
228+
frame.addr_frame_mut().Mode = AddrModeFlat;
229+
230+
IMAGE_FILE_MACHINE_I386
231+
}
232+
233+
#[cfg(target_arch = "aarch64")]
234+
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.u.s().Fp as u64;
241+
}
242+
frame.addr_frame_mut().Mode = AddrModeFlat;
243+
IMAGE_FILE_MACHINE_ARM64
244+
}
245+
246+
#[cfg(target_arch = "arm")]
247+
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
248+
frame.addr_pc_mut().Offset = ctx.Pc as u64;
249+
frame.addr_pc_mut().Mode = AddrModeFlat;
250+
frame.addr_stack_mut().Offset = ctx.Sp as u64;
251+
frame.addr_stack_mut().Mode = AddrModeFlat;
252+
unsafe {
253+
frame.addr_frame_mut().Offset = ctx.R11 as u64;
254+
}
255+
frame.addr_frame_mut().Mode = AddrModeFlat;
256+
IMAGE_FILE_MACHINE_ARMNT
257+
}
File renamed without changes.

src/backtrace/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,15 @@ cfg_if::cfg_if! {
193193
use self::libunwind::trace as trace_imp;
194194
pub(crate) use self::libunwind::Frame as FrameImp;
195195
} else if #[cfg(all(windows, not(target_vendor = "uwp")))] {
196-
mod dbghelp;
196+
cfg_if::cfg_if! {
197+
if #[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "arm64ec"))] {
198+
mod dbghelp64;
199+
use dbghelp64 as dbghelp;
200+
} else if #[cfg(any(target_arch = "x86", target_arch = "arm"))] {
201+
mod dbghelp32;
202+
use dbghelp32 as dbghelp;
203+
}
204+
}
197205
use self::dbghelp::trace as trace_imp;
198206
pub(crate) use self::dbghelp::Frame as FrameImp;
199207
} else {

0 commit comments

Comments
 (0)