about summary refs log tree commit diff
path: root/src/libstd/sys
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-01-28 20:32:56 +0000
committerbors <bors@rust-lang.org>2017-01-28 20:32:56 +0000
commit1491e0425911806f2612f05ee8caf21666447128 (patch)
treeea70520dbf0d73ef2fa82d91074776160517b8bb /src/libstd/sys
parentc81c1d6a41babf7e34120625727211cad7b40b87 (diff)
parentab21314c3fbf093c92123abee62101d15846c1e2 (diff)
Auto merge of #39234 - segevfiner:fix-backtraces-on-windows-gnu, r=petrochenkov
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

Note though that this isn't that pretty...

I had to implement a `WideCharToMultiByte` wrapper function to convert to the ANSI code page. This will work better than only allowing ASCII provided that the ANSI code page is set to the user's local language, which is often the case.

Also, please make sure that I didn't break the Unix build.
Diffstat (limited to 'src/libstd/sys')
-rw-r--r--src/libstd/sys/unix/backtrace/mod.rs11
-rw-r--r--src/libstd/sys/windows/backtrace.rs4
-rw-r--r--src/libstd/sys/windows/backtrace_gnu.rs62
-rw-r--r--src/libstd/sys/windows/c.rs40
-rw-r--r--src/libstd/sys/windows/mod.rs47
5 files changed, 164 insertions, 0 deletions
diff --git a/src/libstd/sys/unix/backtrace/mod.rs b/src/libstd/sys/unix/backtrace/mod.rs
index d7c05e513f6..1eef89bf66f 100644
--- a/src/libstd/sys/unix/backtrace/mod.rs
+++ b/src/libstd/sys/unix/backtrace/mod.rs
@@ -89,3 +89,14 @@ pub use self::tracing::write;
 mod tracing;
 // symbol resolvers:
 mod printing;
+
+#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "emscripten")))]
+pub mod gnu {
+    use io;
+    use fs;
+    use libc::c_char;
+
+    pub fn get_executable_filename() -> io::Result<(Vec<c_char>, 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..94aaf439f3d 100644
--- a/src/libstd/sys/windows/backtrace.rs
+++ b/src/libstd/sys/windows/backtrace.rs
@@ -51,6 +51,10 @@ mod printing;
 #[path = "printing/gnu.rs"]
 mod printing;
 
+#[cfg(target_env = "gnu")]
+#[path = "backtrace_gnu.rs"]
+pub mod gnu;
+
 type SymInitializeFn =
     unsafe extern "system" fn(c::HANDLE, *mut c_void,
                               c::BOOL) -> c::BOOL;
diff --git a/src/libstd/sys/windows/backtrace_gnu.rs b/src/libstd/sys/windows/backtrace_gnu.rs
new file mode 100644
index 00000000000..f0d29dd4178
--- /dev/null
+++ b/src/libstd/sys/windows/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/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs
index dc7b2fc9a6b..1b29bf73c7a 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;
 
@@ -973,6 +974,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,
@@ -1178,3 +1187,34 @@ compat_fn! {
         panic!("rwlocks not available")
     }
 }
+
+#[cfg(target_env = "gnu")]
+mod gnu {
+    use super::*;
+
+    pub const PROCESS_QUERY_INFORMATION: DWORD = 0x0400;
+
+    pub const CP_ACP: UINT = 0;
+
+    pub const WC_NO_BEST_FIT_CHARS: DWORD = 0x00000400;
+
+    extern "system" {
+        pub fn OpenProcess(dwDesiredAccess: DWORD,
+                           bInheritHandle: BOOL,
+                           dwProcessId: DWORD) -> HANDLE;
+    }
+
+    compat_fn! {
+        kernel32:
+
+        pub fn QueryFullProcessImageNameW(_hProcess: HANDLE,
+                                          _dwFlags: DWORD,
+                                          _lpExeName: LPWSTR,
+                                          _lpdwSize: LPDWORD) -> BOOL {
+            SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
+        }
+    }
+}
+
+#[cfg(target_env = "gnu")]
+pub use self::gnu::*;
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index defc41c5f46..4424c6c6136 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,52 @@ fn os2path(s: &[u16]) -> PathBuf {
     PathBuf::from(OsString::from_wide(s))
 }
 
+#[allow(dead_code)] // Only used in backtrace::gnu::get_executable_filename()
+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