about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-04-15 07:48:42 +0000
committerbors <bors@rust-lang.org>2024-04-15 07:48:42 +0000
commit65978edef27a34481e2a6dc54b5d2c507d15de27 (patch)
tree53bcf872737fc00b45709cc1b5e0fd69cd7b67b8
parentc3136b2031e00b56266d632318cedb31529f4bd2 (diff)
parentfb779ee0ac899a08fc9433510359528a335e4ab9 (diff)
downloadrust-65978edef27a34481e2a6dc54b5d2c507d15de27.tar.gz
rust-65978edef27a34481e2a6dc54b5d2c507d15de27.zip
Auto merge of #3464 - RalfJung:windows-err, r=RalfJung
Windows: add basic support for FormatMessageW
-rw-r--r--src/tools/miri/src/helpers.rs41
-rw-r--r--src/tools/miri/src/shims/os_str.rs1
-rw-r--r--src/tools/miri/src/shims/windows/foreign_items.rs38
-rw-r--r--src/tools/miri/tests/pass/shims/fs.rs2
-rw-r--r--src/tools/miri/tests/pass/shims/io.rs16
5 files changed, 79 insertions, 19 deletions
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 6e320b60eec..84eb5f832dd 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -78,6 +78,17 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
         ("EAGAIN", WouldBlock),
     ]
 };
+// This mapping should match `decode_error_kind` in
+// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/mod.rs>.
+const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
+    use std::io::ErrorKind::*;
+    // FIXME: this is still incomplete.
+    &[
+        ("ERROR_ACCESS_DENIED", PermissionDenied),
+        ("ERROR_FILE_NOT_FOUND", NotFound),
+        ("ERROR_INVALID_PARAMETER", InvalidInput),
+    ]
+};
 
 /// Gets an instance for a path.
 ///
@@ -712,20 +723,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             }
             throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind)
         } else if target.families.iter().any(|f| f == "windows") {
-            // FIXME: we have to finish implementing the Windows equivalent of this.
-            use std::io::ErrorKind::*;
-            Ok(this.eval_windows(
-                "c",
-                match err_kind {
-                    NotFound => "ERROR_FILE_NOT_FOUND",
-                    PermissionDenied => "ERROR_ACCESS_DENIED",
-                    _ =>
-                        throw_unsup_format!(
-                            "io error {:?} cannot be translated into a raw os error",
-                            err_kind
-                        ),
-                },
-            ))
+            for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
+                if err_kind == kind {
+                    return Ok(this.eval_windows("c", name));
+                }
+            }
+            throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind);
         } else {
             throw_unsup_format!(
                 "converting io::Error into errnum is unsupported for OS {}",
@@ -749,8 +752,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     return Ok(Some(kind));
                 }
             }
-            // Our table is as complete as the mapping in std, so we are okay with saying "that's a
-            // strange one" here.
+            return Ok(None);
+        } else if target.families.iter().any(|f| f == "windows") {
+            let errnum = errnum.to_u32()?;
+            for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
+                if errnum == this.eval_windows("c", name).to_u32()? {
+                    return Ok(Some(kind));
+                }
+            }
             return Ok(None);
         } else {
             throw_unsup_format!(
diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs
index 62ce2ee58ae..74e8d1d9ed6 100644
--- a/src/tools/miri/src/shims/os_str.rs
+++ b/src/tools/miri/src/shims/os_str.rs
@@ -98,6 +98,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ///
     /// 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`).
+    /// The return value is still `(false, length)` in that case.
     fn write_os_str_to_wide_str(
         &mut self,
         os_str: &OsStr,
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index 9c6994cec70..2e514b11cb1 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -1,3 +1,4 @@
+use std::ffi::OsStr;
 use std::iter;
 use std::str;
 
@@ -533,6 +534,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     this.set_last_error(insufficient_buffer)?;
                 }
             }
+            "FormatMessageW" => {
+                let [flags, module, message_id, language_id, buffer, size, arguments] =
+                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+
+                let flags = this.read_scalar(flags)?.to_u32()?;
+                let _module = this.read_pointer(module)?; // seems to contain a module name
+                let message_id = this.read_scalar(message_id)?;
+                let _language_id = this.read_scalar(language_id)?.to_u32()?;
+                let buffer = this.read_pointer(buffer)?;
+                let size = this.read_scalar(size)?.to_u32()?;
+                let _arguments = this.read_pointer(arguments)?;
+
+                // We only support `FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS`
+                // This also means `arguments` can be ignored.
+                if flags != 4096u32 | 512u32 {
+                    throw_unsup_format!("FormatMessageW: unsupported flags {flags:#x}");
+                }
+
+                let error = this.try_errnum_to_io_error(message_id)?;
+                let formatted = match error {
+                    Some(err) => format!("{err}"),
+                    None => format!("<unknown error in FormatMessageW: {message_id}>"),
+                };
+                let (complete, length) = this.write_os_str_to_wide_str(
+                    OsStr::new(&formatted),
+                    buffer,
+                    size.into(),
+                    /*trunacte*/ false,
+                )?;
+                if !complete {
+                    // The API docs don't say what happens when the buffer is not big enough...
+                    // Let's just bail.
+                    throw_unsup_format!("FormatMessageW: buffer not big enough");
+                }
+                // The return value is the number of characters stored *excluding* the null terminator.
+                this.write_int(length.checked_sub(1).unwrap(), dest)?;
+            }
 
             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
             // These shims are enabled only when the caller is in the standard library.
diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs
index d10faebac7d..8a500b857bc 100644
--- a/src/tools/miri/tests/pass/shims/fs.rs
+++ b/src/tools/miri/tests/pass/shims/fs.rs
@@ -260,7 +260,7 @@ fn test_errors() {
     // Opening a non-existing file should fail with a "not found" error.
     assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
     // Make sure we can also format this.
-    format!("{0:?}: {0}", File::open(&path).unwrap_err());
+    format!("{0}: {0:?}", File::open(&path).unwrap_err());
     // Removing a non-existing file should fail with a "not found" error.
     assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
     // Reading the metadata of a non-existing file should fail with a "not found" error.
diff --git a/src/tools/miri/tests/pass/shims/io.rs b/src/tools/miri/tests/pass/shims/io.rs
index 295723957a4..d20fc75b793 100644
--- a/src/tools/miri/tests/pass/shims/io.rs
+++ b/src/tools/miri/tests/pass/shims/io.rs
@@ -1,7 +1,19 @@
-use std::io::IsTerminal;
+use std::io::{self, IsTerminal};
 
 fn main() {
     // We can't really assume that this is truly a terminal, and anyway on Windows Miri will always
     // return `false` here, but we can check that the call succeeds.
-    std::io::stdout().is_terminal();
+    io::stdout().is_terminal();
+
+    // Ensure we can format `io::Error` created from OS errors
+    // (calls OS-specific error formatting functions).
+    let raw_os_error = if cfg!(unix) {
+        22 // EINVAL (on most Unixes, anyway)
+    } else if cfg!(windows) {
+        87 // ERROR_INVALID_PARAMETER
+    } else {
+        panic!("unsupported OS")
+    };
+    let err = io::Error::from_raw_os_error(raw_os_error);
+    format!("{err}: {err:?}");
 }