Skip to content
Open
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
77 changes: 8 additions & 69 deletions cargo-miri/src/phases.rs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used to have code here that skips the build if the crate type is cdylib. That was obviously wrong, I just didn't realize at the time that there can be multiple crate types. But we could maybe instead filter the crate types and remove cdylib (and anything else that's unsupported), and then skip the build if there's no crate type left?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've now pushed a commit to do that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should then also fix the cdylib-only-dependency, shouldn't it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't. The dummy codegen backend tells cargo that cdylib is not supported and then cargo reports this error before we can filter the --crate-type cdylib.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, unfortunate.

Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,6 @@ fn show_version() {
println!();
}

fn forward_patched_extern_arg(args: &mut impl Iterator<Item = String>, cmd: &mut Command) {
cmd.arg("--extern"); // always forward flag, but adjust filename:
let path = args.next().expect("`--extern` should be followed by a filename");
if let Some(lib) = path.strip_suffix(".rlib") {
// If this is an rlib, make it an rmeta.
cmd.arg(format!("{lib}.rmeta"));
} else {
// Some other extern file (e.g. a `.so`). Forward unchanged.
cmd.arg(path);
}
}

pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
// Require a subcommand before any flags.
// We cannot know which of those flags take arguments and which do not,
Expand Down Expand Up @@ -276,7 +264,7 @@ pub enum RustcPhase {
Rustdoc,
}

pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
pub fn phase_rustc(args: impl Iterator<Item = String>, phase: RustcPhase) {
/// Determines if we are being invoked (as rustc) to build a crate for
/// the "target" architecture, in contrast to the "host" architecture.
/// Host crates are for build scripts and proc macros and still need to
Expand Down Expand Up @@ -444,7 +432,6 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
}

let mut cmd = miri();
let mut emit_link_hack = false;
// 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 {
Expand All @@ -455,7 +442,7 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
}

// Forward arguments, but patched.
let emit_flag = "--emit";

// This hack helps bootstrap run standard library tests in Miri. The issue is as follows:
// when running `cargo miri test` on libcore, cargo builds a local copy of core and makes it
// a dependency of the integration test crate. This copy duplicates all the lang items, so
Expand All @@ -471,30 +458,7 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
let replace_librs = env::var_os("MIRI_REPLACE_LIBRS_IF_NOT_TEST").is_some()
&& !runnable_crate
&& phase == RustcPhase::Build;
while let Some(arg) = args.next() {
// Patch `--emit`: remove "link" from "--emit" to make this a check-only build.
if let Some(val) = arg.strip_prefix(emit_flag) {
// Patch this argument. First, extract its value.
let val =
val.strip_prefix('=').expect("`cargo` should pass `--emit=X` as one argument");
let mut val: Vec<_> = val.split(',').collect();
// Now make sure "link" is not in there, but "metadata" is.
if let Some(i) = val.iter().position(|&s| s == "link") {
emit_link_hack = true;
val.remove(i);
if !val.contains(&"metadata") {
val.push("metadata");
}
}
cmd.arg(format!("{emit_flag}={}", val.join(",")));
continue;
}
// Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files:
// https://github.com/rust-lang/miri/issues/1705
if arg == "--extern" {
forward_patched_extern_arg(&mut args, &mut cmd);
continue;
}
for arg in args {
// If the REPLACE_LIBRS hack is enabled and we are building a `lib.rs` file, and a
// `lib.miri.rs` file exists, then build that instead.
if replace_librs {
Expand Down Expand Up @@ -543,17 +507,6 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
eprintln!("[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate}");
}

// Create a stub .rlib file if "link" was requested by cargo.
// This is necessary to prevent cargo from doing rebuilds all the time.
if emit_link_hack {
for filename in out_filenames() {
if verbose > 0 {
eprintln!("[cargo-miri rustc] creating fake lib file at `{}`", filename.display());
}
File::create(filename).expect("failed to create fake lib file");
}
}

debug_cmd("[cargo-miri rustc]", verbose, &cmd);
exec(cmd);
}
Expand Down Expand Up @@ -624,17 +577,11 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
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
// `.rmeta`.
// We also need to remove `--error-format` as cargo specifies that to be JSON,
// We need to remove `--error-format` as cargo specifies that to be JSON,
// but when we run here, cargo does not interpret the JSON any more. `--json`
// then also needs to be dropped.
let mut args = info.args.iter();
while let Some(arg) = args.next() {
if arg == "--extern" {
forward_patched_extern_arg(&mut (&mut args).cloned(), &mut cmd);
} else if let Some(suffix) = arg.strip_prefix("--error-format") {
for arg in &info.args {
if let Some(suffix) = arg.strip_prefix("--error-format") {
assert!(suffix.starts_with('='));
// Drop this argument.
} else if let Some(suffix) = arg.strip_prefix("--json") {
Expand Down Expand Up @@ -668,23 +615,15 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
}
}

pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) {
pub fn phase_rustdoc(args: impl Iterator<Item = String>) {
let verbose = env::var("MIRI_VERBOSE")
.map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer"));

// phase_cargo_miri sets the RUSTDOC env var to ourselves, and puts a backup
// of the old value into MIRI_ORIG_RUSTDOC. So that's what we have to invoke now.
let rustdoc = env::var("MIRI_ORIG_RUSTDOC").unwrap_or("rustdoc".to_string());
let mut cmd = Command::new(rustdoc);

while let Some(arg) = args.next() {
if arg == "--extern" {
// Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files.
forward_patched_extern_arg(&mut args, &mut cmd);
} else {
cmd.arg(arg);
}
}
cmd.args(args);

// Doctests of `proc-macro` crates (and their dependencies) are always built for the host,
// so we are not able to run them in Miri.
Expand Down
2 changes: 1 addition & 1 deletion cargo-miri/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ pub fn setup(

// Do the build.
let status = SysrootBuilder::new(&sysroot_dir, target)
.build_mode(BuildMode::Check)
.build_mode(BuildMode::Build)
.rustc_version(rustc_version.clone())
.sysroot_config(sysroot_config)
.rustflags(rustflags)
Expand Down
29 changes: 6 additions & 23 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ extern crate rustc_hir;
extern crate rustc_hir_analysis;
extern crate rustc_interface;
extern crate rustc_log;
extern crate rustc_metadata;
extern crate rustc_middle;
extern crate rustc_session;
extern crate rustc_span;
Expand All @@ -26,10 +25,8 @@ mod log;
use std::env;
use std::num::NonZero;
use std::ops::Range;
use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};

use miri::{
Expand All @@ -51,10 +48,8 @@ use rustc_middle::middle::exported_symbols::{
use rustc_middle::query::LocalCrate;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::util::Providers;
use rustc_session::EarlyDiagCtxt;
use rustc_session::config::{CrateType, ErrorOutputType, OptLevel};
use rustc_session::search_paths::PathKind;
use rustc_span::def_id::DefId;

use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers};
Expand Down Expand Up @@ -126,21 +121,6 @@ fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, MiriEntryFnType) {
}

impl rustc_driver::Callbacks for MiriCompilerCalls {
fn config(&mut self, config: &mut Config) {
config.override_queries = Some(|_, providers| {
providers.extern_queries.used_crate_source = |tcx, cnum| {
let mut providers = Providers::default();
rustc_metadata::provide(&mut providers);
let mut crate_source = (providers.extern_queries.used_crate_source)(tcx, cnum);
// HACK: rustc will emit "crate ... required to be available in rlib format, but
// was not found in this form" errors once we use `tcx.dependency_formats()` if
// there's no rlib provided, so setting a dummy path here to workaround those errors.
Arc::make_mut(&mut crate_source).rlib = Some((PathBuf::new(), PathKind::All));
crate_source
};
});
}

fn after_analysis<'tcx>(
&mut self,
_: &rustc_interface::interface::Compiler,
Expand Down Expand Up @@ -253,12 +233,15 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
#[allow(rustc::potential_query_instability)] // rustc_codegen_ssa (where this code is copied from) also allows this lint
fn config(&mut self, config: &mut Config) {
if config.opts.prints.is_empty() && self.target_crate {
// Avoid warnings about unsupported crate types
#[allow(rustc::bad_opt_access)]
config.opts.crate_types.retain(|&c| c == CrateType::Executable || c == CrateType::Rlib);

// Queries overridden here affect the data stored in `rmeta` files of dependencies,
// which will be used later in non-`MIRI_BE_RUSTC` mode.
config.override_queries = Some(|_, local_providers| {
// `exported_non_generic_symbols` and `reachable_non_generics` provided by rustc always returns
// an empty result if `tcx.sess.opts.output_types.should_codegen()` is false.
// In addition we need to add #[used] symbols to exported_symbols for `lookup_link_section`.
// We need to add #[used] symbols to exported_symbols for `lookup_link_section`.
// FIXME handle this somehow in rustc itself to avoid this hack
local_providers.exported_non_generic_symbols = |tcx, LocalCrate| {
let reachable_set = tcx.with_stable_hashing_context(|hcx| {
tcx.reachable_set(()).to_sorted(&hcx, true)
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ pub use crate::shims::unwind::{CatchUnwindData, EvalContextExt as _};
/// Also disable the MIR pass that inserts an alignment check on every pointer dereference. Miri
/// does that too, and with a better error message.
pub const MIRI_DEFAULT_ARGS: &[&str] = &[
"-Zcodegen-backend=dummy",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dummy codegen backend is a builtin codegen backend that is only capable of emitting rlibs containing the crate metadata. For every other crate type it emits an error after codegen. This is fine for miri as for --crate-type bin, miri exits before codegen.

"--cfg=miri",
"-Zalways-encode-mir",
"-Zextra-const-ub-checks",
Expand Down
1 change: 0 additions & 1 deletion test-cargo-miri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions test-cargo-miri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["subcrate", "issue-1567", "exported-symbol-dep", "test-local-crate-detection"]
members = ["cdylib", "subcrate", "issue-1567", "exported-symbol-dep", "test-local-crate-detection"]
exclude = ["no-std-smoke"] # it wants to be panic="abort"

[package]
Expand All @@ -10,7 +10,6 @@ edition = "2024"

[dependencies]
byteorder = "1.0"
cdylib = { path = "cdylib" }
exported_symbol = { path = "exported-symbol" }
proc_macro_crate = { path = "proc-macro-crate" }
issue_1567 = { path = "issue-1567" }
Expand Down
2 changes: 1 addition & 1 deletion test-cargo-miri/run.local_crate.stdout.ref
Original file line number Diff line number Diff line change
@@ -1 +1 @@
subcrate,issue_1567,exported_symbol_dep,test_local_crate_detection,cargo_miri_test,cdylib,exported_symbol,issue_1691,issue_1705,issue_rust_86261,proc_macro_crate
cdylib,subcrate,issue_1567,exported_symbol_dep,test_local_crate_detection,cargo_miri_test,exported_symbol,issue_1691,issue_1705,issue_rust_86261,proc_macro_crate