Skip to content

Commit

Permalink
Auto merge of rust-lang#50526 - moxian:just-fix, r=alexcrichton
Browse files Browse the repository at this point in the history
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 rust-lang#50138
  • Loading branch information
bors committed Jun 29, 2018
2 parents 2eb6969 + be7f619 commit 6e5e63a
Show file tree
Hide file tree
Showing 4 changed files with 398 additions and 101 deletions.
273 changes: 210 additions & 63 deletions src/libstd/sys/windows/backtrace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,110 +46,257 @@ mod printing;
#[path = "backtrace_gnu.rs"]
pub mod gnu;

pub use self::printing::{resolve_symname, foreach_symbol_fileline};
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
use self::printing::{load_printing_fns_64, load_printing_fns_ex};

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

// Fetch the symbols necessary from dbghelp.dll
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;
let StackWalkEx = sym!(dbghelp, "StackWalkEx", StackWalkExFn)?;

// StackWalkEx might not be present and we'll fall back to StackWalk64
let sw_var = match sym!(dbghelp, "StackWalkEx", StackWalkExFn) {
Ok(StackWalkEx) => {
StackWalkVariant::StackWalkEx(StackWalkEx, load_printing_fns_ex(&dbghelp)?)
}
Err(e) => match sym!(dbghelp, "StackWalk64", StackWalk64Fn) {
Ok(StackWalk64) => {
StackWalkVariant::StackWalk64(StackWalk64, load_printing_fns_64(&dbghelp)?)
}
Err(..) => return Err(e),
},
};

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

let backtrace_context = BacktraceContext {
handle: process,
SymCleanup,
StackWalkVariant: sw_var,
dbghelp,
};

// Initialize this process's symbols
let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
if ret != c::TRUE {
return Ok((0, backtrace_context))
return Ok((0, backtrace_context));
}

// And now that we're done with all the setup, do the stack walking!
match backtrace_context.StackWalkVariant {
StackWalkVariant::StackWalkEx(StackWalkEx, _) => {
set_frames(StackWalkEx, frames).map(|i| (i, backtrace_context))
}

StackWalkVariant::StackWalk64(StackWalk64, _) => {
set_frames(StackWalk64, frames).map(|i| (i, backtrace_context))
}
}
}

