about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSegev Finer <segev208@gmail.com>2017-01-22 00:28:17 +0200
committerSegev Finer <segev208@gmail.com>2017-01-24 23:10:00 +0200
commit4186037aaa1a6aa3ba15a10130b424e7508bd276 (patch)
treeebd6d096512c8bf550f774dcf89dce910f9dccb3
parentfe597dc9a9f325de9b2b11d69e190e1c22c4e7dc (diff)
downloadrust-4186037aaa1a6aa3ba15a10130b424e7508bd276.tar.gz
rust-4186037aaa1a6aa3ba15a10130b424e7508bd276.zip
Make backtraces work on Windows GNU targets again.
This is done by adding a function that can return a filename
to pass to backtrace_create_state. The filename is obtained in
a safe way by first getting the filename, locking the file so it can't
be moved, and then getting the filename again and making sure it's the same.

See: https://github.com/rust-lang/rust/pull/37359#issuecomment-260123399
Issue: #33985
-rw-r--r--src/libstd/sys/unix/backtrace/mod.rs7
-rw-r--r--src/libstd/sys/windows/backtrace.rs48
-rw-r--r--src/libstd/sys/windows/c.rs23
-rw-r--r--src/libstd/sys/windows/mod.rs46
-rw-r--r--src/libstd/sys_common/gnu/libbacktrace.rs17
-rw-r--r--src/test/run-pass/backtrace-debuginfo.rs2
-rw-r--r--src/test/run-pass/backtrace.rs4
7 files changed, 140 insertions, 7 deletions
diff --git a/src/libstd/sys/unix/backtrace/mod.rs b/src/libstd/sys/unix/backtrace/mod.rs
index d7c05e513f6..e3f1b23f7a2 100644
--- a/src/libstd/sys/unix/backtrace/mod.rs
+++ b/src/libstd/sys/unix/backtrace/mod.rs
@@ -83,9 +83,16 @@
 /// to symbols. This is a bit of a hokey implementation as-is, but it works for
 /// all unix platforms we support right now, so it at least gets the job done.
 
+use io;
+use fs;
+
 pub use self::tracing::write;
 
 // tracing impls:
 mod tracing;
 // symbol resolvers:
 mod printing;
+
+pub fn get_executable_filename() -> io::Result<(Vec<i8>, fs::File)> {
+    Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
+}
diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs
index 82a44c1c110..e6182cda58a 100644
--- a/src/libstd/sys/windows/backtrace.rs
+++ b/src/libstd/sys/windows/backtrace.rs
@@ -30,9 +30,13 @@ use io;
 use libc::c_void;
 use mem;
 use ptr;
+use path::PathBuf;
+use fs::{OpenOptions, File};
+use sys::ext::fs::OpenOptionsExt;
 use sys::c;
 use sys::dynamic_lib::DynamicLibrary;
 use sys::mutex::Mutex;
+use sys::handle::Handle;
 
 macro_rules! sym {
     ($lib:expr, $e:expr, $t:ident) => (
@@ -157,3 +161,47 @@ unsafe fn _write(w: &mut Write) -> io::Result<()> {
 
     Ok(())
 }
+
+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()));
+        super::fill_utf16_buf(|buf, mut sz| {
+            if c::QueryFullProcessImageNameW(process_handle.raw(), 0, buf, &mut sz) == 0 {
+                0
+            } else {
+                sz
+            }
+        }, super::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<i8>, File)> {
+    let (executable, file) = lock_and_get_executable_filename()?;
+    let u16_executable = super::to_u16s(executable.into_os_string())?;
+    Ok((super::wide_char_to_multi_byte(c::CP_ACP, c::WC_NO_BEST_FIT_CHARS,
+                                       &u16_executable, true)?, file))
+}
diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs
index dc7b2fc9a6b..c6fac6d1759 100644
--- a/src/libstd/sys/windows/c.rs
+++ b/src/libstd/sys/windows/c.rs
@@ -69,6 +69,7 @@ pub type LPWCH = *mut WCHAR;
 pub type LPWIN32_FIND_DATAW = *mut WIN32_FIND_DATAW;
 pub type LPWSADATA = *mut WSADATA;
 pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO;
