Skip to content

Commit 4f61314

Browse files
authored
Merge pull request #566 from RalfJung/foreign-full-mir
Support building and running with full MIR on foreign architectures, drop support for missing MIR
2 parents bccadeb + 5689366 commit 4f61314

File tree

90 files changed

+140
-239
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+140
-239
lines changed

.travis.yml

+19-14
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,52 @@ cache:
99
os:
1010
- linux
1111
- osx
12+
dist: xenial
1213

1314
before_script:
15+
# install extra stuff for cross-compilation
16+
- if [[ "$TRAVIS_OS_NAME" == linux ]]; then sudo apt update && sudo apt install gcc-multilib; fi
1417
# macOS weirdness (https://github.com/travis-ci/travis-ci/issues/6307, https://github.com/travis-ci/travis-ci/issues/10165)
1518
- if [[ "$TRAVIS_OS_NAME" == osx ]]; then rvm get stable; fi
1619
# Compute the rust version we use. We do not use "language: rust" to have more control here.
1720
- |
18-
if [ "$TRAVIS_EVENT_TYPE" = cron ]; then
21+
if [[ "$TRAVIS_EVENT_TYPE" == cron ]]; then
1922
RUST_TOOLCHAIN=nightly
2023
else
2124
RUST_TOOLCHAIN=$(cat rust-version)
2225
fi
26+
- |
27+
if [ "$TRAVIS_OS_NAME" == osx ]; then
28+
export MIRI_SYSROOT_BASE=~/Library/Caches/miri.miri.miri/
29+
else
30+
export MIRI_SYSROOT_BASE=~/.cache/miri/
31+
fi
2332
# install Rust
2433
- curl https://build.travis-ci.org/files/rustup-init.sh -sSf | sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN"
2534
- export PATH=$HOME/.cargo/bin:$PATH
2635
- rustc --version
27-
# customize installation
28-
- rustup target add i686-unknown-linux-gnu
29-
- rustup target add i686-pc-windows-gnu
30-
- rustup target add i686-pc-windows-msvc
3136

3237
script:
3338
- set -e
3439
- |
35-
# Test and install plain miri
40+
# Build and install miri
3641
cargo build --release --all-features --all-targets &&
37-
cargo test --release --all-features &&
3842
cargo install --all-features --force --path .
3943
- |
40-
# Get ourselves a MIR-full libstd, and use it henceforth
44+
# Get ourselves a MIR-full libstd for the host and a foreign architecture
4145
cargo miri setup &&
42-
if [ "$TRAVIS_OS_NAME" == osx ]; then
43-
export MIRI_SYSROOT=~/Library/Caches/miri.miri.miri/HOST
46+
if [[ "$TRAVIS_OS_NAME" == osx ]]; then
47+
cargo miri setup --target i686-apple-darwin
4448
else
45-
export MIRI_SYSROOT=~/.cache/miri/HOST
49+
cargo miri setup --target i686-unknown-linux-gnu
4650
fi
4751
- |
48-
# Test miri with full MIR
49-
cargo test --release --all-features
52+
# Test miri with full MIR, on the host and other architectures
53+
MIRI_SYSROOT=$MIRI_SYSROOT_BASE/HOST cargo test --release --all-features &&
54+
MIRI_SYSROOT=$MIRI_SYSROOT_BASE cargo test --release --all-features
5055
- |
5156
# Test cargo integration
52-
(cd cargo-miri-test && ./run-test.py)
57+
(cd test-cargo-miri && MIRI_SYSROOT=$MIRI_SYSROOT_BASE/HOST ./run-test.py)
5358
5459
notifications:
5560
email:

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ required-features = ["rustc_tests"]
3636
byteorder = { version = "1.1", features = ["i128"]}
3737
cargo_metadata = { version = "0.6", optional = true }
3838
directories = { version = "1.0", optional = true }
39+
rustc_version = { version = "0.2.3", optional = true }
3940
env_logger = "0.5"
4041
log = "0.4"
4142

@@ -44,7 +45,7 @@ vergen = "3"
4445

4546
[features]
4647
default = ["cargo_miri"]
47-
cargo_miri = ["cargo_metadata", "directories"]
48+
cargo_miri = ["cargo_metadata", "directories", "rustc_version"]
4849
rustc_tests = []
4950

5051
[dev-dependencies]

README.md

