Skip to content

Miri subtree update #124007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3365,9 +3365,9 @@ dependencies = [

[[package]]
name = "rustc-build-sysroot"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26170e1d79ea32f7ccec3188dd13cfc1f18c82764a9cbc1071667c0f865a4ea"
checksum = "a9bf37423495cd3a6a9ef8c75fc4566de0d26de0ab75f36f67a426e58df5768c"
dependencies = [
"anyhow",
"rustc_version",
Expand Down
38 changes: 17 additions & 21 deletions src/tools/miri/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,36 +451,32 @@ Some native rustc `-Z` flags are also very relevant for Miri:
* `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri
enables this per default because it is needed for [Stacked Borrows] and [Tree Borrows].

Moreover, Miri recognizes some environment variables:
Moreover, Miri recognizes some environment variables (unless noted otherwise, these are supported
by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`):

* `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and toolchain setup
should be skipped. If it is set to `no`, they are skipped. This is used to allow automated IDE
actions to avoid the auto ops.
* `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during
Miri executions, also [see "Testing the Miri driver" in `CONTRIBUTING.md`][testing-miri].
* `MIRIFLAGS` (recognized by `cargo miri` and the test suite) defines extra
flags to be passed to Miri.
* `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the
standard library that it will build and use for interpretation. This directory
must point to the `library` subdirectory of a `rust-lang/rust` repository
checkout.
* `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When
using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the
automatically created sysroot. For directly invoking the Miri driver, this variable (or a
`--sysroot` flag) is mandatory. When invoking `cargo miri setup`, this indicates where the sysroot
will be put.
* `MIRI_TEST_TARGET` (recognized by the test suite and the `./miri` script) indicates which target
* `MIRIFLAGS` defines extra flags to be passed to Miri.
* `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the standard library that
it will build and use for interpretation. This directory must point to the `library` subdirectory
of a `rust-lang/rust` repository checkout.
* `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri`, this skips the automatic
setup -- only set this if you do not want to use the automatically created sysroot. When invoking
`cargo miri setup`, this indicates where the sysroot will be put.
* `MIRI_TEST_TARGET` (recognized by `./miri {test,run}`) indicates which target
architecture to test against. `miri` and `cargo miri` accept the `--target` flag for the same
purpose.
* `MIRI_TEST_THREADS` (recognized by the test suite): set the number of threads to use for running tests.
By default the number of cores is used.
* `MIRI_NO_STD` (recognized by `cargo miri`) makes sure that the target's sysroot is built without
libstd. This allows testing and running no_std programs.
(Miri has a heuristic to detect no-std targets based on the target name; this environment variable
is only needed when that heuristic fails.)
* `RUSTC_BLESS` (recognized by the test suite and `cargo-miri-test/run-test.py`): overwrite all
* `MIRI_TEST_THREADS` (recognized by `./miri test`): set the number of threads to use for running tests.
By default, the number of cores is used.
* `MIRI_NO_STD` makes sure that the target's sysroot is built without libstd. This allows testing
and running no_std programs. (Miri has a heuristic to detect no-std targets based on the target
name; this environment variable is only needed when that heuristic fails.)
* `RUSTC_BLESS` (recognized by `./miri test` and `cargo-miri-test/run-test.py`): overwrite all
`stderr` and `stdout` files instead of checking whether the output matches.
* `MIRI_SKIP_UI_CHECKS` (recognized by the test suite): don't check whether the
* `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`): don't check whether the
`stderr` or `stdout` files match the actual output.

The following environment variables are *internal* and must not be used by
Expand Down
4 changes: 2 additions & 2 deletions src/tools/miri/cargo-miri/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ dependencies = [

[[package]]
name = "rustc-build-sysroot"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26170e1d79ea32f7ccec3188dd13cfc1f18c82764a9cbc1071667c0f865a4ea"
checksum = "a9bf37423495cd3a6a9ef8c75fc4566de0d26de0ab75f36f67a426e58df5768c"
dependencies = [
"anyhow",
"rustc_version",
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/cargo-miri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ directories = "5"
rustc_version = "0.4"
serde_json = "1.0.40"
cargo_metadata = "0.18.0"
rustc-build-sysroot = "0.4.1"
rustc-build-sysroot = "0.4.6"

# Enable some feature flags that dev-dependencies need but dependencies
# do not. This makes `./miri install` after `./miri build` faster.
Expand Down
26 changes: 19 additions & 7 deletions src/tools/miri/cargo-miri/src/phases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
// Forward all further arguments (not consumed by `ArgSplitFlagValue`) to cargo.
cmd.args(args);

// Let it know where the Miri sysroot lives.
cmd.env("MIRI_SYSROOT", miri_sysroot);
// Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation,
// i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
// the two codepaths. (That extra argument is why we prefer this over setting `RUSTC`.)
Expand All @@ -200,10 +198,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
// always applied. However, buggy build scripts (https://github.com/eyre-rs/eyre/issues/84) and
// also cargo (https://github.com/rust-lang/cargo/issues/10885) will invoke `rustc` even when
// `RUSTC_WRAPPER` is set, bypassing the wrapper. To make sure everything is coherent, we want
// that to be the Miri driver, but acting as rustc, on the target level. (Target, rather than
// host, is needed for cross-interpretation situations.) This is not a perfect emulation of real
// rustc (it might be unable to produce binaries since the sysroot is check-only), but it's as
// close as we can get, and it's good enough for autocfg.
// that to be the Miri driver, but acting as rustc, in host mode.
//
// In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc
// or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that
Expand All @@ -212,14 +207,19 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
// bootstrap `rustc` thing in our way! Instead, we have MIRI_HOST_SYSROOT to use for host
// builds.
cmd.env("RUSTC", fs::canonicalize(find_miri()).unwrap());
cmd.env("MIRI_BE_RUSTC", "target"); // we better remember to *unset* this in the other phases!
// In case we get invoked as RUSTC without the wrapper, let's be a host rustc. This makes no
// sense for cross-interpretation situations, but without the wrapper, this will use the host
// sysroot, so asking it to behave like a target build makes even less sense.
cmd.env("MIRI_BE_RUSTC", "host"); // we better remember to *unset* this in the other phases!

// Set rustdoc to us as well, so we can run doctests.
if let Some(orig_rustdoc) = env::var_os("RUSTDOC") {
cmd.env("MIRI_ORIG_RUSTDOC", orig_rustdoc);
}
cmd.env("RUSTDOC", &cargo_miri_path);

// Forward some crucial information to our own re-invocations.
cmd.env("MIRI_SYSROOT", miri_sysroot);
cmd.env("MIRI_LOCAL_CRATES", local_crates(&metadata));
if verbose > 0 {
cmd.env("MIRI_VERBOSE", verbose.to_string()); // This makes the other phases verbose.
Expand Down Expand Up @@ -412,6 +412,12 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
// Arguments are treated very differently depending on whether this crate is
// for interpretation by Miri, or for use by a build script / proc macro.
if target_crate {
if phase != RustcPhase::Setup {
// Set the sysroot -- except during setup, where we don't have an existing sysroot yet
// and where the bootstrap wrapper adds its own `--sysroot` flag so we can't set ours.
cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap());
}

// Forward arguments, but patched.
let emit_flag = "--emit";
// This hack helps bootstrap run standard library tests in Miri. The issue is as follows:
Expand Down Expand Up @@ -574,6 +580,12 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
cmd.env(name, val);
}

if phase != RunnerPhase::Rustdoc {
// Set the sysroot. Not necessary in rustdoc, where we already set the sysroot in
// `phase_rustdoc`. rustdoc will forward that flag when invoking rustc (i.e., us), so the
// flag is present in `info.args`.
cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap());
}
// Forward rustc arguments.
// We need to patch "--extern" filenames because we forced a check-only
// build without cargo knowing about that: replace `.rlib` suffix by
Expand Down
18 changes: 11 additions & 7 deletions src/tools/miri/miri-script/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::env;
use std::ffi::OsString;
use std::io::Write;
use std::ops::Not;
use std::path::PathBuf;
use std::process;
use std::thread;
use std::time;
Expand All @@ -20,10 +21,11 @@ const JOSH_FILTER: &str =
const JOSH_PORT: &str = "42042";

impl MiriEnv {
fn build_miri_sysroot(&mut self, quiet: bool) -> Result<()> {
if self.sh.var("MIRI_SYSROOT").is_ok() {
/// Returns the location of the sysroot.
fn build_miri_sysroot(&mut self, quiet: bool) -> Result<PathBuf> {
if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") {
// Sysroot already set, use that.
return Ok(());
return Ok(miri_sysroot.into());
}
let manifest_path = path!(self.miri_dir / "cargo-miri" / "Cargo.toml");
let Self { toolchain, cargo_extra_flags, .. } = &self;
Expand Down Expand Up @@ -57,8 +59,8 @@ impl MiriEnv {
.with_context(|| "`cargo miri setup` failed")?;
panic!("`cargo miri setup` didn't fail again the 2nd time?");
};
self.sh.set_var("MIRI_SYSROOT", output);
Ok(())
self.sh.set_var("MIRI_SYSROOT", &output);
Ok(output.into())
}
}

Expand Down Expand Up @@ -505,8 +507,10 @@ impl Command {
flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.`
}