+pub type LPSTR = *mut CHAR;
 pub type LPWSTR = *mut WCHAR;
 pub type LPFILETIME = *mut FILETIME;
 
@@ -157,6 +158,7 @@ pub const WSAECONNREFUSED: c_int = 10061;
 
 pub const MAX_PROTOCOL_CHAIN: DWORD = 7;
 
+pub const PROCESS_QUERY_INFORMATION: DWORD = 0x0400;
 pub const TOKEN_READ: DWORD = 0x20008;
 pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024;
 pub const FSCTL_GET_REPARSE_POINT: DWORD = 0x900a8;
@@ -218,6 +220,10 @@ pub const CREATE_NEW_PROCESS_GROUP: DWORD = 0x00000200;
 pub const CREATE_UNICODE_ENVIRONMENT: DWORD = 0x00000400;
 pub const STARTF_USESTDHANDLES: DWORD = 0x00000100;
 
+pub const CP_ACP: UINT = 0;
+
+pub const WC_NO_BEST_FIT_CHARS: DWORD = 0x00000400;
+
 pub const AF_INET: c_int = 2;
 pub const AF_INET6: c_int = 23;
 pub const SD_BOTH: c_int = 2;
@@ -888,6 +894,9 @@ extern "system" {
                               pNumArgs: *mut c_int) -> *mut *mut u16;
     pub fn GetTempPathW(nBufferLength: DWORD,
                         lpBuffer: LPCWSTR) -> DWORD;
+    pub fn OpenProcess(dwDesiredAccess: DWORD,
+                       bInheritHandle: BOOL,
+                       dwProcessId: DWORD) -> HANDLE;
     pub fn OpenProcessToken(ProcessHandle: HANDLE,
                             DesiredAccess: DWORD,
                             TokenHandle: *mut HANDLE) -> BOOL;
@@ -973,6 +982,14 @@ extern "system" {
     pub fn DeleteFileW(lpPathName: LPCWSTR) -> BOOL;
     pub fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: LPWSTR) -> DWORD;
     pub fn SetCurrentDirectoryW(lpPathName: LPCWSTR) -> BOOL;