-3
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,6 @@ cp config.toml.example config.toml
133133
rustup toolchain link custom build/x86_64-unknown-linux-gnu/stage2
134134
# Now cd to your Miri directory, then configure rustup
135135
rustup override set custom
136-
# We also need to tell Miri where to find its sysroot. Since we set
137-
# `test-miri` above, we can just use rustc' sysroot.
138-
export MIRI_SYSROOT=$(rustc --print sysroot)
139136
```
140137

141138
With this, you should now have a working development setup! See

appveyor.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,12 @@ build: false
2727
test_script:
2828
- set RUSTFLAGS=-g
2929
- set RUST_BACKTRACE=1
30-
# Test plain miri
30+
# Build miri
3131
- cargo build --release --all-features --all-targets
32-
- cargo test --release --all-features
3332
# Get ourselves a MIR-full libstd, and use it henceforth
3433
- cargo run --release --all-features --bin cargo-miri -- miri setup
3534
- set MIRI_SYSROOT=%USERPROFILE%\AppData\Local\miri\miri\cache\HOST
36-
# Test miri with full MIR
35+
# Test miri
3736
- cargo test --release --all-features
3837

3938
notifications:

src/bin/cargo-miri.rs

+46-20
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,41 @@ fn show_error(msg: String) -> ! {
5353
std::process::exit(1)
5454
}
5555

56-
fn list_targets(mut args: impl Iterator<Item=String>) -> impl Iterator<Item=cargo_metadata::Target> {
56+
fn get_arg_flag_value(name: &str) -> Option<String> {
57+
// stop searching at `--`
58+
let mut args = std::env::args().skip_while(|val| !(val.starts_with(name) || val == "--"));
59+
60+
match args.next() {
61+
Some(ref p) if p == "--" => None,
62+
Some(ref p) if p == name => args.next(),
63+
Some(p) => {
64+
// Make sure this really starts with `$name=`, we didn't test for the `=` yet.
65+
let v = &p[name.len()..]; // strip leading `$name`
66+
if v.starts_with('=') {
67+
Some(v[1..].to_owned()) // strip leading `=`
68+
} else {
69+
None
70+
}
71+
},
72+
None => None,
73+
}
74+
}
75+
76+
fn list_targets() -> impl Iterator<Item=cargo_metadata::Target> {
5777
// We need to get the manifest, and then the metadata, to enumerate targets.
58-
let manifest_path_arg = args.find(|val| {
59-
val.starts_with("--manifest-path=")
60-
});
78+
let manifest_path = get_arg_flag_value("--manifest-path").map(|m|
79+
Path::new(&m).canonicalize().unwrap()
80+
);
6181

6282
let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(
63-
manifest_path_arg.as_ref().map(AsRef::as_ref),
83+
manifest_path.as_ref().map(AsRef::as_ref),
6484
)
6585
{
6686
metadata
6787
} else {
6888
show_error(format!("error: Could not obtain cargo metadata."));
6989
};
7090

71-
let manifest_path = manifest_path_arg.map(|arg| {
72-
PathBuf::from(Path::new(&arg["--manifest-path=".len()..]))
73-
});
74-
7591
let current_dir = std::env::current_dir();
7692

7793
let package_index = metadata
@@ -176,17 +192,28 @@ path = "lib.rs"
176192
"#).unwrap();
177193
File::create(dir.join("lib.rs")).unwrap();
178194
// Run xargo
179-
if !Command::new("xargo").arg("build").arg("-q")
195+
let target = get_arg_flag_value("--target");
196+
let mut command = Command::new("xargo");
197+
command.arg("build").arg("-q")
180198
.current_dir(&dir)
181199
.env("RUSTFLAGS", miri::miri_default_args().join(" "))
182-
.env("XARGO_HOME", dir.to_str().unwrap())
183-
.status().unwrap().success()
200+
.env("XARGO_HOME", dir.to_str().unwrap());
201+
if let Some(ref target) = target {
202+
command.arg("--target").arg(&target);
203+
}
204+
if !command.status().unwrap().success()
184205
{
185206
show_error(format!("Failed to run xargo"));
186207
}
187208

188-
// That should be it!
189-
let sysroot = dir.join("HOST");
209+
// That should be it! But we need to figure out where xargo built stuff.
210+
// Unfortunately, it puts things into a different directory when the
211+
// architecture matches the host.
212+
let is_host = match target {
213+
None => true,
214+
Some(target) => target == rustc_version::version_meta().unwrap().host,
215+
};
216+
let sysroot = if is_host { dir.join("HOST") } else { PathBuf::from(dir) };
190217
std::env::set_var("MIRI_SYSROOT", &sysroot);
191218
if !ask_user {
192219
println!("A libstd for miri is now available in `{}`", sysroot.display());
@@ -232,7 +259,7 @@ fn main() {
232259
}
233260

234261
// Now run the command.
235-
for target in list_targets(std::env::args().skip(skip)) {
262+
for target in list_targets() {
236263
let args = std::env::args().skip(skip);
237264
let kind = target.kind.get(0).expect(
238265
"badly formatted cargo metadata: target::kind is an empty array",
@@ -315,22 +342,21 @@ fn main() {
315342
.collect()
316343
};
317344
args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));
345+
args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]);
318346

319347
// this check ensures that dependencies are built but not interpreted and the final crate is
320348
// interpreted but not built
321349
let miri_enabled = std::env::args().any(|s| s == "--emit=dep-info,metadata");
322-
323350
let mut command = if miri_enabled {
324351
let mut path = std::env::current_exe().expect("current executable path invalid");
325352
path.set_file_name("miri");
326353
Command::new(path)
327354
} else {
328355
Command::new("rustc")
329356
};
357+
command.args(&args);
330358

331-
args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]);
332-
333-
match command.args(&args).status() {
359+
match command.status() {
334360
Ok(exit) => {
335361
if !exit.success() {
336362
std::process::exit(exit.code().unwrap_or(42));
@@ -361,7 +387,7 @@ where
361387
args.push(r#"feature="cargo-miri""#.to_owned());
362388

363389
let path = std::env::current_exe().expect("current executable path invalid");
364-
let exit_status = std::process::Command::new("cargo")
390+
let exit_status = Command::new("cargo")
365391
.args(&args)
366392
.env("RUSTC", path)
367393
.spawn()

src/fn_call.rs

+4-82
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,6 @@ pub trait EvalContextExt<'tcx, 'mir> {
1717
ret: mir::BasicBlock,
1818
) -> EvalResult<'tcx>;
1919

20-
/// Emulate a function that should have MIR but does not.
21-
/// This is solely to support execution without full MIR.
22-
/// Fail if emulating this function is not supported.
23-
/// This function will handle `goto_block` if needed.
24-
fn emulate_missing_fn(
25-
&mut self,
26-
path: String,
27-
args: &[OpTy<'tcx, Borrow>],
28-
dest: Option<PlaceTy<'tcx, Borrow>>,
29-
ret: Option<mir::BasicBlock>,
30-
) -> EvalResult<'tcx>;
31-
3220
fn find_fn(
3321
&mut self,
3422
instance: ty::Instance<'tcx>,
@@ -81,24 +69,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
8169
return Ok(None);
8270
}
8371

84-
// Otherwise we really want to see the MIR -- but if we do not have it, maybe we can
85-
// emulate something. This is a HACK to support running without a full-MIR libstd.
86-
let mir = match self.load_mir(instance.def) {
87-
Ok(mir) => mir,
88-
Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
89-
self.emulate_missing_fn(
90-
path,
91-
args,
92-
dest,
93-
ret,
94-
)?;
95-
// `goto_block` already handled
96-
return Ok(None);
97-
}
98-
Err(other) => return Err(other),
99-
};
100-
101-
Ok(Some(mir))
72+
// Otherwise, load the MIR
73+
Ok(Some(self.load_mir(instance.def)?))
10274
}
10375

10476
fn emulate_foreign_item(
@@ -113,6 +85,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
11385
Some(name) => name.as_str(),
11486
None => self.tcx.item_name(def_id).as_str(),
11587
};
88+
// Strip linker suffixes (seen on 32bit macOS)
89+
let link_name = link_name.trim_end_matches("$UNIX2003");
11690

11791
let tcx = &{self.tcx.tcx};
11892

@@ -655,58 +629,6 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
655629
Ok(())
656630
}
657631

658-
fn emulate_missing_fn(
659-
&mut self,
660-
path: String,
661-
_args: &[OpTy<'tcx, Borrow>],
662-
dest: Option<PlaceTy<'tcx, Borrow>>,
663-
ret: Option<mir::BasicBlock>,
664-
) -> EvalResult<'tcx> {
665-
// In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early.
666-
match &path[..] {
667-
"std::panicking::rust_panic_with_hook" |
668-
"core::panicking::panic_fmt::::panic_impl" |
669-
"std::rt::begin_panic_fmt" =>
670-
return err!(MachineError("the evaluated program panicked".to_string())),
671-
_ => {}
672-
}
673-
674-
let dest = dest.ok_or_else(
675-
// Must be some function we do not support
676-
|| EvalErrorKind::NoMirFor(path.clone()),
677-
)?;
678-
679-
match &path[..] {
680-
// A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies).
681-
// Still, we can make many things mostly work by "emulating" or ignoring some functions.
682-
"std::io::_print" |
683-
"std::io::_eprint" => {
684-
warn!(
685-
"Ignoring output. To run programs that prints, make sure you have a libstd with full MIR."
686-
);
687-
}
688-
"std::thread::Builder::new" => {
689-
return err!(Unimplemented("miri does not support threading".to_owned()))
690-
}
691-
"std::env::args" => {
692-
return err!(Unimplemented(
693-
"miri does not support program arguments".to_owned(),
694-
))
695-
}
696-
"std::panicking::panicking" |
697-
"std::rt::panicking" => {
698-
// we abort on panic -> `std::rt::panicking` always returns false
699-
self.write_scalar(Scalar::from_bool(false), dest)?;
700-
}
701-
702-
_ => return err!(NoMirFor(path)),
703-
}
704-
705-
self.goto_block(ret)?;
706-
self.dump_place(*dest);
707-
Ok(())
708-
}
709-
710632
fn write_null(&mut self, dest: PlaceTy<'tcx, Borrow>) -> EvalResult<'tcx> {
711633
self.write_scalar(Scalar::from_int(0, dest.layout.size), dest)
712634
}

0 commit comments

Comments
 (0)