about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authormoxian <moxian@google.com>2018-05-08 06:19:55 +0000
committermoxian <moxian@google.com>2018-06-28 21:56:42 +0000
commitd39c66bf4ff2b49957edadf10afcc4050c3fc60b (patch)
treec75ab891a3b5de6946c513d2b6203bf66367d2bf /src/libstd
parente3bf634e060bc2f8665878288bcea02008ca346e (diff)
downloadrust-d39c66bf4ff2b49957edadf10afcc4050c3fc60b.tar.gz
rust-d39c66bf4ff2b49957edadf10afcc4050c3fc60b.zip
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
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/sys/windows/backtrace/mod.rs170
-rw-r--r--src/libstd/sys/windows/backtrace/printing/msvc.rs253
-rw-r--r--src/libstd/sys/windows/c.rs16
3 files changed, 331 insertions, 108 deletions
diff --git a/src/libstd/sys/windows/backtrace/mod.rs b/src/libstd/sys/windows/backtrace/mod.rs
index 82498ad4d58..8e879e0f49e 100644
--- a/src/libstd/sys/windows/backtrace/mod.rs
+++ b/src/libstd/sys/windows/backtrace/mod.rs
@@ -48,24 +48,21 @@ pub mod gnu;
 
 pub use self::printing::{resolve_symname, foreach_symbol_fileline};
 
-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 ResStackWalkEx = sym!(dbghelp, "StackWalkEx", StackWalkExFn);
+    let ResStackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn);
 
     // 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,
@@ -76,49 +73,139 @@ pub fn unwind_backtrace(frames: &mut [Frame])
     // 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!
-    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;
+    match (ResStackWalkEx, ResStackWalk64) {
+        (Ok(StackWalkEx), _) => {
+            let mut frame: c::STACKFRAME_EX = unsafe { mem::zeroed() };
+            frame.StackFrameSize = mem::size_of_val(&frame) as c::DWORD;
+            let image = init_frame_ex(&mut frame, &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;
+                }
+            }
+
+            Ok((i, backtrace_context))
         }
+        (_, Ok(StackWalk64)) => {
+            let mut frame: c::STACKFRAME64 = unsafe { mem::zeroed() };
+            let image = init_frame_64(&mut frame, &context);
+
+            // Start from -1 to avoid printing this stack frame, which will
+            // always be exactly the same.
+            let mut i = 0;
+            unsafe {
+                while i < frames.len()
+                    && StackWalk64(
+                        image,
+                        process,
+                        thread,
+                        &mut frame,
+                        &mut context,
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                    ) == c::TRUE
+                {
+                    let addr = frame.AddrPC.Offset;
+                    if addr == frame.AddrReturn.Offset || addr == 0 || frame.AddrReturn.Offset == 0
+                    {
+                        break;
+                    }
+
+                    frames[i] = Frame {
+                        symbol_addr: (addr - 1) as *const u8,
+                        exact_position: (addr - 1) as *const u8,
+                        inline_context: 0,
+                    };
+                    i += 1;
+                }
+            }
+
+            Ok((i, backtrace_context))
+        }
+        (Err(e), _) => Err(e),
     }
-
-    Ok((i, backtrace_context))
 }
 
-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 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;
 
-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_ex(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
+}
+
+#[cfg(target_arch = "x86_64")]
+fn init_frame_ex(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
+}
 
 #[cfg(target_arch = "x86")]
