Skip to content

Commit d39c66b

Browse files
committed
Add a fallback for stacktrace printing for older Windows versions.
PR #47252 switched stack inspection functions of dbghelp.dll to their newer alternatives that also capture inlined context. Unfortunately, said new alternatives are not present in older dbghelp.dll versions. In particular Windows 7 at the time of writing has dbghelp.dll version 6.1.7601 from 2010, that lacks StackWalkEx and friends. Fixes #50138
1 parent e3bf634 commit d39c66b

File tree

3 files changed

+331
-108
lines changed

3 files changed

+331
-108
lines changed

Diff for: src/libstd/sys/windows/backtrace/mod.rs

+128-42
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,21 @@ pub mod gnu;
4848

4949
pub use self::printing::{resolve_symname, foreach_symbol_fileline};
5050

51-
pub fn unwind_backtrace(frames: &mut [Frame])
52-
-> io::Result<(usize, BacktraceContext)>
53-
{
51+
pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> {
5452
let dbghelp = DynamicLibrary::open("dbghelp.dll")?;
5553

5654
// Fetch the symbols necessary from dbghelp.dll
5755
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
5856
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;
59-
let StackWalkEx = sym!(dbghelp, "StackWalkEx", StackWalkExFn)?;
57+
// StackWalkEx might not be present and we'll fall back to StackWalk64
58+
let ResStackWalkEx = sym!(dbghelp, "StackWalkEx", StackWalkExFn);
59+
let ResStackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn);
6060

6161
// Allocate necessary structures for doing the stack walk
6262
let process = unsafe { c::GetCurrentProcess() };
6363
let thread = unsafe { c::GetCurrentThread() };
6464
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
6565
unsafe { c::RtlCaptureContext(&mut context) };
66-
let mut frame: c::STACKFRAME_EX = unsafe { mem::zeroed() };
67-
frame.StackFrameSize = mem::size_of_val(&frame) as c::DWORD;
68-
let image = init_frame(&mut frame, &context);
6966

7067
let backtrace_context = BacktraceContext {
7168
handle: process,
@@ -76,49 +73,139 @@ pub fn unwind_backtrace(frames: &mut [Frame])
7673
// Initialize this process's symbols
7774
let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
7875
if ret != c::TRUE {
79-
return Ok((0, backtrace_context))
76+
return Ok((0, backtrace_context));
8077
}
8178

8279
// And now that we're done with all the setup, do the stack walking!
83-
let mut i = 0;
84-
unsafe {
85-
while i < frames.len() &&
86-
StackWalkEx(image, process, thread, &mut frame, &mut context,
87-
ptr::null_mut(),
88-
ptr::null_mut(),
89-
ptr::null_mut(),
90-
ptr::null_mut(),
91-
0) == c::TRUE
92-
{
93-
let addr = (frame.AddrPC.Offset - 1) as *const u8;
94-
95-
frames[i] = Frame {
96-
symbol_addr: addr,
97-
exact_position: addr,
98-
inline_context: frame.InlineFrameContext,
99-
};
100-
i += 1;
80+
match (ResStackWalkEx, ResStackWalk64) {
81+
(Ok(StackWalkEx), _) => {
82+
let mut frame: c::STACKFRAME_EX = unsafe { mem::zeroed() };
83+
frame.StackFrameSize = mem::size_of_val(&frame) as c::DWORD;
84+
let image = init_frame_ex(&mut frame, &context);
85+
86+
let mut i = 0;
87+
unsafe {
88+
while i < frames.len()
89+
&& StackWalkEx(
90+
image,
91+
process,
92+
thread,
93+
&mut frame,
94+
&mut context,
95+
ptr::null_mut(),
96+
ptr::null_mut(),
97+
ptr::null_mut(),
98+
ptr::null_mut(),
99+
0,
100+
) == c::TRUE
101+
{
102+
let addr = (frame.AddrPC.Offset - 1) as *const u8;
103+
104+
frames[i] = Frame {
105+
symbol_addr: addr,
106+
exact_position: addr,
107+
inline_context: frame.InlineFrameContext,
108+
};
109+
i += 1;
110+
}
111+
}
112+
113+
Ok((i, backtrace_context))
101114
}
115+
(_, Ok(StackWalk64)) => {
116+
let mut frame: c::STACKFRAME64 = unsafe { mem::zeroed() };
117+
let image = init_frame_64(&mut frame, &context);
118+
119+
// Start from -1 to avoid printing this stack frame, which will
120+
// always be exactly the same.
121+
let mut i = 0;
122+
unsafe {
123+
while i < frames.len()
124+
&& StackWalk64(
125+
image,
126+
process,
127+
thread,
128+
&mut frame,
129+
&mut context,
130+
ptr::null_mut(),
131+
ptr::null_mut(),
132+
ptr::null_mut(),
133+
ptr::null_mut(),
134+
) == c::TRUE
135+
{
136+
let addr = frame.AddrPC.Offset;
137+
if addr == frame.AddrReturn.Offset || addr == 0 || frame.AddrReturn.Offset == 0
138+
{
139+
break;
140+
}
141+
142+
frames[i] = Frame {
143+
symbol_addr: (addr - 1) as *const u8,
144+
exact_position: (addr - 1) as *const u8,
145+
inline_context: 0,
146+
};
147+
i += 1;
148+
}
149+
}
150+
151+
Ok((i, backtrace_context))
152+
}
153+
(Err(e), _) => Err(e),
102154
}
103-
104-
Ok((i, backtrace_context))
105155
}
106156

107-
type SymInitializeFn =
108-
unsafe extern "system" fn(c::HANDLE, *mut c_void,
109-
c::BOOL) -> c::BOOL;
110-
type SymCleanupFn =
111-
unsafe extern "system" fn(c::HANDLE) -> c::BOOL;
157+
type SymInitializeFn = unsafe extern "system" fn(c::HANDLE, *mut c_void, c::BOOL) -> c::BOOL;
158+
type SymCleanupFn = unsafe extern "system" fn(c::HANDLE) -> c::BOOL;
159+
160+
type StackWalkExFn = unsafe extern "system" fn(
161+
c::DWORD,
162+
c::HANDLE,
163+
c::HANDLE,
164+
*mut c::STACKFRAME_EX,
165+
*mut c::CONTEXT,
166+
*mut c_void,
167+
*mut c_void,
168+
*mut c_void,
169+
*mut c_void,
170+
c::DWORD,
171+
) -> c::BOOL;
172+
173+
type StackWalk64Fn = unsafe extern "system" fn(
174+
c::DWORD,
175+
c::HANDLE,
176+
c::HANDLE,
177+
*mut c::STACKFRAME64,
178+
*mut c::CONTEXT,
179+
*mut c_void,
180+
*mut c_void,
181+
*mut c_void,
182+
*mut c_void,
183+
) -> c::BOOL;
112184

113-
type StackWalkExFn =
114-
unsafe extern "system" fn(c::DWORD, c::HANDLE, c::HANDLE,
115-
*mut c::STACKFRAME_EX, *mut c::CONTEXT,
116-
*mut c_void, *mut c_void,
117-
*mut c_void, *mut c_void, c::DWORD) -> c::BOOL;
185+
#[cfg(target_arch = "x86")]
186+
fn init_frame_ex(frame: &mut c::STACKFRAME_EX, ctx: &c::CONTEXT) -> c::DWORD {
187+
frame.AddrPC.Offset = ctx.Eip as u64;
188+
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
189+
frame.AddrStack.Offset = ctx.Esp as u64;
190+
frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
191+
frame.AddrFrame.Offset = ctx.Ebp as u64;
192+
frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
193+
c::IMAGE_FILE_MACHINE_I386
194+
}
195+
196+
#[cfg(target_arch = "x86_64")]
197+
fn init_frame_ex(frame: &mut c::STACKFRAME_EX, ctx: &c::CONTEXT) -> c::DWORD {
198+
frame.AddrPC.Offset = ctx.Rip as u64;
199+
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
200+
frame.AddrStack.Offset = ctx.Rsp as u64;
201+
frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
202+
frame.AddrFrame.Offset = ctx.Rbp as u64;
203+
frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
204+
c::IMAGE_FILE_MACHINE_AMD64
205+
}
118206

119207
#[cfg(target_arch = "x86")]
120-
fn init_frame(frame: &mut c::STACKFRAME_EX,
121-
ctx: &c::CONTEXT) -> c::DWORD {
208+
fn init_frame_64(frame: &mut c::STACKFRAME64, ctx: &c::CONTEXT) -> c::DWORD {
122209
frame.AddrPC.Offset = ctx.Eip as u64;
123210
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
124211
frame.AddrStack.Offset = ctx.Esp as u64;
@@ -129,8 +216,7 @@ fn init_frame(frame: &mut c::STACKFRAME_EX,
129216
}
130217

131218
#[cfg(target_arch = "x86_64")]
132-
fn init_frame(frame: &mut c::STACKFRAME_EX,
133-
ctx: &c::CONTEXT) -> c::DWORD {
219+
fn init_frame_64(frame: &mut c::STACKFRAME64, ctx: &c::CONTEXT) -> c::DWORD {
134220
frame.AddrPC.Offset = ctx.Rip as u64;
135221
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
136222
frame.AddrStack.Offset = ctx.Rsp as u64;

0 commit comments

Comments
 (0)