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
This commit is contained in:
parent
fe597dc9a9
commit
4186037aaa
7 changed files with 140 additions and 7 deletions
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue