Skip to content

Commit 8a6396f

Browse files
committed
Auto merge of #1405 - RalfJung:stage-0, r=RalfJung
make Miri work in rustc bootstrap stage 0 Fixes rust-lang/rust#52856
2 parents 4eaf05c + e65d87b commit 8a6396f

File tree

3 files changed

+97
-56
lines changed

3 files changed

+97
-56
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ Moreover, Miri recognizes some environment variables:
222222
* `MIRI_TEST_FLAGS` (recognized by the test suite) defines extra flags to be
223223
passed to Miri.
224224

225+
The following environment variables are internal, but used to communicate between
226+
different Miri binaries, and as such worth documenting:
227+
228+
* `MIRI_BE_RUSTC` when set to any value tells the Miri driver to actually not
229+
interpret the code but compile it like rustc would. This is useful to be sure
230+
that the compiled `rlib`s are compatible with Miri.
231+
225232
## Contributing and getting help
226233

227234
If you want to contribute to Miri, great! Please check out our

src/bin/cargo-miri.rs

+32-17
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,15 @@ fn get_arg_flag_value(name: &str) -> Option<String> {
8686
}
8787
}
8888

89-
/// Returns a command for the right `miri` binary.
90-
fn miri() -> Command {
89+
/// Returns the path to the `miri` binary
90+
fn find_miri() -> PathBuf {
9191
let mut path = std::env::current_exe().expect("current executable path invalid");
9292
path.set_file_name("miri");
93-
Command::new(path)
93+
path
94+
}
95+
96+
fn miri() -> Command {
97+
Command::new(find_miri())
9498
}
9599

96100
fn cargo() -> Command {
@@ -322,7 +326,8 @@ fn setup(subcommand: MiriCommand) {
322326
show_error(format!("Given Rust source directory `{}` does not exist.", rust_src.display()));
323327
}
324328

325-
// Next, we need our own libstd. We will do this work in whatever is a good cache dir for this platform.
329+
// Next, we need our own libstd. Prepare a xargo project for that purpose.
330+
// We will do this work in whatever is a good cache dir for this platform.
326331
let dirs = directories::ProjectDirs::from("org", "rust-lang", "miri").unwrap();
327332
let dir = dirs.cache_dir();
328333
if !dir.exists() {
@@ -360,20 +365,31 @@ path = "lib.rs"
360365
)
361366
.unwrap();
362367
File::create(dir.join("lib.rs")).unwrap();
363-
// Prepare xargo invocation.
368+
369+
// Determine architectures.
370+
// We always need to set a target so rustc bootstrap can tell apart host from target crates.
371+
let host = rustc_version::version_meta().unwrap().host;
364372
let target = get_arg_flag_value("--target");
365-
let print_sysroot = subcommand == MiriCommand::Setup
366-
&& has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
373+
let target = target.as_ref().unwrap_or(&host);
374+
// Now invoke xargo.
367375
let mut command = xargo_check();
368376
command.arg("build").arg("-q");
377+
command.arg("--target").arg(target);
369378
command.current_dir(&dir);
370-
command.env("RUSTFLAGS", miri::miri_default_args().join(" "));
371379
command.env("XARGO_HOME", &dir);
372380
command.env("XARGO_RUST_SRC", &rust_src);
373-
// Handle target flag.
374-
if let Some(target) = &target {
375-
command.arg("--target").arg(target);
381+
// Use Miri as rustc to build a libstd compatible with us (and use the right flags).
382+
// However, when we are running in bootstrap, we cannot just overwrite `RUSTC`,
383+
// because we still need bootstrap to distinguish between host and target crates.
384+
// In that case we overwrite `RUSTC_REAL` instead which determines the rustc used
385+
// for target crates.
386+
if env::var_os("RUSTC_STAGE").is_some() {
387+
command.env("RUSTC_REAL", find_miri());
388+
} else {
389+
command.env("RUSTC", find_miri());
376390
}
391+
command.env("MIRI_BE_RUSTC", "1");
392+
command.env("RUSTFLAGS", miri::miri_default_args().join(" "));
377393
// Finally run it!
378394
if command.status().expect("failed to run xargo").success().not() {
379395
show_error(format!("Failed to run xargo"));
@@ -382,12 +398,11 @@ path = "lib.rs"
382398
// That should be it! But we need to figure out where xargo built stuff.
383399
// Unfortunately, it puts things into a different directory when the
384400
// architecture matches the host.
385-
let is_host = match &target {
386-
None => true,
387-
Some(target) => target == &rustc_version::version_meta().unwrap().host,
388-
};
389-
let sysroot = if is_host { dir.join("HOST") } else { PathBuf::from(dir) };
401+
let sysroot = if target == &host { dir.join("HOST") } else { PathBuf::from(dir) };
390402
std::env::set_var("MIRI_SYSROOT", &sysroot); // pass the env var to the processes we spawn, which will turn it into "--sysroot" flags
403+
// Figure out what to print.
404+
let print_sysroot = subcommand == MiriCommand::Setup
405+
&& has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
391406
if print_sysroot {
392407
// Print just the sysroot and nothing else; this way we do not need any escaping.
393408
println!("{}", sysroot.display());
@@ -476,7 +491,7 @@ fn in_cargo_miri() {
476491

477492
// Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation,
478493
// i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
479-
// the two codepaths.
494+
// the two codepaths. (That extra argument is why we prefer this over setting `RUSTC`.)
480495
let path = std::env::current_exe().expect("current executable path invalid");
481496
cmd.env("RUSTC_WRAPPER", path);
482497
if verbose {

src/bin/miri.rs

+58-39
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use log::debug;
1616
use rustc_session::CtfeBacktrace;
1717
use rustc_driver::Compilation;
1818
use rustc_hir::def_id::LOCAL_CRATE;
19-
use rustc_interface::{interface, Queries};
2019
use rustc_middle::ty::TyCtxt;
2120

2221
struct MiriCompilerCalls {
@@ -26,8 +25,8 @@ struct MiriCompilerCalls {
2625
impl rustc_driver::Callbacks for MiriCompilerCalls {
2726
fn after_analysis<'tcx>(
2827
&mut self,
29-
compiler: &interface::Compiler,
30-
queries: &'tcx Queries<'tcx>,
28+
compiler: &rustc_interface::interface::Compiler,
29+
queries: &'tcx rustc_interface::Queries<'tcx>,
3130
) -> Compilation {
3231
compiler.session().abort_if_errors();
3332

@@ -106,12 +105,12 @@ fn init_late_loggers(tcx: TyCtxt<'_>) {
106105
fn compile_time_sysroot() -> Option<String> {
107106
if option_env!("RUSTC_STAGE").is_some() {
108107
// This is being built as part of rustc, and gets shipped with rustup.
109-
// We can rely on the sysroot computation in librustc.
108+
// We can rely on the sysroot computation in librustc_session.
110109
return None;
111110
}
112111
// For builds outside rustc, we need to ensure that we got a sysroot
113-
// that gets used as a default. The sysroot computation in librustc would
114-
// end up somewhere in the build dir.
112+
// that gets used as a default. The sysroot computation in librustc_session would
113+
// end up somewhere in the build dir (see `get_or_default_sysroot`).
115114
// Taken from PR <https://github.com/Manishearth/rust-clippy/pull/911>.
116115
let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
117116
let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
@@ -123,7 +122,47 @@ fn compile_time_sysroot() -> Option<String> {
123122
})
124123
}
125124

125+
/// Execute a compiler with the given CLI arguments and callbacks.
126+
fn run_compiler(mut args: Vec<String>, callbacks: &mut (dyn rustc_driver::Callbacks + Send)) {
127+
// Make sure we use the right default sysroot. The default sysroot is wrong,
128+
// because `get_or_default_sysroot` in `librustc_session` bases that on `current_exe`.
129+
//
130+
// Make sure we always call `compile_time_sysroot` as that also does some sanity-checks
131+
// of the environment we were built in.
132+
// FIXME: Ideally we'd turn a bad build env into a compile-time error via CTFE or so.
133+
if let Some(sysroot) = compile_time_sysroot() {
134+
let sysroot_flag = "--sysroot";
135+
if !args.iter().any(|e| e == sysroot_flag) {
136+
// We need to overwrite the default that librustc_session would compute.
137+
args.push(sysroot_flag.to_owned());
138+
args.push(sysroot);
139+
}
140+
}
141+
142+
// Invoke compiler, and handle return code.
143+
let result = rustc_driver::catch_fatal_errors(move || {
144+
rustc_driver::run_compiler(&args, callbacks, None, None)
145+
})
146+
.and_then(|result| result);
147+
let exit_code = match result {
148+
Ok(()) => rustc_driver::EXIT_SUCCESS,
149+
Err(_) => rustc_driver::EXIT_FAILURE,
150+
};
151+
std::process::exit(exit_code);
152+
}
153+
126154
fn main() {
155+
rustc_driver::install_ice_hook();
156+
157+
// If the environment asks us to actually be rustc, then do that.
158+
if env::var_os("MIRI_BE_RUSTC").is_some() {
159+
rustc_driver::init_rustc_env_logger();
160+
// We cannot use `rustc_driver::main` as we need to adjust the CLI arguments.
161+
let mut callbacks = rustc_driver::TimePassesCallbacks::default();
162+
return run_compiler(env::args().collect(), &mut callbacks);
163+
}
164+
165+
// Init loggers the Miri way.
127166
init_early_loggers();
128167

129168
// Parse our arguments and split them across `rustc` and `miri`.
@@ -136,16 +175,20 @@ fn main() {
136175
let mut tracked_pointer_tag: Option<miri::PtrId> = None;
137176
let mut tracked_alloc_id: Option<miri::AllocId> = None;
138177
let mut rustc_args = vec![];
139-
let mut miri_args = vec![];
178+
let mut crate_args = vec![];
140179
let mut after_dashdash = false;
141180
let mut excluded_env_vars = vec![];
142-
for arg in std::env::args() {
181+
for arg in env::args() {
143182
if rustc_args.is_empty() {
144-
// Very first arg: for `rustc`.
183+
// Very first arg: binary name.
145184
rustc_args.push(arg);
185+
// After this, push Miri default args (before everything else so they can be overwritten).
186+
for arg in miri::miri_default_args().iter() {
187+
rustc_args.push(arg.to_string());
188+
}
146189
} else if after_dashdash {
147-
// Everything that comes after are `miri` args.
148-
miri_args.push(arg);
190+
// Everything that comes after `--` is forwarded to the interpreted crate.
191+
crate_args.push(arg);
149192
} else {
150193
match arg.as_str() {
151194
"-Zmiri-disable-validation" => {
@@ -221,30 +264,15 @@ fn main() {
221264
tracked_alloc_id = Some(miri::AllocId(id));
222265
}
223266
_ => {
267+
// Forward to rustc.
224268
rustc_args.push(arg);
225269
}
226270
}
227271
}
228272
}
229273

230-
// Determine sysroot if needed. Make sure we always call `compile_time_sysroot`
231-
// as that also does some sanity-checks of the environment we were built in.
232-
// FIXME: Ideally we'd turn a bad build env into a compile-time error, but
233-
// CTFE does not seem powerful enough for that yet.
234-
if let Some(sysroot) = compile_time_sysroot() {
235-
let sysroot_flag = "--sysroot";
236-
if !rustc_args.iter().any(|e| e == sysroot_flag) {
237-
// We need to overwrite the default that librustc would compute.
238-
rustc_args.push(sysroot_flag.to_owned());
239-
rustc_args.push(sysroot);
240-
}
241-
}
242-
243-
// Finally, add the default flags all the way in the beginning, but after the binary name.
244-
rustc_args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string));
245-
246274
debug!("rustc arguments: {:?}", rustc_args);
247-
debug!("miri arguments: {:?}", miri_args);
275+
debug!("crate arguments: {:?}", crate_args);
248276
let miri_config = miri::MiriConfig {
249277
validate,
250278
stacked_borrows,
@@ -253,18 +281,9 @@ fn main() {
253281
ignore_leaks,
254282
excluded_env_vars,
255283
seed,
256-
args: miri_args,
284+
args: crate_args,
257285
tracked_pointer_tag,
258286
tracked_alloc_id,
259287
};
260-
rustc_driver::install_ice_hook();
261-
let result = rustc_driver::catch_fatal_errors(move || {
262-
rustc_driver::run_compiler(&rustc_args, &mut MiriCompilerCalls { miri_config }, None, None)
263-
})
264-
.and_then(|result| result);
265-
let exit_code = match result {
266-
Ok(()) => rustc_driver::EXIT_SUCCESS,
267-
Err(_) => rustc_driver::EXIT_FAILURE,
268-
};
269-
std::process::exit(exit_code);
288+
return run_compiler(rustc_args, &mut MiriCompilerCalls { miri_config });
270289
}

0 commit comments

Comments
 (0)