From 51ed485ba4eb19083f7da719c55edb8a11f7f28e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 18 Dec 2018 21:49:01 +0100 Subject: [PATCH 1/4] implement stdout/stderr on Windows --- src/fn_call.rs | 45 +++++++++++++++++++++++++++++-- tests/run-pass/box-pair-to-vec.rs | 2 -- tests/run-pass/catch.rs | 1 - tests/run-pass/format.rs | 1 - tests/run-pass/hello.rs | 1 - tests/run-pass/issue-17877.rs | 2 +- tests/run-pass/issue-3794.rs | 1 - 7 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index ab38270724..7815c1ce40 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -596,18 +596,59 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a, this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; } "TlsGetValue" => { - let key = this.read_scalar(args[0])?.to_bits(args[0].layout.size)?; + let key = this.read_scalar(args[0])?.to_u32()? as u128; let ptr = this.machine.tls.load_tls(key)?; this.write_scalar(ptr, dest)?; } "TlsSetValue" => { - let key = this.read_scalar(args[0])?.to_bits(args[0].layout.size)?; + let key = this.read_scalar(args[0])?.to_u32()? as u128; let new_ptr = this.read_scalar(args[1])?.not_undef()?; this.machine.tls.store_tls(key, new_ptr)?; // Return success (1) this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?; } + "GetStdHandle" => { + let which = this.read_scalar(args[0])?.to_i32()?; + // We just make this the identity function, so we know later in "WriteFile" + // which one it is. + this.write_scalar(Scalar::from_int(which, this.pointer_size()), dest)?; + } + "WriteFile" => { + let handle = this.read_scalar(args[0])?.to_isize(this)?; + let buf = this.read_scalar(args[1])?.not_undef()?; + let n = this.read_scalar(args[2])?.to_u32()?; + let written_place = this.deref_operand(args[3])?; + this.write_null(written_place.into())?; // spec says we always write 0 first + let written = if handle == -11 || handle == -12 { + // stdout/stderr + use std::io::{self, Write}; + + let buf_cont = this.memory().read_bytes(buf, Size::from_bytes(u64::from(n)))?; + let res = if handle == -11 { + io::stdout().write(buf_cont) + } else { + io::stderr().write(buf_cont) + }; + res.ok().map(|n| n as u32) + } else { + eprintln!("Miri: Ignored output to handle {}", handle); + Some(n) // pretend it all went well + }; + // If there was no error, write back how much was written + if let Some(n) = written { + this.write_scalar(Scalar::from_uint(n, Size::from_bits(32)), written_place.into())?; + } + // Return whether this was a success + this.write_scalar( + Scalar::from_int(if written.is_some() { 1 } else { 0 }, dest.layout.size), + dest, + )?; + } + "GetConsoleMode" => { + // Everything is a pipe + this.write_null(dest)?; + } // We can't execute anything else _ => { diff --git a/tests/run-pass/box-pair-to-vec.rs b/tests/run-pass/box-pair-to-vec.rs index 3b829d0f12..353afb9d32 100644 --- a/tests/run-pass/box-pair-to-vec.rs +++ b/tests/run-pass/box-pair-to-vec.rs @@ -1,5 +1,3 @@ -//ignore-msvc: Stdout not implemented on Windows - #[repr(C)] #[derive(Debug)] struct PairFoo { diff --git a/tests/run-pass/catch.rs b/tests/run-pass/catch.rs index aa7bccaa5f..5fd65f138a 100644 --- a/tests/run-pass/catch.rs +++ b/tests/run-pass/catch.rs @@ -1,4 +1,3 @@ -//ignore-msvc: Stdout not implemented on Windows use std::panic::{catch_unwind, AssertUnwindSafe}; fn main() { diff --git a/tests/run-pass/format.rs b/tests/run-pass/format.rs index b1b05abd29..78729b9156 100644 --- a/tests/run-pass/format.rs +++ b/tests/run-pass/format.rs @@ -1,4 +1,3 @@ -//ignore-msvc: Stdout not implemented on Windows fn main() { println!("Hello {}", 13); } diff --git a/tests/run-pass/hello.rs b/tests/run-pass/hello.rs index c92c1567f5..e7a11a969c 100644 --- a/tests/run-pass/hello.rs +++ b/tests/run-pass/hello.rs @@ -1,4 +1,3 @@ -//ignore-msvc: Stdout not implemented on Windows fn main() { println!("Hello, world!"); } diff --git a/tests/run-pass/issue-17877.rs b/tests/run-pass/issue-17877.rs index 762bacbe0e..91d17683e3 100644 --- a/tests/run-pass/issue-17877.rs +++ b/tests/run-pass/issue-17877.rs @@ -9,7 +9,7 @@ // except according to those terms. //ignore-windows: Causes a stack overflow?!? Likely a rustc bug: https://github.com/rust-lang/rust/issues/53820 -//Once that bug is fixed, increase the size to 16*1024 and enable on all platforms. +//FIXME: Once that bug is fixed, increase the size to 16*1024 and enable on all platforms. #![feature(slice_patterns)] diff --git a/tests/run-pass/issue-3794.rs b/tests/run-pass/issue-3794.rs index 32b4d27f11..badb833ee8 100644 --- a/tests/run-pass/issue-3794.rs +++ b/tests/run-pass/issue-3794.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//ignore-msvc: Stdout not implemented on Windows #![feature(box_syntax)] trait T { From e7c523f72ee0850972cc8a37b6780d2fe1666b0c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 18 Dec 2018 19:52:30 +0100 Subject: [PATCH 2/4] run test-cargo-miri on Windows --- appveyor.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 09040ed42a..a46214a3c4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,13 +27,17 @@ build: false test_script: - set RUST_TEST_NOCAPTURE=1 - set RUST_BACKTRACE=1 - # Build miri + # Build and install miri - cargo build --release --all-features --all-targets + - cargo install --all-features --force --path . # Get ourselves a MIR-full libstd, and use it henceforth - - cargo run --release --all-features --bin cargo-miri -- miri setup + - cargo miri setup - set MIRI_SYSROOT=%USERPROFILE%\AppData\Local\miri\miri\cache\HOST # Test miri - cargo test --release --all-features + # Test cargo integration + - cd test-cargo-miri + - python3 run-test.py notifications: - provider: Email From af4fb6655e9a09d4154250bcff99fc80eb2c402f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 19 Dec 2018 15:25:25 +0100 Subject: [PATCH 3/4] implement GetCommandLineW, GetEnvironmentVariableW, GetConsoleScreenBufferInfo, SetConsoleTextAttribute, GetSystemInfo --- src/fn_call.rs | 47 ++++++++++++++++++++++++++++------ src/lib.rs | 69 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 93 insertions(+), 23 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index 7815c1ce40..16d18dbfe6 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -562,6 +562,14 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a, }, // Windows API stubs + "SetLastError" => { + let err = this.read_scalar(args[0])?.to_u32()?; + this.machine.last_error = err; + } + "GetLastError" => { + this.write_scalar(Scalar::from_uint(this.machine.last_error, Size::from_bits(32)), dest)?; + } + "AddVectoredExceptionHandler" => { // any non zero value works for the stdlib. This is just used for stackoverflows anyway this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?; @@ -569,20 +577,35 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a, "InitializeCriticalSection" | "EnterCriticalSection" | "LeaveCriticalSection" | - "DeleteCriticalSection" | - "SetLastError" => { - // Function does not return anything, nothing to do + "DeleteCriticalSection" => { + // Nothing to do, not even a return value }, "GetModuleHandleW" | "GetProcAddress" | - "TryEnterCriticalSection" => { + "TryEnterCriticalSection" | + "GetConsoleScreenBufferInfo" | + "SetConsoleTextAttribute" => { // pretend these do not exist/nothing happened, by returning zero this.write_null(dest)?; }, - "GetLastError" => { - // this is c::ERROR_CALL_NOT_IMPLEMENTED - this.write_scalar(Scalar::from_int(120, dest.layout.size), dest)?; - }, + "GetSystemInfo" => { + let system_info = this.deref_operand(args[0])?; + let system_info_ptr = system_info.ptr.to_ptr()?; + // initialize with 0 + this.memory_mut().get_mut(system_info_ptr.alloc_id)? + .write_repeat(tcx, system_info_ptr, 0, system_info.layout.size)?; + // set number of processors to 1 + let dword_size = Size::from_bytes(4); + let offset = 2*dword_size + 3*tcx.pointer_size(); + this.memory_mut().get_mut(system_info_ptr.alloc_id)? + .write_scalar( + tcx, + system_info_ptr.offset(offset, tcx)?, + Scalar::from_int(1, dword_size).into(), + dword_size, + )?; + } + "TlsAlloc" => { // This just creates a key; Windows does not natively support TLS dtors. @@ -649,6 +672,14 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a, // Everything is a pipe this.write_null(dest)?; } + "GetEnvironmentVariableW" => { + // This is not the env var you are looking for + this.machine.last_error = 203; // ERROR_ENVVAR_NOT_FOUND + this.write_null(dest)?; + } + "GetCommandLineW" => { + this.write_scalar(Scalar::Ptr(this.machine.cmd_line.unwrap()), dest)?; + } // We can't execute anything else _ => { diff --git a/src/lib.rs b/src/lib.rs index e41a92e55f..bc5e8363e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ use std::borrow::Cow; use std::env; use rustc::ty::{self, TyCtxt, query::TyCtxtAt}; -use rustc::ty::layout::{TyLayout, LayoutOf, Size}; +use rustc::ty::layout::{TyLayout, LayoutOf, Size, Align}; use rustc::hir::{self, def_id::DefId}; use rustc::mir; @@ -123,24 +123,56 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>( let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?; let argc = Scalar::from_int(1, dest.layout.size); ecx.write_scalar(argc, dest)?; - let argc_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into())?; - ecx.write_scalar(argc, argc_place.into())?; - ecx.machine.argc = Some(argc_place.ptr.to_ptr()?); + // Store argc for macOS _NSGetArgc + { + let argc_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into())?; + ecx.write_scalar(argc, argc_place.into())?; + ecx.machine.argc = Some(argc_place.ptr.to_ptr()?); + } // FIXME: extract main source file path // Third argument (argv): &[b"foo"] + const CMD: &str = "running-in-miri"; let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?; - let foo = ecx.memory_mut().allocate_static_bytes(b"foo\0").with_default_tag(); - let foo_ty = ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8); - let foo_layout = ecx.layout_of(foo_ty)?; - let foo_place = ecx.allocate(foo_layout, MiriMemoryKind::Env.into())?; - ecx.write_scalar(Scalar::Ptr(foo), foo_place.into())?; - ecx.memory_mut().mark_immutable(foo_place.to_ptr()?.alloc_id)?; - let argv = foo_place.ptr; - ecx.write_scalar(argv, dest)?; - let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into())?; - ecx.write_scalar(argv, argv_place.into())?; - ecx.machine.argv = Some(argv_place.ptr.to_ptr()?); + let cmd = ecx.memory_mut().allocate_static_bytes(CMD.as_bytes()).with_default_tag(); + let raw_str_layout = ecx.layout_of(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8))?; + let cmd_place = ecx.allocate(raw_str_layout, MiriMemoryKind::Env.into())?; + ecx.write_scalar(Scalar::Ptr(cmd), cmd_place.into())?; + ecx.memory_mut().mark_immutable(cmd_place.to_ptr()?.alloc_id)?; + // Store argv for macOS _NSGetArgv + { + let argv = cmd_place.ptr; + ecx.write_scalar(argv, dest)?; + let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into())?; + ecx.write_scalar(argv, argv_place.into())?; + ecx.machine.argv = Some(argv_place.ptr.to_ptr()?); + } + // Store cmdline as UTF-16 for Windows GetCommandLineW + { + let tcx = &{ecx.tcx.tcx}; + let cmd_utf16: Vec = CMD.encode_utf16() + .chain(Some(0)) // add 0-terminator + .collect(); + let cmd_ptr = ecx.memory_mut().allocate( + Size::from_bytes(cmd_utf16.len() as u64 * 2), + Align::from_bytes(2).unwrap(), + MiriMemoryKind::Env.into(), + )?.with_default_tag(); + ecx.machine.cmd_line = Some(cmd_ptr); + // store the UTF-16 string + let char_size = Size::from_bytes(2); + let cmd_alloc = ecx.memory_mut().get_mut(cmd_ptr.alloc_id)?; + let mut cur_ptr = cmd_ptr; + for &c in cmd_utf16.iter() { + cmd_alloc.write_scalar( + tcx, + cur_ptr, + Scalar::from_uint(c, char_size).into(), + char_size, + )?; + cur_ptr = cur_ptr.offset(char_size, tcx)?; + } + } assert!(args.next().is_none(), "start lang item has more arguments than expected"); @@ -263,8 +295,13 @@ pub struct Evaluator<'tcx> { /// Program arguments (`Option` because we can only initialize them after creating the ecx). /// These are *pointers* to argc/argv because macOS. + /// We also need the full cmdline as one string because Window. pub(crate) argc: Option>, pub(crate) argv: Option>, + pub(crate) cmd_line: Option>, + + /// Last OS error + pub(crate) last_error: u32, /// TLS state pub(crate) tls: TlsData<'tcx>, @@ -282,6 +319,8 @@ impl<'tcx> Evaluator<'tcx> { env_vars: HashMap::default(), argc: None, argv: None, + cmd_line: None, + last_error: 0, tls: TlsData::default(), validate, stacked_borrows: stacked_borrows::State::default(), From 750cd442beb0009df6ee3dc80b88582f3b8730d9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 19 Dec 2018 15:45:23 +0100 Subject: [PATCH 4/4] fix argv null terminator --- src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bc5e8363e8..cf2c39f61e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,7 +132,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>( // FIXME: extract main source file path // Third argument (argv): &[b"foo"] - const CMD: &str = "running-in-miri"; + const CMD: &str = "running-in-miri\0"; let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?; let cmd = ecx.memory_mut().allocate_static_bytes(CMD.as_bytes()).with_default_tag(); let raw_str_layout = ecx.layout_of(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8))?; @@ -150,9 +150,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>( // Store cmdline as UTF-16 for Windows GetCommandLineW { let tcx = &{ecx.tcx.tcx}; - let cmd_utf16: Vec = CMD.encode_utf16() - .chain(Some(0)) // add 0-terminator - .collect(); + let cmd_utf16: Vec = CMD.encode_utf16().collect(); let cmd_ptr = ecx.memory_mut().allocate( Size::from_bytes(cmd_utf16.len() as u64 * 2), Align::from_bytes(2).unwrap(),