fn set_frames<W: StackWalker>(StackWalk: W, frames: &mut [Frame]) -> io::Result<usize> {
let process = unsafe { c::GetCurrentProcess() };
let thread = unsafe { c::GetCurrentProcess() };
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
unsafe { c::RtlCaptureContext(&mut context) };
let mut frame = W::Item::new();
let image = frame.init(&context);

let mut i = 0;
unsafe {
while i < frames.len() &&
StackWalkEx(image, process, thread, &mut frame, &mut context,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0) == c::TRUE
{
let addr = (frame.AddrPC.Offset - 1) as *const u8;

frames[i] = Frame {
symbol_addr: addr,
exact_position: addr,
inline_context: frame.InlineFrameContext,
};
i += 1;
while i < frames.len()
&& StackWalk.walk(image, process, thread, &mut frame, &mut context) == c::TRUE
{
let addr = frame.get_addr();
frames[i] = Frame {
symbol_addr: addr,
exact_position: addr,
inline_context: 0,
};

i += 1
}
Ok(i)
}

type SymInitializeFn = unsafe extern "system" fn(c::HANDLE, *mut c_void, c::BOOL) -> c::BOOL;
type SymCleanupFn = unsafe extern "system" fn(c::HANDLE) -> c::BOOL;

type StackWalkExFn = unsafe extern "system" fn(
c::DWORD,
c::HANDLE,
c::HANDLE,
*mut c::STACKFRAME_EX,
*mut c::CONTEXT,
*mut c_void,
*mut c_void,
*mut c_void,
*mut c_void,
c::DWORD,
) -> c::BOOL;

type StackWalk64Fn = unsafe extern "system" fn(
c::DWORD,
c::HANDLE,
c::HANDLE,
*mut c::STACKFRAME64,
*mut c::CONTEXT,
*mut c_void,
*mut c_void,
*mut c_void,
*mut c_void,
) -> c::BOOL;

trait StackWalker {
type Item: StackFrame;

fn walk(&self, c::DWORD, c::HANDLE, c::HANDLE, &mut Self::Item, &mut c::CONTEXT) -> c::BOOL;
}

impl StackWalker for StackWalkExFn {
type Item = c::STACKFRAME_EX;
fn walk(
&self,
image: c::DWORD,
process: c::HANDLE,
thread: c::HANDLE,
frame: &mut Self::Item,
context: &mut c::CONTEXT,
) -> c::BOOL {
unsafe {
self(
image,
process,
thread,
frame,
context,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
)
}
}
}

Ok((i, backtrace_context))
impl StackWalker for StackWalk64Fn {
type Item = c::STACKFRAME64;
fn walk(
&self,
image: c::DWORD,
process: c::HANDLE,
thread: c::HANDLE,
frame: &mut Self::Item,
context: &mut c::CONTEXT,
) -> c::BOOL {
unsafe {
self(
image,
process,
thread,
frame,
context,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
)
}
}
}

type SymInitializeFn =
unsafe extern "system" fn(c::HANDLE, *mut c_void,
c::BOOL) -> c::BOOL;
type SymCleanupFn =
unsafe extern "system" fn(c::HANDLE) -> c::BOOL;

type StackWalkExFn =
unsafe extern "system" fn(c::DWORD, c::HANDLE, c::HANDLE,
*mut c::STACKFRAME_EX, *mut c::CONTEXT,
*mut c_void, *mut c_void,
*mut c_void, *mut c_void, c::DWORD) -> c::BOOL;

#[cfg(target_arch = "x86")]
fn init_frame(frame: &mut c::STACKFRAME_EX,
ctx: &c::CONTEXT) -> c::DWORD {
frame.AddrPC.Offset = ctx.Eip as u64;
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
frame.AddrStack.Offset = ctx.Esp as u64;
frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
frame.AddrFrame.Offset = ctx.Ebp as u64;
frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_I386
trait StackFrame {
fn new() -> Self;
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD;
fn get_addr(&self) -> *const u8;
}

#[cfg(target_arch = "x86_64")]
fn init_frame(frame: &mut c::STACKFRAME_EX,
ctx: &c::CONTEXT) -> c::DWORD {
frame.AddrPC.Offset = ctx.Rip as u64;
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
frame.AddrStack.Offset = ctx.Rsp as u64;
frame.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
frame.AddrFrame.Offset = ctx.Rbp as u64;
frame.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_AMD64
impl StackFrame for c::STACKFRAME_EX {
fn new() -> c::STACKFRAME_EX {
unsafe { mem::zeroed() }
}

#[cfg(target_arch = "x86")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Eip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Esp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Ebp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_I386
}
#[cfg(target_arch = "x86_64")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Rip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Rsp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Rbp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_AMD64
}

fn get_addr(&self) -> *const u8 {
(self.AddrPC.Offset - 1) as *const u8
}
}

impl StackFrame for c::STACKFRAME64 {
fn new() -> c::STACKFRAME64 {
unsafe { mem::zeroed() }
}

#[cfg(target_arch = "x86")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Eip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Esp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Ebp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_I386
}
#[cfg(target_arch = "x86_64")]
fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
self.AddrPC.Offset = ctx.Rip as u64;
self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrStack.Offset = ctx.Rsp as u64;
self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
self.AddrFrame.Offset = ctx.Rbp as u64;
self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
c::IMAGE_FILE_MACHINE_AMD64
}

fn get_addr(&self) -> *const u8 {
(self.AddrPC.Offset - 1) as *const u8
}
}

enum StackWalkVariant {
StackWalkEx(StackWalkExFn, printing::PrintingFnsEx),
StackWalk64(StackWalk64Fn, printing::PrintingFns64),
}

pub struct BacktraceContext {
handle: c::HANDLE,
SymCleanup: SymCleanupFn,
// Only used in printing for msvc and not gnu
// The gnu version is effectively a ZST dummy.
#[allow(dead_code)]
StackWalkVariant: StackWalkVariant,
// keeping DynamycLibrary loaded until its functions no longer needed
#[allow(dead_code)]
dbghelp: DynamicLibrary,
}

impl Drop for BacktraceContext {
fn drop(&mut self) {
unsafe { (self.SymCleanup)(self.handle); }
unsafe {
(self.SymCleanup)(self.handle);
}
}
}
14 changes: 14 additions & 0 deletions src/libstd/sys/windows/backtrace/printing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ mod printing;
#[cfg(target_env = "gnu")]
mod printing {
pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};

// dummy functions to mirror those present in msvc version.
use sys::dynamic_lib::DynamicLibrary;
use io;
pub struct PrintingFnsEx {}
pub struct PrintingFns64 {}
pub fn load_printing_fns_ex(_: &DynamicLibrary) -> io::Result<PrintingFnsEx> {
Ok(PrintingFnsEx{})
}
pub fn load_printing_fns_64(_: &DynamicLibrary) -> io::Result<PrintingFns64> {
Ok(PrintingFns64{})
}
}

pub use self::printing::{foreach_symbol_fileline, resolve_symname};
pub use self::printing::{load_printing_fns_ex, load_printing_fns_64,
PrintingFnsEx, PrintingFns64};
Loading

0 comments on commit 6e5e63a

Please sign in to comment.