mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 23:34:48 +00:00
Windows: add basic support for FormatMessageW
This commit is contained in:
parent
c3136b2031
commit
fb779ee0ac
@ -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!(
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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:?}");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user