Skip to content

Commit 4186037

Browse files
committed
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: #37359 (comment) Issue: #33985
1 parent fe597dc commit 4186037

File tree

7 files changed

+140
-7
lines changed

7 files changed

+140
-7
lines changed

src/libstd/sys/unix/backtrace/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,16 @@
8383
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
8484
/// all unix platforms we support right now, so it at least gets the job done.
8585
86+
use io;
87+
use fs;
88+
8689
pub use self::tracing::write;
8790

8891
// tracing impls:
8992
mod tracing;
9093
// symbol resolvers:
9194
mod printing;
95+
96+
pub fn get_executable_filename() -> io::Result<(Vec<i8>, fs::File)> {
97+
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
98+
}

src/libstd/sys/windows/backtrace.rs

+48
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@ use io;
3030
use libc::c_void;
3131
use mem;
3232
use ptr;
33+
use path::PathBuf;
34+
use fs::{OpenOptions, File};
35+
use sys::ext::fs::OpenOptionsExt;
3336
use sys::c;
3437
use sys::dynamic_lib::DynamicLibrary;
3538
use sys::mutex::Mutex;
39+
use sys::handle::Handle;
3640

3741
macro_rules! sym {
3842
($lib:expr, $e:expr, $t:ident) => (
@@ -157,3 +161,47 @@ unsafe fn _write(w: &mut Write) -> io::Result<()> {
157161

158162
Ok(())
159163
}
164+
165+
fn query_full_process_image_name() -> io::Result<PathBuf> {
166+
unsafe {
167+
let process_handle = Handle::new(c::OpenProcess(c::PROCESS_QUERY_INFORMATION,
168+
c::FALSE,
169+
c::GetCurrentProcessId()));
170+
super::fill_utf16_buf(|buf, mut sz| {
171+
if c::QueryFullProcessImageNameW(process_handle.raw(), 0, buf, &mut sz) == 0 {
172+
0
173+
} else {
174+
sz
175+
}
176+
}, super::os2path)
177+
}
178+
}
179+
180+
fn lock_and_get_executable_filename() -> io::Result<(PathBuf, File)> {
181+
// We query the current image name, open the file without FILE_SHARE_DELETE so it
182+
// can't be moved and then get the current image name again. If the names are the
183+
// same than we have successfully locked the file
184+
let image_name1 = query_full_process_image_name()?;
185+
let file = OpenOptions::new()
186+
.read(true)
187+
.share_mode(c::FILE_SHARE_READ | c::FILE_SHARE_WRITE)
188+
.open(&image_name1)?;
189+
let image_name2 = query_full_process_image_name()?;
190+
191+
if image_name1 != image_name2 {
192+
return Err(io::Error::new(io::ErrorKind::Other,
193+
"executable moved while trying to lock it"));
194+
}
195+
196+
Ok((image_name1, file))
197+
}
198+
199+
// Get the executable filename for libbacktrace
200+
// This returns the path in the ANSI code page and a File which should remain open
201+
// for as long as the path should remain valid
202+
pub fn get_executable_filename() -> io::Result<(Vec<i8>, File)> {
203+
let (executable, file) = lock_and_get_executable_filename()?;
204+
let u16_executable = super::to_u16s(executable.into_os_string())?;
205+
Ok((super::wide_char_to_multi_byte(c::CP_ACP, c::WC_NO_BEST_FIT_CHARS,
206+
&u16_executable, true)?, file))
207+
}

src/libstd/sys/windows/c.rs

+23
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub type LPWCH = *mut WCHAR;
6969
pub type LPWIN32_FIND_DATAW = *mut WIN32_FIND_DATAW;
7070
pub type LPWSADATA = *mut WSADATA;
7171
pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO;
72+
pub type LPSTR = *mut CHAR;
7273
pub type LPWSTR = *mut WCHAR;
7374
pub type LPFILETIME = *mut FILETIME;
7475

@@ -157,6 +158,7 @@ pub const WSAECONNREFUSED: c_int = 10061;
157158

158159
pub const MAX_PROTOCOL_CHAIN: DWORD = 7;
159160

161+
pub const PROCESS_QUERY_INFORMATION: DWORD = 0x0400;
160162
pub const TOKEN_READ: DWORD = 0x20008;
161163
pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024;
162164
pub const FSCTL_GET_REPARSE_POINT: DWORD = 0x900a8;
@@ -218,6 +220,10 @@ pub const CREATE_NEW_PROCESS_GROUP: DWORD = 0x00000200;
218220
pub const CREATE_UNICODE_ENVIRONMENT: DWORD = 0x00000400;
219221
pub const STARTF_USESTDHANDLES: DWORD = 0x00000100;
220222

223+
pub const CP_ACP: UINT = 0;
224+
225+
pub const WC_NO_BEST_FIT_CHARS: DWORD = 0x00000400;
226+
221227
pub const AF_INET: c_int = 2;
222228
pub const AF_INET6: c_int = 23;
223229
pub const SD_BOTH: c_int = 2;
@@ -888,6 +894,9 @@ extern "system" {
888894
pNumArgs: *mut c_int) -> *mut *mut u16;
889895
pub fn GetTempPathW(nBufferLength: DWORD,
890896
lpBuffer: LPCWSTR) -> DWORD;
897+
pub fn OpenProcess(dwDesiredAccess: DWORD,
898+
bInheritHandle: BOOL,
899+
dwProcessId: DWORD) -> HANDLE;
891900
pub fn OpenProcessToken(ProcessHandle: HANDLE,
892901
DesiredAccess: DWORD,
893902
TokenHandle: *mut HANDLE) -> BOOL;
@@ -973,6 +982,14 @@ extern "system" {
973982
pub fn DeleteFileW(lpPathName: LPCWSTR) -> BOOL;
974983
pub fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: LPWSTR) -> DWORD;
975984
pub fn SetCurrentDirectoryW(lpPathName: LPCWSTR) -> BOOL;
985+
pub fn WideCharToMultiByte(CodePage: UINT,
986+
dwFlags: DWORD,
987+
lpWideCharStr: LPCWSTR,
988+
cchWideChar: c_int,
989+
lpMultiByteStr: LPSTR,
990+
cbMultiByte: c_int,
991+
lpDefaultChar: LPCSTR,
992+
lpUsedDefaultChar: LPBOOL) -> c_int;
976993

977994
pub fn closesocket(socket: SOCKET) -> c_int;
978995
pub fn recv(socket: SOCKET, buf: *mut c_void, len: c_int,
@@ -1136,6 +1153,12 @@ compat_fn! {
11361153
_dwFlags: DWORD) -> DWORD {
11371154
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
11381155
}
1156+
pub fn QueryFullProcessImageNameW(_hProcess: HANDLE,
1157+
_dwFlags: DWORD,
1158+
_lpExeName: LPWSTR,
1159+
_lpdwSize: LPDWORD) -> BOOL {
1160+
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
1161+
}
11391162
pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL {
11401163
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
11411164
}

src/libstd/sys/windows/mod.rs

+46
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#![allow(missing_docs, bad_style)]
1212

13+
use ptr;
1314
use ffi::{OsStr, OsString};
1415
use io::{self, ErrorKind};
1516
use os::windows::ffi::{OsStrExt, OsStringExt};
@@ -171,6 +172,51 @@ fn os2path(s: &[u16]) -> PathBuf {
171172
PathBuf::from(OsString::from_wide(s))
172173
}
173174

175+
fn wide_char_to_multi_byte(code_page: u32,
176+
flags: u32,
177+
s: &[u16],
178+
no_default_char: bool)
179+
-> io::Result<Vec<i8>> {
180+
unsafe {
181+
let mut size = c::WideCharToMultiByte(code_page,
182+
flags,
183+
s.as_ptr(),
184+
s.len() as i32,
185+
ptr::null_mut(),
186+
0,
187+
ptr::null(),
188+
ptr::null_mut());
189+
if size == 0 {
190+
return Err(io::Error::last_os_error());
191+
}
192+
193+
let mut buf = Vec::with_capacity(size as usize);
194+
buf.set_len(size as usize);
195+
196+
let mut used_default_char = c::FALSE;
197+
size = c::WideCharToMultiByte(code_page,
198+
flags,
199+
s.as_ptr(),
200+
s.len() as i32,
201+
buf.as_mut_ptr(),
202+
buf.len() as i32,
203+
ptr::null(),
204+
if no_default_char { &mut used_default_char }
205+
else { ptr::null_mut() });
206+
if size == 0 {
207+
return Err(io::Error::last_os_error());
208+
}
209+
if no_default_char && used_default_char == c::TRUE {
210+
return Err(io::Error::new(io::ErrorKind::InvalidData,
211+
"string cannot be converted to requested code page"));
212+
}
213+
214+
buf.set_len(size as usize);
215+
216+
Ok(buf)
217+
}
218+
}
219+
174220
pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
175221
match v.iter().position(|c| *c == 0) {
176222
// don't include the 0

src/libstd/sys_common/gnu/libbacktrace.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use sys_common::backtrace::{output, output_fileline};
1616
pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
1717
symaddr: *mut libc::c_void) -> io::Result<()> {
1818
use ffi::CStr;
19+
use mem;
1920
use ptr;
2021

2122
////////////////////////////////////////////////////////////////////////
@@ -124,7 +125,21 @@ pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
124125
unsafe fn init_state() -> *mut backtrace_state {
125126
static mut STATE: *mut backtrace_state = ptr::null_mut();
126127
if !STATE.is_null() { return STATE }
127-
STATE = backtrace_create_state(ptr::null(), 0, error_cb,
128+
129+
let filename = match ::sys::backtrace::get_executable_filename() {
130+
Ok((filename, file)) => {
131+
// filename is purposely leaked here since libbacktrace requires
132+
// it to stay allocated permanently, file is also leaked so that
133+
// the file stays locked
134+
let filename_ptr = filename.as_ptr();
135+
mem::forget(filename);
136+
mem::forget(file);
137+
filename_ptr
138+
},
139+
Err(_) => ptr::null(),
140+
};
141+
142+
STATE = backtrace_create_state(filename, 0, error_cb,
128143
ptr::null_mut());
129144
STATE
130145
}

src/test/run-pass/backtrace-debuginfo.rs

-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ macro_rules! dump_and_die {
3737
target_os = "ios",
3838
target_os = "android",
3939
all(target_os = "linux", target_arch = "arm"),
40-
target_os = "windows",
4140
target_os = "freebsd",
4241
target_os = "dragonfly",
4342
target_os = "bitrig",
@@ -173,4 +172,3 @@ fn main() {
173172
run_test(&args[0]);
174173
}
175174
}
176-

src/test/run-pass/backtrace.rs

-4
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,6 @@ fn runtest(me: &str) {
104104
}
105105

106106
fn main() {
107-
if cfg!(windows) && cfg!(target_env = "gnu") {
108-
return
109-
}
110-
111107
let args: Vec<String> = env::args().collect();
112108
if args.len() >= 2 && args[1] == "fail" {
113109
foo();

0 commit comments

Comments
 (0)