Skip to content

Commit 6e5e63a

Browse files
committed
Auto merge of #50526 - moxian:just-fix, r=alexcrichton
Add a fallback for stacktrace printing for older Windows versions. Some time ago we 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. Tested on my Windows 7 - both msvc and gnu versions produce a readable stacktrace. Fixes #50138
2 parents 2eb6969 + be7f619 commit 6e5e63a

File tree

4 files changed

+398
-101
lines changed

4 files changed

+398
-101
lines changed

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

+210-63
Original file line numberDiff line numberDiff line change
@@ -46,110 +46,257 @@ mod printing;
4646
#[path = "backtrace_gnu.rs"]
4747
pub mod gnu;
4848

49-
pub use self::printing::{resolve_symname, foreach_symbol_fileline};
49+
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
50+
use self::printing::{load_printing_fns_64, load_printing_fns_ex};
5051

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

5655
// Fetch the symbols necessary from dbghelp.dll
5756
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
5857
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;
59-
let StackWalkEx = sym!(dbghelp, "StackWalkEx", StackWalkExFn)?;
58+
59+
// StackWalkEx might not be present and we'll fall back to StackWalk64
60+
let sw_var = match sym!(dbghelp, "StackWalkEx", StackWalkExFn) {
61+
Ok(StackWalkEx) => {
62+
StackWalkVariant::StackWalkEx(StackWalkEx, load_printing_fns_ex(&dbghelp)?)
63+
}
64+
Err(e) => match sym!(dbghelp, "StackWalk64", StackWalk64Fn) {
65+
Ok(StackWalk64) => {
66+
StackWalkVariant::StackWalk64(StackWalk64, load_printing_fns_64(&dbghelp)?)
67+
}
68+
Err(..) => return Err(e),
69+
},
70+
};
6071

6172
// Allocate necessary structures for doing the stack walk
6273
let process = unsafe { c::GetCurrentProcess() };
63-
let thread = unsafe { c::GetCurrentThread() };
64-
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
65-
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);
6974

7075
let backtrace_context = BacktraceContext {
7176
handle: process,
7277
SymCleanup,
78+
StackWalkVariant: sw_var,
7379
dbghelp,
7480
};
7581

7682
// Initialize this process's symbols
7783
let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
7884
if ret != c::TRUE {
79-
return Ok((0, backtrace_context))
85+
return Ok((0, backtrace_context));
8086
}
8187