-fn init_frame(frame: &mut c::STACKFRAME_EX,
-              ctx: &c::CONTEXT) -> c::DWORD {
+fn init_frame_64(frame: &mut c::STACKFRAME64, 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;
@@ -129,8 +216,7 @@ fn init_frame(frame: &mut c::STACKFRAME_EX,
 }
 
 #[cfg(target_arch = "x86_64")]
-fn init_frame(frame: &mut c::STACKFRAME_EX,
-              ctx: &c::CONTEXT) -> c::DWORD {
+fn init_frame_64(frame: &mut c::STACKFRAME64, 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;
diff --git a/src/libstd/sys/windows/backtrace/printing/msvc.rs b/src/libstd/sys/windows/backtrace/printing/msvc.rs
index 967df1c8a2d..e26d95a4f9d 100644
--- a/src/libstd/sys/windows/backtrace/printing/msvc.rs
+++ b/src/libstd/sys/windows/backtrace/printing/msvc.rs
@@ -10,88 +10,209 @@
 
 use ffi::CStr;
 use io;
-use libc::{c_ulong, c_char};
+use libc::{c_char, c_ulong};
 use mem;
-use sys::c;
 use sys::backtrace::BacktraceContext;
+use sys::c;
 use sys_common::backtrace::Frame;
 
 type SymFromInlineContextFn =
-    unsafe extern "system" fn(c::HANDLE, u64, c::ULONG,
-                              *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
-type SymGetLineFromInlineContextFn =
-    unsafe extern "system" fn(c::HANDLE, u64, c::ULONG,
-                              u64, *mut c::DWORD, *mut c::IMAGEHLP_LINE64) -> c::BOOL;
+    unsafe extern "system" fn(c::HANDLE, u64, c::ULONG, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
+type SymFromInlineContextFn =
+    unsafe extern "system" fn(c::HANDLE, u64, c::ULONG, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
+type SymGetLineFromInlineContextFn = unsafe extern "system" fn(
+    c::HANDLE,
+    u64,
+    c::ULONG,
+    u64,
+    *mut c::DWORD,
+    *mut c::IMAGEHLP_LINE64,
+) -> c::BOOL;
+
+type SymFromAddrFn =
+    unsafe extern "system" fn(c::HANDLE, u64, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
+type SymGetLineFromAddr64Fn =
+    unsafe extern "system" fn(c::HANDLE, u64, *mut u32, *mut c::IMAGEHLP_LINE64) -> c::BOOL;
 
 /// Converts a pointer to symbol to its string value.
-pub fn resolve_symname<F>(frame: Frame,
-                          callback: F,
-                          context: &BacktraceContext) -> io::Result<()>
-    where F: FnOnce(Option<&str>) -> io::Result<()>
+pub fn resolve_symname<F>(frame: Frame, callback: F, context: &BacktraceContext) -> io::Result<()>
+where
+    F: FnOnce(Option<&str>) -> io::Result<()>,
 {
-    let SymFromInlineContext = sym!(&context.dbghelp,
-                                    "SymFromInlineContext",
-                                    SymFromInlineContextFn)?;
+    match (
+        sym!(
+            &context.dbghelp,
+            "SymFromInlineContext",
+            SymFromInlineContextFn
+        ),
+        sym!(&context.dbghelp, "SymFromAddr", SymFromAddrFn),
+    ) {
+        (Ok(SymFromInlineContext), _) => unsafe {
+            let mut info: c::SYMBOL_INFO = mem::zeroed();
+            info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
+            // the struct size in C.  the value is different to
+            // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
+            // due to struct alignment.
+            info.SizeOfStruct = 88;
 
-    unsafe {
-        let mut info: c::SYMBOL_INFO = mem::zeroed();
-        info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
-        // the struct size in C.  the value is different to
-        // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
-        // due to struct alignment.
-        info.SizeOfStruct = 88;
+            let mut displacement = 0u64;
+            let ret = SymFromInlineContext(
+                context.handle,
+                frame.symbol_addr as u64,
+                frame.inline_context,
+                &mut displacement,
+                &mut info,
+            );
+            let valid_range =
+                if ret == c::TRUE && frame.symbol_addr as usize >= info.Address as usize {
+                    if info.Size != 0 {
+                        (frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
+                    } else {
+                        true
+                    }
+                } else {
+                    false
+                };
+            let symname = if valid_range {
+                let ptr = info.Name.as_ptr() as *const c_char;
+                CStr::from_ptr(ptr).to_str().ok()
+            } else {
+                None
+            };
+            callback(symname)
+{
+    match (
+        sym!(
+            &context.dbghelp,
+            "SymFromInlineContext",
+            SymFromInlineContextFn
+        ),
+        sym!(&context.dbghelp, "SymFromAddr", SymFromAddrFn),
+    ) {
+        (Ok(SymFromInlineContext), _) => unsafe {
+            let mut info: c::SYMBOL_INFO = mem::zeroed();
+            info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
+            // the struct size in C.  the value is different to
+            // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
+            // due to struct alignment.
+            info.SizeOfStruct = 88;
 
-        let mut displacement = 0u64;
-        let ret = SymFromInlineContext(context.handle,
-                                       frame.symbol_addr as u64,
-                                       frame.inline_context,
-                                       &mut displacement,
-                                       &mut info);
-        let valid_range = if ret == c::TRUE &&
-                             frame.symbol_addr as usize >= info.Address as usize {
-            if info.Size != 0 {
-                (frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
+            let mut displacement = 0u64;
+            let ret = SymFromInlineContext(
+                context.handle,
+                frame.symbol_addr as u64,
+                frame.inline_context,
+                &mut displacement,
+                &mut info,
+            );
+            let valid_range =
+                if ret == c::TRUE && frame.symbol_addr as usize >= info.Address as usize {
+                    if info.Size != 0 {
+                        (frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
+                    } else {
+                        true
+                    }
+                } else {
+                    false
+                };
+            let symname = if valid_range {
+                let ptr = info.Name.as_ptr() as *const c_char;
+                CStr::from_ptr(ptr).to_str().ok()
             } else {
-                true
-            }
-        } else {
-            false
-        };
-        let symname = if valid_range {
-            let ptr = info.Name.as_ptr() as *const c_char;
-            CStr::from_ptr(ptr).to_str().ok()
-        } else {
-            None
-        };
-        callback(symname)
+                None
+            };
+            callback(symname)
+        },
+        (_, Ok(SymFromAddr)) => unsafe {
+            } else {
+                None
+            };
+            callback(symname)
+        },
+        (_, Ok(SymFromAddr)) => unsafe {
+            let mut info: c::SYMBOL_INFO = mem::zeroed();
+            info.MaxNameLen = c::MAX_SYM_NAME as c_ulong;
+            // the struct size in C.  the value is different to
+            // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81)
+            // due to struct alignment.
+            info.SizeOfStruct = 88;
+
+            let mut displacement = 0u64;
+            let ret = SymFromAddr(
+                context.handle,
+                frame.symbol_addr as u64,
+                &mut displacement,
+                &mut info,
+            );
+
+            let symname = if ret == c::TRUE {
+                let ptr = info.Name.as_ptr() as *const c_char;
+                CStr::from_ptr(ptr).to_str().ok()
+            } else {
+                None
+            };
+            callback(symname)
+        },
+        (Err(e), _) => Err(e),
     }
 }
 
-pub fn foreach_symbol_fileline<F>(frame: Frame,
-                                  mut f: F,
-                                  context: &BacktraceContext)
-    -> io::Result<bool>
-    where F: FnMut(&[u8], u32) -> io::Result<()>
+pub fn foreach_symbol_fileline<F>(
+    frame: Frame,
+    mut f: F,
+    context: &BacktraceContext,
+) -> io::Result<bool>
+where
+    F: FnMut(&[u8], u32) -> io::Result<()>,
 {
-    let SymGetLineFromInlineContext = sym!(&context.dbghelp,
-                                    "SymGetLineFromInlineContext",
-                                    SymGetLineFromInlineContextFn)?;
+    match (
+        sym!(
+            &context.dbghelp,
+            "SymGetLineFromInlineContext",
+            SymGetLineFromInlineContextFn
+        ),
+        sym!(
+            &context.dbghelp,
+            "SymGetLineFromAddr64",
+            SymGetLineFromAddr64Fn
+        ),
+    ) {
+        (Ok(SymGetLineFromInlineContext), _) => unsafe {
+            let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
+            line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
 
-    unsafe {
-        let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
-        line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
+            let mut displacement = 0u32;
+            let ret = SymGetLineFromInlineContext(
+                context.handle,
+                frame.exact_position as u64,
+                frame.inline_context,
+                0,
+                &mut displacement,
+                &mut line,
+            );
+            if ret == c::TRUE {
+                let name = CStr::from_ptr(line.Filename).to_bytes();
+                f(name, line.LineNumber as u32)?;
+            }
+            Ok(false)
+        },
+        (_, Ok(SymGetLineFromAddr64)) => unsafe {
+            let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
+            line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;
 
-        let mut displacement = 0u32;
-        let ret = SymGetLineFromInlineContext(context.handle,
-                                              frame.exact_position as u64,
-                                              frame.inline_context,
-                                              0,
-                                              &mut displacement,
-                                              &mut line);
-        if ret == c::TRUE {
-            let name = CStr::from_ptr(line.Filename).to_bytes();
-            f(name, line.LineNumber as u32)?;
-        }
-        Ok(false)
+            let mut displacement = 0u32;
+            let ret = SymGetLineFromAddr64(
+                context.handle,
+                frame.exact_position as u64,
+                &mut displacement,
+                &mut line,
+            );
+            if ret == c::TRUE {
+                let name = CStr::from_ptr(line.Filename).to_bytes();
+                f(name, line.LineNumber as u32)?;
+            }
+            Ok(false)
+        },
+        (Err(e), _) => Err(e),
     }
 }
diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs
index 6d929f21365..30aba2f400f 100644
--- a/src/libstd/sys/windows/c.rs
+++ b/src/libstd/sys/windows/c.rs
@@ -637,6 +637,22 @@ pub struct STACKFRAME_EX {
 
 #[repr(C)]
 #[cfg(feature = "backtrace")]
+pub struct STACKFRAME64 {
+    pub AddrPC: ADDRESS64,
+    pub AddrReturn: ADDRESS64,
+    pub AddrFrame: ADDRESS64,
+    pub AddrStack: ADDRESS64,
+    pub AddrBStore: ADDRESS64,
+    pub FuncTableEntry: *mut c_void,
+    pub Params: [u64; 4],
+    pub Far: BOOL,
+    pub Virtual: BOOL,
+    pub Reserved: [u64; 3],
+    pub KdHelp: KDHELP64,
+}
+
+#[repr(C)]
+#[cfg(feature = "backtrace")]
 pub struct KDHELP64 {
     pub Thread: u64,
     pub ThCallbackStack: DWORD,