make env::current_exe work on Windows
This commit is contained in:
parent
0f3403959b
commit
5977a1626d
@ -166,7 +166,9 @@ fn GetEnvironmentVariableW(
|
|||||||
// `buf_size` represents the size in characters.
|
// `buf_size` represents the size in characters.
|
||||||
let buf_size = u64::from(this.read_scalar(size_op)?.to_u32()?);
|
let buf_size = u64::from(this.read_scalar(size_op)?.to_u32()?);
|
||||||
Scalar::from_u32(windows_check_buffer_size(
|
Scalar::from_u32(windows_check_buffer_size(
|
||||||
this.write_os_str_to_wide_str(&var, buf_ptr, buf_size)?,
|
this.write_os_str_to_wide_str(
|
||||||
|
&var, buf_ptr, buf_size, /*truncate*/ false,
|
||||||
|
)?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -366,7 +368,7 @@ fn GetCurrentDirectoryW(
|
|||||||
match env::current_dir() {
|
match env::current_dir() {
|
||||||
Ok(cwd) =>
|
Ok(cwd) =>
|
||||||
return Ok(Scalar::from_u32(windows_check_buffer_size(
|
return Ok(Scalar::from_u32(windows_check_buffer_size(
|
||||||
this.write_path_to_wide_str(&cwd, buf, size)?,
|
this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?,
|
||||||
))),
|
))),
|
||||||
Err(e) => this.set_last_error_from_io_error(e.kind())?,
|
Err(e) => this.set_last_error_from_io_error(e.kind())?,
|
||||||
}
|
}
|
||||||
|
@ -101,17 +101,23 @@ fn write_os_str_to_c_str(
|
|||||||
self.eval_context_mut().write_c_str(bytes, ptr, size)
|
self.eval_context_mut().write_c_str(bytes, ptr, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what
|
/// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what the
|
||||||
/// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
|
/// Windows APIs usually handle.
|
||||||
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
|
///
|
||||||
/// terminator. It returns `Ok((true, length))` if the writing process was successful. The
|
/// If `truncate == false` (the usual mode of operation), this function returns `Ok((false,
|
||||||
/// string length returned does include the null terminator. Length is measured in units of
|
/// length))` without trying to write if `size` is not large enough to fit the contents of
|
||||||
/// `u16.`
|
/// `os_string` plus a null terminator. It returns `Ok((true, length))` if the writing process
|
||||||
|
/// was successful. The string length returned does include the null terminator. Length is
|
||||||
|
/// measured in units of `u16.`
|
||||||
|
///
|
||||||
|
/// If `truncate == true`, then in case `size` is not large enough it *will* write the first
|
||||||
|
/// `size.saturating_sub(1)` many items, followed by a null terminator (if `size > 0`).
|
||||||
fn write_os_str_to_wide_str(
|
fn write_os_str_to_wide_str(
|
||||||
&mut self,
|
&mut self,
|
||||||
os_str: &OsStr,
|
os_str: &OsStr,
|
||||||
ptr: Pointer<Option<Provenance>>,
|
ptr: Pointer<Option<Provenance>>,
|
||||||
size: u64,
|
size: u64,
|
||||||
|
truncate: bool,
|
||||||
) -> InterpResult<'tcx, (bool, u64)> {
|
) -> InterpResult<'tcx, (bool, u64)> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
|
fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
|
||||||
@ -129,7 +135,15 @@ fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let u16_vec = os_str_to_u16vec(os_str)?;
|
let u16_vec = os_str_to_u16vec(os_str)?;
|
||||||
self.eval_context_mut().write_wide_str(&u16_vec, ptr, size)
|
let (written, size_needed) = self.eval_context_mut().write_wide_str(&u16_vec, ptr, size)?;
|
||||||
|
if truncate && !written && size > 0 {
|
||||||
|
// Write the truncated part that fits.
|
||||||
|
let truncated_data = &u16_vec[..size.saturating_sub(1).try_into().unwrap()];
|
||||||
|
let (written, written_len) =
|
||||||
|
self.eval_context_mut().write_wide_str(truncated_data, ptr, size)?;
|
||||||
|
assert!(written && written_len == size);
|
||||||
|
}
|
||||||
|
Ok((written, size_needed))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
|
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
|
||||||
@ -143,7 +157,8 @@ fn alloc_os_str_as_c_str(
|
|||||||
|
|
||||||
let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
|
let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
|
||||||
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
|
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
|
||||||
assert!(self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap().0);
|
let (written, _) = self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap();
|
||||||
|
assert!(written);
|
||||||
Ok(arg_place.ptr)
|
Ok(arg_place.ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +173,9 @@ fn alloc_os_str_as_wide_str(
|
|||||||
|
|
||||||
let arg_type = this.tcx.mk_array(this.tcx.types.u16, size);
|
let arg_type = this.tcx.mk_array(this.tcx.types.u16, size);
|
||||||
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
|
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
|
||||||
assert!(self.write_os_str_to_wide_str(os_str, arg_place.ptr, size).unwrap().0);
|
let (written, _) =
|
||||||
|
self.write_os_str_to_wide_str(os_str, arg_place.ptr, size, /*truncate*/ false).unwrap();
|
||||||
|
assert!(written);
|
||||||
Ok(arg_place.ptr)
|
Ok(arg_place.ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,11 +229,12 @@ fn write_path_to_wide_str(
|
|||||||
path: &Path,
|
path: &Path,
|
||||||
ptr: Pointer<Option<Provenance>>,
|
ptr: Pointer<Option<Provenance>>,
|
||||||
size: u64,
|
size: u64,
|
||||||
|
truncate: bool,
|
||||||
) -> InterpResult<'tcx, (bool, u64)> {
|
) -> InterpResult<'tcx, (bool, u64)> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
let os_str =
|
let os_str =
|
||||||
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
|
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
|
||||||
this.write_os_str_to_wide_str(&os_str, ptr, size)
|
this.write_os_str_to_wide_str(&os_str, ptr, size, truncate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate enough memory to store a Path as a null-terminated sequence of bytes,
|
/// Allocate enough memory to store a Path as a null-terminated sequence of bytes,
|
||||||
@ -232,6 +250,19 @@ fn alloc_path_as_c_str(
|
|||||||
this.alloc_os_str_as_c_str(&os_str, memkind)
|
this.alloc_os_str_as_c_str(&os_str, memkind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocate enough memory to store a Path as a null-terminated sequence of `u16`s,
|
||||||
|
/// adjusting path separators if needed.
|
||||||
|
fn alloc_path_as_wide_str(
|
||||||
|
&mut self,
|
||||||
|
path: &Path,
|
||||||
|
memkind: MemoryKind<MiriMemoryKind>,
|
||||||
|
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
|
||||||
|
let this = self.eval_context_mut();
|
||||||
|
let os_str =
|
||||||
|
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
|
||||||
|
this.alloc_os_str_as_wide_str(&os_str, memkind)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::get_first)]
|
#[allow(clippy::get_first)]
|
||||||
fn convert_path<'a>(
|
fn convert_path<'a>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -381,6 +381,46 @@ fn emulate_foreign_item_by_name(
|
|||||||
|
|
||||||
this.write_scalar(Scalar::from_u32(1), dest)?;
|
this.write_scalar(Scalar::from_u32(1), dest)?;
|
||||||
}
|
}
|
||||||
|
"GetModuleFileNameW" => {
|
||||||
|
let [handle, filename, size] =
|
||||||
|
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
|
||||||
|
this.check_no_isolation("`GetModuleFileNameW`")?;
|
||||||
|
|
||||||
|
let handle = this.read_machine_usize(handle)?;
|
||||||
|
let filename = this.read_pointer(filename)?;
|
||||||
|
let size = this.read_scalar(size)?.to_u32()?;
|
||||||
|
|
||||||
|
if handle != 0 {
|
||||||
|
throw_unsup_format!("`GetModuleFileNameW` only supports the NULL handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using the host current_exe is a bit off, but consistent with Linux
|
||||||
|
// (where stdlib reads /proc/self/exe).
|
||||||
|
// Unfortunately this Windows function has a crazy behavior so we can't just use
|
||||||
|
// `write_path_to_wide_str`...
|
||||||
|
let path = std::env::current_exe().unwrap();
|
||||||
|
let (all_written, size_needed) = this.write_path_to_wide_str(
|
||||||
|
&path,
|
||||||
|
filename,
|
||||||
|
size.into(),
|
||||||
|
/*truncate*/ true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if all_written {
|
||||||
|
// If the function succeeds, the return value is the length of the string that
|
||||||
|
// is copied to the buffer, in characters, not including the terminating null
|
||||||
|
// character.
|
||||||
|
this.write_int(size_needed.checked_sub(1).unwrap(), dest)?;
|
||||||
|
} else {
|
||||||
|
// If the buffer is too small to hold the module name, the string is truncated
|
||||||
|
// to nSize characters including the terminating null character, the function
|
||||||
|
// returns nSize, and the function sets the last error to
|
||||||
|
// ERROR_INSUFFICIENT_BUFFER.
|
||||||
|
this.write_int(size, dest)?;
|
||||||
|
let insufficient_buffer = this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER");
|
||||||
|
this.set_last_error(insufficient_buffer)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Threading
|
// Threading
|
||||||
"CreateThread" => {
|
"CreateThread" => {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//@ignore-target-windows: current_exe not supported on Windows
|
|
||||||
//@only-on-host: the Linux std implementation opens /proc/self/exe, which doesn't work cross-target
|
//@only-on-host: the Linux std implementation opens /proc/self/exe, which doesn't work cross-target
|
||||||
//@compile-flags: -Zmiri-disable-isolation
|
//@compile-flags: -Zmiri-disable-isolation
|
||||||
use std::env;
|
use std::env;
|
||||||
|
Loading…
Reference in New Issue
Block a user