// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! As always, windows has something very different than unix, we mainly want //! to avoid having to depend too much on libunwind for windows. //! //! If you google around, you'll find a fair bit of references to built-in //! functions to get backtraces on windows. It turns out that most of these are //! in an external library called dbghelp. I was unable to find this library //! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent //! of it. //! //! You'll also find that there's a function called CaptureStackBackTrace //! mentioned frequently (which is also easy to use), but sadly I didn't have a //! copy of that function in my mingw install (maybe it was broken?). Instead, //! this takes the route of using StackWalk64 in order to walk the stack. #![allow(deprecated)] // dynamic_lib use io; use libc::c_void; use mem; use ptr; use sys::c; use sys::dynamic_lib::DynamicLibrary; use sys_common::backtrace::Frame; macro_rules! sym { ($lib:expr, $e:expr, $t:ident) => ( $lib.symbol($e).map(|f| unsafe { $crate::mem::transmute::(f) }) ) } mod printing; #[cfg(target_env = "gnu")] #[path = "backtrace_gnu.rs"] pub mod gnu; 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)> { 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)?; // 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 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)); } // 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(StackWalk: W, frames: &mut [Frame]) -> io::Result { 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; 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, ) } } } 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(), ) } } } trait StackFrame { fn new() -> Self; fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD; fn get_addr(&self) -> *const u8; } 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 } #[cfg(target_arch = "arm")] fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { self.AddrPC.Offset = ctx.Pc as u64; self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; self.AddrStack.Offset = ctx.Sp as u64; self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; self.AddrFrame.Offset = ctx.R11 as u64; self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; c::IMAGE_FILE_MACHINE_ARMNT } #[cfg(target_arch = "aarch64")] fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { self.AddrPC.Offset = ctx.Pc as u64; self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; self.AddrStack.Offset = ctx.Sp as u64; self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; self.AddrFrame.Offset = ctx.Fp as u64; self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; c::IMAGE_FILE_MACHINE_ARM64 } 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 } #[cfg(target_arch = "arm")] fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { self.AddrPC.Offset = ctx.Pc as u64; self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; self.AddrStack.Offset = ctx.Sp as u64; self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; self.AddrFrame.Offset = ctx.R11 as u64; self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; c::IMAGE_FILE_MACHINE_ARMNT } #[cfg(target_arch = "aarch64")] fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { self.AddrPC.Offset = ctx.Pc as u64; self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; self.AddrStack.Offset = ctx.Sp as u64; self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; self.AddrFrame.Offset = ctx.Fp as u64; self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; c::IMAGE_FILE_MACHINE_ARM64 } 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); } } }