Skip to content

Commit

Permalink
Make backtraces work on Windows GNU targets again.
Browse files Browse the repository at this point in the history
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: #37359 (comment)
Issue: #33985
  • Loading branch information
segevfiner committed Jan 24, 2017
1 parent fe597dc commit 4186037
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 7 deletions.
7 changes: 7 additions & 0 deletions src/libstd/sys/unix/backtrace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
}
48 changes: 48 additions & 0 deletions src/libstd/sys/windows/backtrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => (
Expand Down Expand Up @@ -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))
}
23 changes: 23 additions & 0 deletions src/libstd/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}
Expand Down
46 changes: 46 additions & 0 deletions src/libstd/sys/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
Expand Down
17 changes: 16 additions & 1 deletion src/libstd/sys_common/gnu/libbacktrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 0 additions & 2 deletions src/test/run-pass/backtrace-debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -173,4 +172,3 @@ fn main() {
run_test(&args[0]);
}
}

4 changes: 0 additions & 4 deletions src/test/run-pass/backtrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 4186037

Please sign in to comment.