8288
// And now that we're done with all the setup, do the stack walking!
89+
match backtrace_context.StackWalkVariant {
90+
StackWalkVariant::StackWalkEx(StackWalkEx, _) => {
91+
set_frames(StackWalkEx, frames).map(|i| (i, backtrace_context))
92+
}
93+
94+
StackWalkVariant::StackWalk64(StackWalk64, _) => {
95+
set_frames(StackWalk64, frames).map(|i| (i, backtrace_context))
96+
}
97+
}
98+
}
99+
100+
fn set_frames<W: StackWalker>(StackWalk: W, frames: &mut [Frame]) -> io::Result<usize> {
101+
let process = unsafe { c::GetCurrentProcess() };
102+
let thread = unsafe { c::GetCurrentProcess() };
103+
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
104+
unsafe { c::RtlCaptureContext(&mut context) };
105+
let mut frame = W::Item::new();
106+
let image = frame.init(&context);
107+
83108
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;
109+
while i < frames.len()
110+
&& StackWalk.walk(image, process, thread, &mut frame, &mut context) == c::TRUE
111+
{
112+
let addr = frame.get_addr();
113+
frames[i] = Frame {
114+
symbol_addr: addr,
115+
exact_position: addr,
116+
inline_context: 0,
117+
};
118+
119+
i += 1
120+
}
121+
Ok(i)
122+
}
123+
124+
type SymInitializeFn = unsafe extern "system" fn(c::HANDLE, *mut c_void, c::BOOL) -> c::BOOL;
125+
type SymCleanupFn = unsafe extern "system" fn(c::HANDLE) -> c::BOOL;
126+
127+
type StackWalkExFn = unsafe extern "system" fn(
128+
c::DWORD,
129+
c::HANDLE,
130+
c::HANDLE,
131+
*mut c::STACKFRAME_EX,
132+
*mut c::CONTEXT,
133+
*mut c_void,
134+
*mut c_void,
135+
*mut c_void,
136+
*mut c_void,
137+
c::DWORD,
138+
) -> c::BOOL;
139+
140+
type StackWalk64Fn = unsafe extern "system" fn(
141+
c::DWORD,
142+
c::HANDLE,
143+
c::HANDLE,
144+
*mut c::STACKFRAME64,
145+
*mut c::CONTEXT,
146+
*mut c_void,
147+
*mut c_void,
148+
*mut c_void,
149+
*mut c_void,
150+
) -> c::BOOL;
151+
152+
trait StackWalker {
153+
type Item: StackFrame;
154+
155+
fn walk(&self, c::DWORD, c::HANDLE, c::HANDLE, &mut Self::Item, &mut c::CONTEXT) -> c::BOOL;
156+
}
157+
158+
impl StackWalker for StackWalkExFn {
159+
type Item = c::STACKFRAME_EX;
160+
fn walk(
161+
&self,
162+
image: c::DWORD,
163+
process: c::HANDLE,
164+
thread: c::HANDLE,
165+
frame: &mut Self::Item,
166+
context: &mut c::CONTEXT,
167+
) -> c::BOOL {
168+
unsafe {
169+
self(
170+
image,
171+
process,
172+
thread,
173+
frame,
174+
context,
175+
ptr::null_mut(),
176+
ptr::null_mut(),
177+
ptr::null_mut(),
178+
ptr::null_mut(),
179+
0,
180+
)
101181
}
102182
}
183+
}
103184

