Skip to content

Commit 65978ed

Browse files
committed
Auto merge of rust-lang#3464 - RalfJung:windows-err, r=RalfJung
Windows: add basic support for FormatMessageW
2 parents c3136b2 + fb779ee commit 65978ed

File tree

5 files changed

+79
-19
lines changed

5 files changed

+79
-19
lines changed

src/tools/miri/src/helpers.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
7878
("EAGAIN", WouldBlock),
7979
]
8080
};
81+
// This mapping should match `decode_error_kind` in
82+
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/mod.rs>.
83+
const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
84+
use std::io::ErrorKind::*;
85+
// FIXME: this is still incomplete.
86+
&[
87+
("ERROR_ACCESS_DENIED", PermissionDenied),
88+
("ERROR_FILE_NOT_FOUND", NotFound),
89+
("ERROR_INVALID_PARAMETER", InvalidInput),
90+
]
91+
};
8192

8293
/// Gets an instance for a path.
8394
///
@@ -712,20 +723,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
712723
}
713724
throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind)
714725
} else if target.families.iter().any(|f| f == "windows") {
715-
// FIXME: we have to finish implementing the Windows equivalent of this.
716-
use std::io::ErrorKind::*;
717-
Ok(this.eval_windows(
718-
"c",
719-
match err_kind {
720-
NotFound => "ERROR_FILE_NOT_FOUND",
721-
PermissionDenied => "ERROR_ACCESS_DENIED",
722-
_ =>
723-
throw_unsup_format!(
724-
"io error {:?} cannot be translated into a raw os error",
725-
err_kind
726-
),
727-
},
728-
))
726+
for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
727+
if err_kind == kind {
728+
return Ok(this.eval_windows("c", name));
729+
}
730+
}
731+
throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind);
729732
} else {
730733
throw_unsup_format!(
731734
"converting io::Error into errnum is unsupported for OS {}",
@@ -749,8 +752,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
749752
return Ok(Some(kind));
750753
}
751754
}
752-
// Our table is as complete as the mapping in std, so we are okay with saying "that's a
753-
// strange one" here.
755+
return Ok(None);
756+
} else if target.families.iter().any(|f| f == "windows") {
757+
let errnum = errnum.to_u32()?;
758+
for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
759+
if errnum == this.eval_windows("c", name).to_u32()? {
760+
return Ok(Some(kind));
761+
}
762+
}
754763
return Ok(None);
755764
} else {
756765
throw_unsup_format!(

src/tools/miri/src/shims/os_str.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
9898
///
9999
/// If `truncate == true`, then in case `size` is not large enough it *will* write the first
100100
/// `size.saturating_sub(1)` many items, followed by a null terminator (if `size > 0`).
101+
/// The return value is still `(false, length)` in that case.
101102
fn write_os_str_to_wide_str(
102103
&mut self,
103104
os_str: &OsStr,

src/tools/miri/src/shims/windows/foreign_items.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::ffi::OsStr;
12
use std::iter;
23
use std::str;
34

@@ -533,6 +534,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
533534
this.set_last_error(insufficient_buffer)?;
534535
}
535536
}
537+
"FormatMessageW" => {
538+
let [flags, module, message_id, language_id, buffer, size, arguments] =
539+
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
540+
541+
let flags = this.read_scalar(flags)?.to_u32()?;
542+
let _module = this.read_pointer(module)?; // seems to contain a module name
543+
let message_id = this.read_scalar(message_id)?;
544+
let _language_id = this.read_scalar(language_id)?.to_u32()?;
545+
let buffer = this.read_pointer(buffer)?;
546+
let size = this.read_scalar(size)?.to_u32()?;
547+
let _arguments = this.read_pointer(arguments)?;
548+
549+
// We only support `FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS`
550+
// This also means `arguments` can be ignored.
551+
if flags != 4096u32 | 512u32 {
552+
throw_unsup_format!("FormatMessageW: unsupported flags {flags:#x}");
553+
}
554+
555+
let error = this.try_errnum_to_io_error(message_id)?;
556+
let formatted = match error {
557+
Some(err) => format!("{err}"),
558+
None => format!("<unknown error in FormatMessageW: {message_id}>"),
559+
};
560+
let (complete, length) = this.write_os_str_to_wide_str(
561+
OsStr::new(&formatted),
562+
buffer,
563+
size.into(),
564+
/*trunacte*/ false,
565+
)?;
566+
if !complete {
567+
// The API docs don't say what happens when the buffer is not big enough...
568+
// Let's just bail.
569+
throw_unsup_format!("FormatMessageW: buffer not big enough");
570+
}
571+
// The return value is the number of characters stored *excluding* the null terminator.
572+
this.write_int(length.checked_sub(1).unwrap(), dest)?;
573+
}
536574

537575
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
538576
// These shims are enabled only when the caller is in the standard library.

src/tools/miri/tests/pass/shims/fs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ fn test_errors() {
260260
// Opening a non-existing file should fail with a "not found" error.
261261
assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
262262
// Make sure we can also format this.
263-
format!("{0:?}: {0}", File::open(&path).unwrap_err());
263+
format!("{0}: {0:?}", File::open(&path).unwrap_err());
264264
// Removing a non-existing file should fail with a "not found" error.
265265
assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
266266
// Reading the metadata of a non-existing file should fail with a "not found" error.

src/tools/miri/tests/pass/shims/io.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1-
use std::io::IsTerminal;
1+
use std::io::{self, IsTerminal};
22

33
fn main() {
44
// We can't really assume that this is truly a terminal, and anyway on Windows Miri will always
55
// return `false` here, but we can check that the call succeeds.
6-
std::io::stdout().is_terminal();
6+
io::stdout().is_terminal();
7+
8+
// Ensure we can format `io::Error` created from OS errors
9+
// (calls OS-specific error formatting functions).
10+
let raw_os_error = if cfg!(unix) {
11+
22 // EINVAL (on most Unixes, anyway)
12+
} else if cfg!(windows) {
13+
87 // ERROR_INVALID_PARAMETER
14+
} else {
15+
panic!("unsupported OS")
16+
};
17+
let err = io::Error::from_raw_os_error(raw_os_error);
18+
format!("{err}: {err:?}");
719
}

0 commit comments

Comments
 (0)