Skip to content

Commit 35aeba7

Browse files
committed
Auto merge of #2067 - RalfJung:strerror_r, r=RalfJung
implement strerror_r This isn't perfect; we end up using [this match](https://github.com/rust-lang/rust/blob/72a25d05bf1a4b155d74139ef700ff93af6d8e22/library/std/src/io/error.rs#L380) rather than the platform-specific messages, but at least we show something -- this is mostly informational anyway. Cc #2057
2 parents d1f31f9 + db2c4b6 commit 35aeba7

File tree

3 files changed

+77
-34
lines changed

3 files changed

+77
-34
lines changed

src/helpers.rs

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,27 @@ use crate::*;
2121

2222
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
2323

24+
const UNIX_IO_ERROR_TABLE: &[(std::io::ErrorKind, &str)] = {
25+
use std::io::ErrorKind::*;
26+
&[
27+
(ConnectionRefused, "ECONNREFUSED"),
28+
(ConnectionReset, "ECONNRESET"),
29+
(PermissionDenied, "EPERM"),
30+
(BrokenPipe, "EPIPE"),
31+
(NotConnected, "ENOTCONN"),
32+
(ConnectionAborted, "ECONNABORTED"),
33+
(AddrNotAvailable, "EADDRNOTAVAIL"),
34+
(AddrInUse, "EADDRINUSE"),
35+
(NotFound, "ENOENT"),
36+
(Interrupted, "EINTR"),
37+
(InvalidInput, "EINVAL"),
38+
(TimedOut, "ETIMEDOUT"),
39+
(AlreadyExists, "EEXIST"),
40+
(WouldBlock, "EWOULDBLOCK"),
41+
(DirectoryNotEmpty, "ENOTEMPTY"),
42+
]
43+
};
44+
2445
/// Gets an instance for a path.
2546
fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option<DefId> {
2647
tcx.crates(()).iter().find(|&&krate| tcx.crate_name(krate).as_str() == path[0]).and_then(
@@ -502,39 +523,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
502523
this.read_scalar(&errno_place.into())?.check_init()
503524
}
504525

505-
/// Sets the last OS error using a `std::io::ErrorKind`. This function tries to produce the most
506-
/// similar OS error from the `std::io::ErrorKind` and sets it as the last OS error.
507-
fn set_last_error_from_io_error(&mut self, err_kind: std::io::ErrorKind) -> InterpResult<'tcx> {
508-
use std::io::ErrorKind::*;
509-
let this = self.eval_context_mut();
526+
/// This function tries to produce the most similar OS error from the `std::io::ErrorKind`
527+
/// as a platform-specific errnum.
528+
fn io_error_to_errnum(&self, err_kind: std::io::ErrorKind) -> InterpResult<'tcx, Scalar<Tag>> {
529+
let this = self.eval_context_ref();
510530
let target = &this.tcx.sess.target;
511-
let target_os = &target.os;
512-
let last_error = if target.families.iter().any(|f| f == "unix") {
513-
this.eval_libc(match err_kind {
514-
ConnectionRefused => "ECONNREFUSED",
515-
ConnectionReset => "ECONNRESET",
516-
PermissionDenied => "EPERM",
517-
BrokenPipe => "EPIPE",
518-
NotConnected => "ENOTCONN",
519-
ConnectionAborted => "ECONNABORTED",
520-
AddrNotAvailable => "EADDRNOTAVAIL",
521-
AddrInUse => "EADDRINUSE",
522-
NotFound => "ENOENT",
523-
Interrupted => "EINTR",
524-
InvalidInput => "EINVAL",
525-
TimedOut => "ETIMEDOUT",
526-
AlreadyExists => "EEXIST",
527-
WouldBlock => "EWOULDBLOCK",
528-
DirectoryNotEmpty => "ENOTEMPTY",
529-
_ => {
530-
throw_unsup_format!(
531-
"io error {:?} cannot be translated into a raw os error",
532-
err_kind
533-
)
531+
if target.families.iter().any(|f| f == "unix") {
532+
for &(kind, name) in UNIX_IO_ERROR_TABLE {
533+
if err_kind == kind {
534+
return this.eval_libc(name);
534535
}
535-
})?
536+
}
537+
throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind)
536538
} else if target.families.iter().any(|f| f == "windows") {
537539
// FIXME: we have to finish implementing the Windows equivalent of this.
540+
use std::io::ErrorKind::*;
538541
this.eval_windows(
539542
"c",
540543
match err_kind {
@@ -546,14 +549,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
546549
err_kind
547550
),
548551
},
549-
)?
552+
)
550553
} else {
551554
throw_unsup_format!(
552-
"setting the last OS error from an io::Error is unsupported for {}.",
553-
target_os
555+
"converting io::Error into errnum is unsupported for OS {}",
556+
target.os
554557
)
555-
};
556-
this.set_last_error(last_error)
558+
}
559+
}
560+
561+
/// The inverse of `io_error_to_errnum`.
562+
fn errnum_to_io_error(&self, errnum: Scalar<Tag>) -> InterpResult<'tcx, std::io::ErrorKind> {
563+
let this = self.eval_context_ref();
564+
let target = &this.tcx.sess.target;
565+
if target.families.iter().any(|f| f == "unix") {
566+
let errnum = errnum.to_i32()?;
567+
for &(kind, name) in UNIX_IO_ERROR_TABLE {
568+
if errnum == this.eval_libc_i32(name)? {
569+
return Ok(kind);
570+
}
571+
}
572+
throw_unsup_format!("raw errnum {:?} cannot be translated into io::Error", errnum)
573+
} else {
574+
throw_unsup_format!(
575+
"converting errnum into io::Error is unsupported for OS {}",
576+
target.os
577+
)
578+
}
579+
}
580+
581+
/// Sets the last OS error using a `std::io::ErrorKind`.
582+
fn set_last_error_from_io_error(&mut self, err_kind: std::io::ErrorKind) -> InterpResult<'tcx> {
583+
self.set_last_error(self.io_error_to_errnum(err_kind)?)
557584
}
558585

559586
/// Helper function that consumes an `std::io::Result<T>` and returns an

src/shims/posix/foreign_items.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::ffi::OsStr;
2+
13
use log::trace;
24

35
use rustc_middle::mir;
@@ -421,6 +423,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
421423
// We do not support forking, so there is nothing to do here.
422424
this.write_null(dest)?;
423425
}
426+
"strerror_r" | "__xpg_strerror_r" => {
427+
let &[ref errnum, ref buf, ref buflen] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
428+
let errnum = this.read_scalar(errnum)?.check_init()?;
429+
let buf = this.read_pointer(buf)?;
430+
let buflen = this.read_scalar(buflen)?.to_machine_usize(this)?;
431+
432+
let error = this.errnum_to_io_error(errnum)?;
433+
let formatted = error.to_string();
434+
let (complete, _) = this.write_os_str_to_c_str(OsStr::new(&formatted), buf, buflen)?;
435+
let ret = if complete { 0 } else { this.eval_libc_i32("ERANGE")? };
436+
this.write_int(ret, dest)?;
437+
}
424438

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

tests/run-pass/fs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ fn test_errors() {
335335
// The following tests also check that the `__errno_location()` shim is working properly.
336336
// Opening a non-existing file should fail with a "not found" error.
337337
assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
338+
// Make sure we can also format this.
339+
format!("{0:?}: {0}", File::open(&path).unwrap_err());
338340
// Removing a non-existing file should fail with a "not found" error.
339341
assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
340342
// Reading the metadata of a non-existing file should fail with a "not found" error.

0 commit comments

Comments
 (0)