aboutsummaryrefslogtreecommitdiff
path: root/ctr-std/src/sys/windows/backtrace
diff options
context:
space:
mode:
authorValentin <[email protected]>2018-06-15 18:57:24 +0200
committerFenrirWolf <[email protected]>2018-06-15 10:57:24 -0600
commitf2a90174bb36b9ad528e863ab34c02ebce002b02 (patch)
tree959e8d67883d3a89e179b3549b1f30d28e51a87c /ctr-std/src/sys/windows/backtrace
parentMerge pull request #68 from linouxis9/master (diff)
downloadctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.tar.xz
ctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.zip
Update for latest nightly 2018-06-09 (#70)
* Update for latest nightly 2018-06-09 * We now have a proper horizon os and sys modules in libstd
Diffstat (limited to 'ctr-std/src/sys/windows/backtrace')
-rw-r--r--ctr-std/src/sys/windows/backtrace/backtrace_gnu.rs62
-rw-r--r--ctr-std/src/sys/windows/backtrace/mod.rs155
-rw-r--r--ctr-std/src/sys/windows/backtrace/printing/mod.rs20
-rw-r--r--ctr-std/src/sys/windows/backtrace/printing/msvc.rs97
4 files changed, 334 insertions, 0 deletions
diff --git a/ctr-std/src/sys/windows/backtrace/backtrace_gnu.rs b/ctr-std/src/sys/windows/backtrace/backtrace_gnu.rs
new file mode 100644
index 0000000..f0d29dd
--- /dev/null
+++ b/ctr-std/src/sys/windows/backtrace/backtrace_gnu.rs
@@ -0,0 +1,62 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use io;
+use sys::c;
+use libc::c_char;
+use path::PathBuf;
+use fs::{OpenOptions, File};
+use sys::ext::fs::OpenOptionsExt;
+use sys::handle::Handle;
+use super::super::{fill_utf16_buf, os2path, to_u16s, wide_char_to_multi_byte};
+
+fn query_full_process_image_name() -> io::Result<PathBuf> {
+ unsafe {
+ let process_handle = Handle::new(c::OpenProcess(c::PROCESS_QUERY_INFORMATION,
+ c::FALSE,
+ c::GetCurrentProcessId()));
+ fill_utf16_buf(|buf, mut sz| {
+ if c::QueryFullProcessImageNameW(process_handle.raw(), 0, buf, &mut sz) == 0 {
+ 0
+ } else {
+ sz
+ }
+ }, os2path)
+ }
+}
+
+fn lock_and_get_executable_filename() -> io::Result<(PathBuf, File)> {
+ // We query the current image name, open the file without FILE_SHARE_DELETE so it
+ // can't be moved and then get the current image name again. If the names are the
+ // same than we have successfully locked the file
+ let image_name1 = query_full_process_image_name()?;
+ let file = OpenOptions::new()
+ .read(true)
+ .share_mode(c::FILE_SHARE_READ | c::FILE_SHARE_WRITE)
+ .open(&image_name1)?;
+ let image_name2 = query_full_process_image_name()?;
+
+ if image_name1 != image_name2 {
+ return Err(io::Error::new(io::ErrorKind::Other,
+ "executable moved while trying to lock it"));
+ }
+
+ Ok((image_name1, file))
+}
+
+// Get the executable filename for libbacktrace
+// This returns the path in the ANSI code page and a File which should remain open
+// for as long as the path should remain valid
+pub fn get_executable_filename() -> io::Result<(Vec<c_char>, File)> {
+ let (executable, file) = lock_and_get_executable_filename()?;
+ let u16_executable = to_u16s(executable.into_os_string())?;
+ Ok((wide_char_to_multi_byte(c::CP_ACP, c::WC_NO_BEST_FIT_CHARS,
+ &u16_executable, true)?, file))
+}
diff --git a/ctr-std/src/sys/windows/backtrace/mod.rs b/ctr-std/src/sys/windows/backtrace/mod.rs
new file mode 100644
index 0000000..82498ad
--- /dev/null
+++ b/ctr-std/src/sys/windows/backtrace/mod.rs
@@ -0,0 +1,155 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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::<usize, $t>(f)
+ })
+ )
+}
+
+mod printing;
+
+#[cfg(target_env = "gnu")]
+#[path = "backtrace_gnu.rs"]
+pub mod gnu;
+
+pub use self::printing::{resolve_symname, foreach_symbol_fileline};
+
+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)?;
+
+ // 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,
+ 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!
+ 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))
+}
+
+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
+}
+
+#[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
+}
+
+pub struct BacktraceContext {
+ handle: c::HANDLE,
+ SymCleanup: SymCleanupFn,
+ // Only used in printing for msvc and not gnu
+ #[allow(dead_code)]
+ dbghelp: DynamicLibrary,
+}
+
+impl Drop for BacktraceContext {
+ fn drop(&mut self) {
+ unsafe { (self.SymCleanup)(self.handle); }
+ }
+}
diff --git a/ctr-std/src/sys/windows/backtrace/printing/mod.rs b/ctr-std/src/sys/windows/backtrace/printing/mod.rs
new file mode 100644
index 0000000..3e566f6
--- /dev/null
+++ b/ctr-std/src/sys/windows/backtrace/printing/mod.rs
@@ -0,0 +1,20 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[cfg(target_env = "msvc")]
+#[path = "msvc.rs"]
+mod printing;
+
+#[cfg(target_env = "gnu")]
+mod printing {
+ pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname};
+}
+
+pub use self::printing::{foreach_symbol_fileline, resolve_symname};
diff --git a/ctr-std/src/sys/windows/backtrace/printing/msvc.rs b/ctr-std/src/sys/windows/backtrace/printing/msvc.rs
new file mode 100644
index 0000000..967df1c
--- /dev/null
+++ b/ctr-std/src/sys/windows/backtrace/printing/msvc.rs
@@ -0,0 +1,97 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use ffi::CStr;
+use io;
+use libc::{c_ulong, c_char};
+use mem;
+use sys::c;
+use sys::backtrace::BacktraceContext;
+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;
+
+/// 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<()>
+{
+ let SymFromInlineContext = sym!(&context.dbghelp,
+ "SymFromInlineContext",
+ SymFromInlineContextFn)?;
+
+ 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)
+ }
+}
+
+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)?;
+
+ 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)
+ }
+}