+    pub fn WideCharToMultiByte(CodePage: UINT,
+                               dwFlags: DWORD,
+                               lpWideCharStr: LPCWSTR,
+                               cchWideChar: c_int,
+                               lpMultiByteStr: LPSTR,
+                               cbMultiByte: c_int,
+                               lpDefaultChar: LPCSTR,
+                               lpUsedDefaultChar: LPBOOL) -> c_int;
 
     pub fn closesocket(socket: SOCKET) -> c_int;
     pub fn recv(socket: SOCKET, buf: *mut c_void, len: c_int,
@@ -1136,6 +1153,12 @@ compat_fn! {
                                      _dwFlags: DWORD) -> DWORD {
         SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
     }
+    pub fn QueryFullProcessImageNameW(_hProcess: HANDLE,
+                                      _dwFlags: DWORD,
+                                      _lpExeName: LPWSTR,
+                                      _lpdwSize: LPDWORD) -> BOOL {
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
+    }
     pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL {
         SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
     }
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index defc41c5f46..4468cf574b3 100644
--- a/src/libstd/sys/windows/mod.rs
+++ b/src/libstd/sys/windows/mod.rs
@@ -10,6 +10,7 @@
 
 #![allow(missing_docs, bad_style)]
 
+use ptr;
 use ffi::{OsStr, OsString};
 use io::{self, ErrorKind};
 use os::windows::ffi::{OsStrExt, OsStringExt};
@@ -171,6 +172,51 @@ fn os2path(s: &[u16]) -> PathBuf {
     PathBuf::from(OsString::from_wide(s))
 }
 
+fn wide_char_to_multi_byte(code_page: u32,
+                           flags: u32,
+                           s: &[u16],
+                           no_default_char: bool)
+                           -> io::Result<Vec<i8>> {
+    unsafe {
+        let mut size = c::WideCharToMultiByte(code_page,
+                                              flags,
+                                              s.as_ptr(),
+                                              s.len() as i32,
+                                              ptr::null_mut(),
+                                              0,
+                                              ptr::null(),
+                                              ptr::null_mut());
+        if size == 0 {
+            return Err(io::Error::last_os_error());
+        }
+
+        let mut buf = Vec::with_capacity(size as usize);
+        buf.set_len(size as usize);
+
+        let mut used_default_char = c::FALSE;
+        size = c::WideCharToMultiByte(code_page,
+                                      flags,
+                                      s.as_ptr(),
+                                      s.len() as i32,
+                                      buf.as_mut_ptr(),
+                                      buf.len() as i32,
+                                      ptr::null(),
+                                      if no_default_char { &mut used_default_char }
+                                      else { ptr::null_mut() });
+        if size == 0 {
+            return Err(io::Error::last_os_error());
+        }
+        if no_default_char && used_default_char == c::TRUE {
+            return Err(io::Error::new(io::ErrorKind::InvalidData,
+                                      "string cannot be converted to requested code page"));
+        }
+
+        buf.set_len(size as usize);
+
+        Ok(buf)
+    }
+}
+
 pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
     match v.iter().position(|c| *c == 0) {
         // don't include the 0
diff --git a/src/libstd/sys_common/gnu/libbacktrace.rs b/src/libstd/sys_common/gnu/libbacktrace.rs
index b5802afc109..94d206f3ac3 100644
--- a/src/libstd/sys_common/gnu/libbacktrace.rs
+++ b/src/libstd/sys_common/gnu/libbacktrace.rs
@@ -16,6 +16,7 @@ use sys_common::backtrace::{output, output_fileline};
 pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
              symaddr: *mut libc::c_void) -> io::Result<()> {
     use ffi::CStr;
+    use mem;
     use ptr;
 
     ////////////////////////////////////////////////////////////////////////
@@ -124,7 +125,21 @@ pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
     unsafe fn init_state() -> *mut backtrace_state {
         static mut STATE: *mut backtrace_state = ptr::null_mut();
         if !STATE.is_null() { return STATE }
-        STATE = backtrace_create_state(ptr::null(), 0, error_cb,
+
+        let filename = match ::sys::backtrace::get_executable_filename() {
+            Ok((filename, file)) => {
+                // filename is purposely leaked here since libbacktrace requires
+                // it to stay allocated permanently, file is also leaked so that
+                // the file stays locked
+                let filename_ptr = filename.as_ptr();
+                mem::forget(filename);
+                mem::forget(file);
+                filename_ptr
+            },
+            Err(_) => ptr::null(),
+        };
+
+        STATE = backtrace_create_state(filename, 0, error_cb,
                                        ptr::null_mut());
         STATE
     }
diff --git a/src/test/run-pass/backtrace-debuginfo.rs b/src/test/run-pass/backtrace-debuginfo.rs
index 72cf109fd59..626eccfc9ec 100644
--- a/src/test/run-pass/backtrace-debuginfo.rs
+++ b/src/test/run-pass/backtrace-debuginfo.rs
@@ -37,7 +37,6 @@ macro_rules! dump_and_die {
                     target_os = "ios",
                     target_os = "android",
                     all(target_os = "linux", target_arch = "arm"),
-                    target_os = "windows",
                     target_os = "freebsd",
                     target_os = "dragonfly",
                     target_os = "bitrig",
@@ -173,4 +172,3 @@ fn main() {
         run_test(&args[0]);
     }
 }
-
diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs
index 75c665b04a1..834ce984e66 100644
--- a/src/test/run-pass/backtrace.rs
+++ b/src/test/run-pass/backtrace.rs
@@ -104,10 +104,6 @@ fn runtest(me: &str) {
 }
 
 fn main() {
-    if cfg!(windows) && cfg!(target_env = "gnu") {
-        return
-    }
-
     let args: Vec<String> = env::args().collect();
     if args.len() >= 2 && args[1] == "fail" {
         foo();