104-
Ok((i, backtrace_context))
185+
impl StackWalker for StackWalk64Fn {
186+
type Item = c::STACKFRAME64;
187+
fn walk(
188+
&self,
189+
image: c::DWORD,
190+
process: c::HANDLE,
191+
thread: c::HANDLE,
192+
frame: &mut Self::Item,
193+
context: &mut c::CONTEXT,
194+
) -> c::BOOL {
195+
unsafe {
196+
self(
197+
image,
198+
process,
199+
thread,
200+
frame,
201+
context,
202+
ptr::null_mut(),
203+
ptr::null_mut(),
204+
ptr::null_mut(),
205+
ptr::null_mut(),
206+
)
207+
}
208+
}
105209
}
106210

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;
112-
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;
118-
119-
#[cfg(target_arch = "x86")]
120-
fn init_frame(frame: &mut c::STACKFRAME_EX,
121-
ctx: &c::CONTEXT) -> c::DWORD {
122-
frame.AddrPC.Offset = ctx.Eip as u64;
123-
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
124-
frame.AddrStack.Offset = ctx.Esp as u64;
125-
frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
126-
frame.AddrFrame.Offset = ctx.Ebp as u64;
127-
frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
128-
c::IMAGE_FILE_MACHINE_I386
211+
trait StackFrame {
212+
fn new() -> Self;
213+
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD;
214+
fn get_addr(&self) -> *const u8;
129215
}
130216

131-
#[cfg(target_arch = "x86_64")]
132-
fn init_frame(frame: &mut c::STACKFRAME_EX,
133-
ctx: &c::CONTEXT) -> c::DWORD {
134-
frame.AddrPC.Offset = ctx.Rip as u64;
135-
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
136-
frame.AddrStack.Offset = ctx.Rsp as u64;
137-
frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
138-
frame.AddrFrame.Offset = ctx.Rbp as u64;
139-
frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
140-
c::IMAGE_FILE_MACHINE_AMD64
217+
impl StackFrame for c::STACKFRAME_EX {
218+
fn new() -> c::STACKFRAME_EX {
219+
unsafe { mem::zeroed() }
220+
}
221+
222+
#[cfg(target_arch = "x86")]
223+
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
224+
self.AddrPC.Offset = ctx.Eip as u64;
225+
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
226+
self.AddrStack.Offset = ctx.Esp as u64;
227+
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
228+
self.AddrFrame.Offset = ctx.Ebp as u64;
229+
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
230+
c::IMAGE_FILE_MACHINE_I386
231+
}
232+
#[cfg(target_arch = "x86_64")]
233+
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
234+
self.AddrPC.Offset = ctx.Rip as u64;
235+
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
236+
self.AddrStack.Offset = ctx.Rsp as u64;
237+
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
238+
self.AddrFrame.Offset = ctx.Rbp as u64;
239+
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
240+
c::IMAGE_FILE_MACHINE_AMD64
241+
}
242+
243+
fn get_addr(&self) -> *const u8 {
244+
(self.AddrPC.Offset - 1) as *const u8
245+
}
246+
}
247+
248+
impl StackFrame for c::STACKFRAME64 {
249+
fn new() -> c::STACKFRAME64 {
250+
unsafe { mem::zeroed() }
251+
}
252+
253+
#[cfg(target_arch = "x86")]
254+
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
255+
self.AddrPC.Offset = ctx.Eip as u64;
256+
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
257+
self.AddrStack.Offset = ctx.Esp as u64;
258+
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
259+
self.AddrFrame.Offset = ctx.Ebp as u64;
260+
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
261+
c::IMAGE_FILE_MACHINE_I386
262+
}
263+
#[cfg(target_arch = "x86_64")]
264+
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
265+
self.AddrPC.Offset = ctx.Rip as u64;
266+
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
267+
self.AddrStack.Offset = ctx.Rsp as u64;
268+
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
269+
self.AddrFrame.Offset = ctx.Rbp as u64;
270+
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
271+
c::IMAGE_FILE_MACHINE_AMD64
272+
}
273+
274+
fn get_addr(&self) -> *const u8 {
275+
(self.AddrPC.Offset - 1) as *const u8
276+
}
277+
}
278+
279+
enum StackWalkVariant {
280+
StackWalkEx(StackWalkExFn, printing::PrintingFnsEx),
281+
StackWalk64(StackWalk64Fn, printing::PrintingFns64),
141282
}
142283

143284
pub struct BacktraceContext {
144285
handle: c::HANDLE,
145286
SymCleanup: SymCleanupFn,
146287
// Only used in printing for msvc and not gnu
288+
// The gnu version is effectively a ZST dummy.
289+
#[allow(dead_code)]
290+
StackWalkVariant: StackWalkVariant,
291+
// keeping DynamycLibrary loaded until its functions no longer needed
147292
#[allow(dead_code)]
148293
dbghelp: DynamicLibrary,
149294
}
150295

151296
impl Drop for BacktraceContext {
152297
fn drop(&mut self) {
153-
unsafe { (self.SymCleanup)(self.handle); }
298+
unsafe {
299+
(self.SymCleanup)(self.handle);
300+
}
154301
}
155302
}

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

+14
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ mod printing;
1515
#[cfg(target_env = "gnu")]
1616
mod printing {
1717
pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
18+
19+
// dummy functions to mirror those present in msvc version.
20+
use sys::dynamic_lib::DynamicLibrary;
21+
use io;
22+
pub struct PrintingFnsEx {}
23+
pub struct PrintingFns64 {}
24+
pub fn load_printing_fns_ex(_: &DynamicLibrary) -> io::Result<PrintingFnsEx> {
25+
Ok(PrintingFnsEx{})
26+
}
27+
pub fn load_printing_fns_64(_: &DynamicLibrary) -> io::Result<PrintingFns64> {
28+
Ok(PrintingFns64{})
29+
}
1830
}
1931

2032
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
33+
pub use self::printing::{load_printing_fns_ex, load_printing_fns_64,
34+
PrintingFnsEx, PrintingFns64};

0 commit comments

Comments
 (0)