// Prepare a sysroot.
e.build_miri_sysroot(/* quiet */ true)?;
// Prepare a sysroot, and add it to the flags.
let miri_sysroot = e.build_miri_sysroot(/* quiet */ true)?;
flags.push("--sysroot".into());
flags.push(miri_sysroot.into());

// Then run the actual command. Also add MIRIFLAGS.
let miri_manifest = path!(e.miri_dir / "Cargo.toml");
Expand Down
19 changes: 0 additions & 19 deletions src/tools/miri/src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,25 +271,6 @@ fn run_compiler(
callbacks: &mut (dyn rustc_driver::Callbacks + Send),
using_internal_features: std::sync::Arc<std::sync::atomic::AtomicBool>,
) -> ! {
if target_crate {
// Miri needs a custom sysroot for target crates.
// If no `--sysroot` is given, the `MIRI_SYSROOT` env var is consulted to find where
// that sysroot lives, and that is passed to rustc.
let sysroot_flag = "--sysroot";
if !args.iter().any(|e| e.starts_with(sysroot_flag)) {
// Using the built-in default here would be plain wrong, so we *require*
// the env var to make sure things make sense.
let miri_sysroot = env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
show_error!(
"Miri was invoked in 'target' mode without `MIRI_SYSROOT` or `--sysroot` being set"
)
});

args.push(sysroot_flag.to_owned());
args.push(miri_sysroot);
}
}

// Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building
// a "host" crate. That may cause procedural macros (and probably build scripts) to
// depend on Miri-only symbols, such as `miri_resolve_frame`:
Expand Down
56 changes: 40 additions & 16 deletions src/tools/miri/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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 {}",
Expand All @@ -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!(
Expand Down Expand Up @@ -1276,3 +1285,18 @@ pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<
_ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
})
}

/// Check whether an operation that writes to a target buffer was successful.
/// Accordingly select return value.
/// Local helper function to be used in Windows shims.
pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
if success {
// If the function succeeds, the return value is the number of characters stored in the target buffer,
// not including the terminating null character.
u32::try_from(len.checked_sub(1).unwrap()).unwrap()
} else {
// If the target buffer was not large enough to hold the data, the return value is the buffer size, in characters,
// required to hold the string and its terminating null character.
u32::try_from(len).unwrap()
}
}
1 change: 1 addition & 0 deletions src/tools/miri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#![feature(let_chains)]
#![feature(lint_reasons)]
#![feature(trait_upcasting)]
#![feature(absolute_path)]
// Configure clippy and other lints
#![allow(
clippy::collapsible_else_if,
Expand Down
25 changes: 7 additions & 18 deletions src/tools/miri/src/shims/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,7 @@ use rustc_middle::ty::Ty;
use rustc_target::abi::Size;

use crate::*;

/// Check whether an operation that writes to a target buffer was successful.
/// Accordingly select return value.
/// Local helper function to be used in Windows shims.
fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
if success {
// If the function succeeds, the return value is the number of characters stored in the target buffer,
// not including the terminating null character.
u32::try_from(len.checked_sub(1).unwrap()).unwrap()
} else {
// If the target buffer was not large enough to hold the data, the return value is the buffer size, in characters,
// required to hold the string and its terminating null character.
u32::try_from(len).unwrap()
}
}
use helpers::windows_check_buffer_size;

#[derive(Default)]
pub struct EnvVars<'tcx> {
Expand Down Expand Up @@ -164,7 +150,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let name_ptr = this.read_pointer(name_op)?;
let name = this.read_os_str_from_wide_str(name_ptr)?;
Ok(match this.machine.env_vars.map.get(&name) {
Some(var_ptr) => {
Some(&var_ptr) => {
this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error
// The offset is used to strip the "{name}=" part of the string.
#[rustfmt::skip]
let name_offset_bytes = u64::try_from(name.len()).unwrap()
Expand Down Expand Up @@ -375,10 +362,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

// If we cannot get the current directory, we return 0
match env::current_dir() {
Ok(cwd) =>
Ok(cwd) => {
this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error
return Ok(Scalar::from_u32(windows_check_buffer_size(
this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?,
))),
)));
}
Err(e) => this.set_last_error_from_io_error(e.kind())?,
}
Ok(Scalar::from_u32(0))
Expand Down
Loading
Loading