Skip to content

Commit 1a13857

Browse files
committed
Auto merge of #2752 - RalfJung:win-env-current-exe, r=RalfJung
make env::current_exe work on Windows
2 parents 218d7b7 + 5eb524e commit 1a13857

File tree

4 files changed

+85
-13
lines changed

4 files changed

+85
-13
lines changed

src/shims/env.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
166166
// `buf_size` represents the size in characters.
167167
let buf_size = u64::from(this.read_scalar(size_op)?.to_u32()?);
168168
Scalar::from_u32(windows_check_buffer_size(
169-
this.write_os_str_to_wide_str(&var, buf_ptr, buf_size)?,
169+
this.write_os_str_to_wide_str(
170+
&var, buf_ptr, buf_size, /*truncate*/ false,
171+
)?,
170172
))
171173
}
172174
None => {
@@ -366,7 +368,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
366368
match env::current_dir() {
367369
Ok(cwd) =>
368370
return Ok(Scalar::from_u32(windows_check_buffer_size(
369-
this.write_path_to_wide_str(&cwd, buf, size)?,
371+
this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?,
370372
))),
371373
Err(e) => this.set_last_error_from_io_error(e.kind())?,
372374
}

src/shims/os_str.rs

+41-10
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
101101
self.eval_context_mut().write_c_str(bytes, ptr, size)
102102
}
103103

104-
/// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what
105-
/// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
106-
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
107-
/// terminator. It returns `Ok((true, length))` if the writing process was successful. The
108-
/// string length returned does include the null terminator. Length is measured in units of
109-
/// `u16.`
104+
/// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what the
105+
/// Windows APIs usually handle.
106+
///
107+
/// If `truncate == false` (the usual mode of operation), this function returns `Ok((false,
108+
/// length))` without trying to write if `size` is not large enough to fit the contents of
109+
/// `os_string` plus a null terminator. It returns `Ok((true, length))` if the writing process
110+
/// was successful. The string length returned does include the null terminator. Length is
111+
/// measured in units of `u16.`
112+
///
113+
/// If `truncate == true`, then in case `size` is not large enough it *will* write the first
114+
/// `size.saturating_sub(1)` many items, followed by a null terminator (if `size > 0`).
110115
fn write_os_str_to_wide_str(
111116
&mut self,
112117
os_str: &OsStr,
113118
ptr: Pointer<Option<Provenance>>,
114119
size: u64,
120+
truncate: bool,
115121
) -> InterpResult<'tcx, (bool, u64)> {
116122
#[cfg(windows)]
117123
fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
@@ -129,7 +135,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
129135
}
130136

131137
let u16_vec = os_str_to_u16vec(os_str)?;
132-
self.eval_context_mut().write_wide_str(&u16_vec, ptr, size)
138+
let (written, size_needed) = self.eval_context_mut().write_wide_str(&u16_vec, ptr, size)?;
139+
if truncate && !written && size > 0 {
140+
// Write the truncated part that fits.
141+
let truncated_data = &u16_vec[..size.saturating_sub(1).try_into().unwrap()];
142+
let (written, written_len) =
143+
self.eval_context_mut().write_wide_str(truncated_data, ptr, size)?;
144+
assert!(written && written_len == size);
145+
}
146+
Ok((written, size_needed))
133147
}
134148

135149
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
@@ -143,7 +157,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
143157

144158
let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
145159
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
146-
assert!(self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap().0);
160+
let (written, _) = self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap();
161+
assert!(written);
147162
Ok(arg_place.ptr)
148163
}
149164

@@ -158,7 +173,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
158173

159174
let arg_type = this.tcx.mk_array(this.tcx.types.u16, size);
160175
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
161-
assert!(self.write_os_str_to_wide_str(os_str, arg_place.ptr, size).unwrap().0);
176+
let (written, _) =
177+
self.write_os_str_to_wide_str(os_str, arg_place.ptr, size, /*truncate*/ false).unwrap();
178+
assert!(written);
162179
Ok(arg_place.ptr)
163180
}
164181

@@ -212,11 +229,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
212229
path: &Path,
213230
ptr: Pointer<Option<Provenance>>,
214231
size: u64,
232+
truncate: bool,
215233
) -> InterpResult<'tcx, (bool, u64)> {
216234
let this = self.eval_context_mut();
217235
let os_str =
218236
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
219-
this.write_os_str_to_wide_str(&os_str, ptr, size)
237+
this.write_os_str_to_wide_str(&os_str, ptr, size, truncate)
220238
}
221239

222240
/// Allocate enough memory to store a Path as a null-terminated sequence of bytes,
@@ -232,6 +250,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
232250
this.alloc_os_str_as_c_str(&os_str, memkind)
233251
}
234252

253+
/// Allocate enough memory to store a Path as a null-terminated sequence of `u16`s,
254+
/// adjusting path separators if needed.
255+
fn alloc_path_as_wide_str(
256+
&mut self,
257+
path: &Path,
258+
memkind: MemoryKind<MiriMemoryKind>,
259+
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
260+
let this = self.eval_context_mut();
261+
let os_str =
262+
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
263+
this.alloc_os_str_as_wide_str(&os_str, memkind)
264+
}
265+
235266
#[allow(clippy::get_first)]
236267
fn convert_path<'a>(
237268
&self,

src/shims/windows/foreign_items.rs

+40
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
381381

382382
this.write_scalar(Scalar::from_u32(1), dest)?;
383383
}
384+
"GetModuleFileNameW" => {
385+
let [handle, filename, size] =
386+
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
387+
this.check_no_isolation("`GetModuleFileNameW`")?;
388+
389+
let handle = this.read_machine_usize(handle)?;
390+
let filename = this.read_pointer(filename)?;
391+
let size = this.read_scalar(size)?.to_u32()?;
392+
393+
if handle != 0 {
394+
throw_unsup_format!("`GetModuleFileNameW` only supports the NULL handle");
395+
}
396+
397+
// Using the host current_exe is a bit off, but consistent with Linux
398+
// (where stdlib reads /proc/self/exe).
399+
// Unfortunately this Windows function has a crazy behavior so we can't just use
400+
// `write_path_to_wide_str`...
401+
let path = std::env::current_exe().unwrap();
402+
let (all_written, size_needed) = this.write_path_to_wide_str(
403+
&path,
404+
filename,
405+
size.into(),
406+
/*truncate*/ true,
407+
)?;
408+
409+
if all_written {
410+
// If the function succeeds, the return value is the length of the string that
411+
// is copied to the buffer, in characters, not including the terminating null
412+
// character.
413+
this.write_int(size_needed.checked_sub(1).unwrap(), dest)?;
414+
} else {
415+
// If the buffer is too small to hold the module name, the string is truncated
416+
// to nSize characters including the terminating null character, the function
417+
// returns nSize, and the function sets the last error to
418+
// ERROR_INSUFFICIENT_BUFFER.
419+
this.write_int(size, dest)?;
420+
let insufficient_buffer = this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER");
421+
this.set_last_error(insufficient_buffer)?;
422+
}
423+
}
384424

385425
// Threading
386426
"CreateThread" => {

tests/pass/shims/env/current_exe.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
//@ignore-target-windows: current_exe not supported on Windows
21
//@only-on-host: the Linux std implementation opens /proc/self/exe, which doesn't work cross-target
32
//@compile-flags: -Zmiri-disable-isolation
43
use std::env;

0 commit comments

Comments
 (0)