Skip to content

Commit a6d74a6

Browse files
committed
Auto merge of #1251 - RalfJung:win-cross, r=RalfJung
cross-test on a Windows host
2 parents ad7089f + e9e04e5 commit a6d74a6

File tree

7 files changed

+155
-42
lines changed

7 files changed

+155
-42
lines changed

.appveyor.yml

+23-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ environment:
22
global:
33
PROJECT_NAME: miri
44
matrix:
5-
- TARGET: x86_64-pc-windows-msvc
65
- TARGET: i686-pc-windows-msvc
76

87
# branches to build
@@ -43,18 +42,36 @@ build_script:
4342
# Build and install miri
4443
- cargo build --release --all-features --all-targets --locked
4544
- cargo install --all-features --force --path . --locked --offline
46-
# Get ourselves a MIR-full libstd, and use it henceforth
47-
- cargo miri setup
48-
- set MIRI_SYSROOT=%USERPROFILE%\AppData\Local\rust-lang\miri\cache\HOST
4945

5046
test_script:
5147
- set RUST_TEST_NOCAPTURE=1
5248
- set RUST_BACKTRACE=1
53-
# Test miri
49+
# Test host miri: 32bit Windows
50+
- cargo miri setup
51+
- set MIRI_SYSROOT=%USERPROFILE%\AppData\Local\rust-lang\miri\cache\HOST
52+
- cargo test --release --all-features --locked
53+
- cd test-cargo-miri
54+
- '"C:\msys64\mingw64\bin\python3.exe" run-test.py'
55+
- cd ..
56+
- ps: $env:MIRI_SYSROOT = ""
57+
# Test foreign miri: 64bit Linux
58+
- cargo miri setup --target x86_64-unknown-linux-gnu
59+
- set MIRI_SYSROOT=%USERPROFILE%\AppData\Local\rust-lang\miri\cache
60+
- set MIRI_TEST_TARGET=x86_64-unknown-linux-gnu
61+
- cargo test --release --all-features --locked
62+
- cd test-cargo-miri
63+
- '"C:\msys64\mingw64\bin\python3.exe" run-test.py'
64+
- cd ..
65+
- ps: $env:MIRI_SYSROOT = ""
66+
# Test foreign miri: 64bit macOS
67+
- cargo miri setup --target x86_64-apple-darwin
68+
- set MIRI_SYSROOT=%USERPROFILE%\AppData\Local\rust-lang\miri\cache
69+
- set MIRI_TEST_TARGET=x86_64-apple-darwin
5470
- cargo test --release --all-features --locked
55-
# Test cargo integration
5671
- cd test-cargo-miri
5772
- '"C:\msys64\mingw64\bin\python3.exe" run-test.py'
73+
- cd ..
74+
- ps: $env:MIRI_SYSROOT = ""
5875

5976
after_test:
6077
# Don't cache "master" toolchain, it's a waste

src/helpers.rs

+90-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
use std::ffi::{OsStr, OsString};
2+
use std::path::{Path, PathBuf};
23
use std::{iter, mem};
34
use std::convert::TryFrom;
5+
use std::borrow::Cow;
6+
7+
#[cfg(unix)]
8+
use std::os::unix::ffi::{OsStrExt, OsStringExt};
9+
#[cfg(windows)]
10+
use std::os::windows::ffi::{OsStrExt, OsStringExt};
411

512
use rustc::mir;
613
use rustc::ty::{
@@ -477,7 +484,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
477484
{
478485
#[cfg(unix)]
479486
fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
480-
Ok(std::os::unix::ffi::OsStrExt::from_bytes(bytes))
487+
Ok(OsStr::from_bytes(bytes))
481488
}
482489
#[cfg(not(unix))]
483490
fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
@@ -500,7 +507,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
500507
{
501508
#[cfg(windows)]
502509
pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
503-
Ok(std::os::windows::ffi::OsStringExt::from_wide(&u16_vec[..]))
510+
Ok(OsString::from_wide(&u16_vec[..]))
504511
}
505512
#[cfg(not(windows))]
506513
pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
@@ -513,7 +520,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
513520
u16vec_to_osstring(u16_vec)
514521
}
515522

516-
517523
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
518524
/// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
519525
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
@@ -527,7 +533,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
527533
) -> InterpResult<'tcx, (bool, u64)> {
528534
#[cfg(unix)]
529535
fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
530-
Ok(std::os::unix::ffi::OsStrExt::as_bytes(os_str))
536+
Ok(os_str.as_bytes())
531537
}
532538
#[cfg(not(unix))]
533539
fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
@@ -566,7 +572,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
566572
) -> InterpResult<'tcx, (bool, u64)> {
567573
#[cfg(windows)]
568574
fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
569-
Ok(std::os::windows::ffi::OsStrExt::encode_wide(os_str).collect())
575+
Ok(os_str.encode_wide().collect())
570576
}
571577
#[cfg(not(windows))]
572578
fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
@@ -592,7 +598,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
592598
// Store the UTF-16 string.
593599
let char_size = Size::from_bytes(2);
594600
for (idx, c) in u16_vec.into_iter().chain(iter::once(0x0000)).enumerate() {
595-
let place = this.mplace_field(mplace, idx as u64)?;
601+
let place = this.mplace_field(mplace, u64::try_from(idx).unwrap())?;
596602
this.write_scalar(Scalar::from_uint(c, char_size), place.into())?;
597603
}
598604
Ok((true, string_length))
@@ -613,6 +619,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
613619
}
614620
}
615621

622+
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
616623
fn alloc_os_str_as_c_str(
617624
&mut self,
618625
os_str: &OsStr,
@@ -627,6 +634,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
627634
arg_place.ptr.assert_ptr()
628635
}
629636

637+
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of `u16`.
630638
fn alloc_os_str_as_wide_str(
631639
&mut self,
632640
os_str: &OsStr,
@@ -640,6 +648,82 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
640648
assert!(self.write_os_str_to_wide_str(os_str, arg_place, size).unwrap().0);
641649
arg_place.ptr.assert_ptr()
642650
}
651+
652+
/// Read a null-terminated sequence of bytes, and perform path separator conversion if needed.
653+
fn read_path_from_c_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, Cow<'a, Path>>
654+
where
655+
'tcx: 'a,
656+
'mir: 'a,
657+
{
658+
let this = self.eval_context_ref();
659+
let os_str = this.read_os_str_from_c_str(scalar)?;
660+
661+
#[cfg(windows)]
662+
return Ok(if this.tcx.sess.target.target.target_os == "windows" {
663+
// Windows-on-Windows, all fine.
664+
Cow::Borrowed(Path::new(os_str))
665+
} else {
666+
// Unix target, Windows host. Need to convert target '/' to host '\'.
667+
let converted = os_str
668+
.encode_wide()
669+
.map(|wchar| if wchar == '/' as u16 { '\\' as u16 } else { wchar })
670+
.collect::<Vec<_>>();
671+
Cow::Owned(PathBuf::from(OsString::from_wide(&converted)))
672+
});
673+
#[cfg(unix)]
674+
return Ok(if this.tcx.sess.target.target.target_os == "windows" {
675+
// Windows target, Unix host. Need to convert target '\' to host '/'.
676+
let converted = os_str
677+
.as_bytes()
678+
.iter()
679+
.map(|&wchar| if wchar == '/' as u8 { '\\' as u8 } else { wchar })
680+
.collect::<Vec<_>>();
681+
Cow::Owned(PathBuf::from(OsString::from_vec(converted)))
682+
} else {
683+
// Unix-on-Unix, all is fine.
684+
Cow::Borrowed(Path::new(os_str))
685+
});
686+
}
687+
688+
/// Write a Path to the machine memory, adjusting path separators if needed.
689+
fn write_path_to_c_str(
690+
&mut self,
691+
path: &Path,
692+
scalar: Scalar<Tag>,
693+
size: u64,
694+
) -> InterpResult<'tcx, (bool, u64)> {
695+
let this = self.eval_context_mut();
696+
697+
#[cfg(windows)]
698+
let os_str = if this.tcx.sess.target.target.target_os == "windows" {
699+
// Windows-on-Windows, all fine.
700+
Cow::Borrowed(path.as_os_str())
701+
} else {
702+
// Unix target, Windows host. Need to convert host '\\' to target '/'.
703+
let converted = path
704+
.as_os_str()
705+
.encode_wide()
706+
.map(|wchar| if wchar == '\\' as u16 { '/' as u16 } else { wchar })
707+
.collect::<Vec<_>>();
708+
Cow::Owned(OsString::from_wide(&converted))
709+
};
710+
#[cfg(unix)]
711+
let os_str = if this.tcx.sess.target.target.target_os == "windows" {
712+
// Windows target, Unix host. Need to convert host '/' to target '\'.
713+
let converted = path
714+
.as_os_str()
715+
.as_bytes()
716+
.iter()
717+
.map(|&wchar| if wchar == '/' as u8 { '\\' as u8 } else { wchar })
718+
.collect::<Vec<_>>();
719+
Cow::Owned(OsString::from_vec(converted))
720+
} else {
721+
// Unix-on-Unix, all is fine.
722+
Cow::Borrowed(path.as_os_str())
723+
};
724+
725+
this.write_os_str_to_c_str(&os_str, scalar, size)
726+
}
643727
}
644728

645729
pub fn immty_from_int_checked<'tcx>(

src/shims/env.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
134134
// If we cannot get the current directory, we return null
135135
match env::current_dir() {
136136
Ok(cwd) => {
137-
if this.write_os_str_to_c_str(&OsString::from(cwd), buf, size)?.0 {
137+
if this.write_path_to_c_str(&cwd, buf, size)?.0 {
138138
return Ok(buf);
139139
}
140140
let erange = this.eval_libc("ERANGE")?;
@@ -150,7 +150,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
150150

151151
this.check_no_isolation("chdir")?;
152152

153-
let path = this.read_os_str_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
153+
let path = this.read_path_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
154154

155155
match env::set_current_dir(path) {
156156
Ok(()) => Ok(0),

src/shims/fs.rs

+21-21
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
22
use std::convert::{TryFrom, TryInto};
33
use std::fs::{read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir};
44
use std::io::{Read, Seek, SeekFrom, Write};
5-
use std::path::PathBuf;
5+
use std::path::Path;
66
use std::time::SystemTime;
77

88
use rustc_data_structures::fx::FxHashMap;
@@ -79,9 +79,9 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, '
7979
let this = self.eval_context_mut();
8080

8181
let path_scalar = this.read_scalar(path_op)?.not_undef()?;
82-
let path: PathBuf = this.read_os_str_from_c_str(path_scalar)?.into();
82+
let path = this.read_path_from_c_str(path_scalar)?.into_owned();
8383

84-
let metadata = match FileMetadata::from_path(this, path, follow_symlink)? {
84+
let metadata = match FileMetadata::from_path(this, &path, follow_symlink)? {
8585
Some(metadata) => metadata,
8686
None => return Ok(-1),
8787
};
@@ -303,7 +303,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
303303
throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
304304
}
305305

306-
let path = this.read_os_str_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
306+
let path = this.read_path_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
307307

308308
let fd = options.open(&path).map(|file| {
309309
let fh = &mut this.machine.file_handler;
@@ -524,10 +524,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
524524

525525
this.check_no_isolation("unlink")?;
526526

527-
let path = this.read_os_str_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
527+
let path = this.read_path_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
528528

529529
let result = remove_file(path).map(|_| 0);
530-
531530
this.try_unwrap_io_result(result)
532531
}
533532

@@ -537,12 +536,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
537536
linkpath_op: OpTy<'tcx, Tag>
538537
) -> InterpResult<'tcx, i32> {
539538
#[cfg(target_family = "unix")]
540-
fn create_link(src: PathBuf, dst: PathBuf) -> std::io::Result<()> {
539+
fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> {
541540
std::os::unix::fs::symlink(src, dst)
542541
}
543542

544543
#[cfg(target_family = "windows")]
545-
fn create_link(src: PathBuf, dst: PathBuf) -> std::io::Result<()> {
544+
fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> {
546545
use std::os::windows::fs;
547546
if src.is_dir() {
548547
fs::symlink_dir(src, dst)
@@ -555,10 +554,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
555554

556555
this.check_no_isolation("symlink")?;
557556

558-
let target = this.read_os_str_from_c_str(this.read_scalar(target_op)?.not_undef()?)?.into();
559-
let linkpath = this.read_os_str_from_c_str(this.read_scalar(linkpath_op)?.not_undef()?)?.into();
557+
let target = this.read_path_from_c_str(this.read_scalar(target_op)?.not_undef()?)?;
558+
let linkpath = this.read_path_from_c_str(this.read_scalar(linkpath_op)?.not_undef()?)?;
560559

561-
this.try_unwrap_io_result(create_link(target, linkpath).map(|_| 0))
560+
let result = create_link(&target, &linkpath).map(|_| 0);
561+
this.try_unwrap_io_result(result)
562562
}
563563

564564
fn macos_stat(
@@ -644,7 +644,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
644644
this.ref_to_mplace(statxbuf_imm)?
645645
};
646646

647-
let path: PathBuf = this.read_os_str_from_c_str(pathname_scalar)?.into();
647+
let path = this.read_path_from_c_str(pathname_scalar)?.into_owned();
648648
// `flags` should be a `c_int` but the `syscall` function provides an `isize`.
649649
let flags: i32 =
650650
this.read_scalar(flags_op)?.to_machine_isize(&*this.tcx)?.try_into().map_err(|e| {
@@ -691,7 +691,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
691691
let metadata = if path.as_os_str().is_empty() && empty_path_flag {
692692
FileMetadata::from_fd(this, dirfd)?
693693
} else {
694-
FileMetadata::from_path(this, path, follow_symlink)?
694+
FileMetadata::from_path(this, &path, follow_symlink)?
695695
};
696696
let metadata = match metadata {
697697
Some(metadata) => metadata,
@@ -785,8 +785,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
785785
return Ok(-1);
786786
}
787787

788-
let oldpath = this.read_os_str_from_c_str(oldpath_scalar)?;
789-
let newpath = this.read_os_str_from_c_str(newpath_scalar)?;
788+
let oldpath = this.read_path_from_c_str(oldpath_scalar)?;
789+
let newpath = this.read_path_from_c_str(newpath_scalar)?;
790790

791791
let result = rename(oldpath, newpath).map(|_| 0);
792792

@@ -808,7 +808,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
808808
this.read_scalar(mode_op)?.to_u32()?
809809
};
810810

811-
let path = this.read_os_str_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
811+
let path = this.read_path_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
812812

813813
let mut builder = DirBuilder::new();
814814

@@ -833,7 +833,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
833833

834834
this.check_no_isolation("rmdir")?;
835835

836-
let path = this.read_os_str_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
836+
let path = this.read_path_from_c_str(this.read_scalar(path_op)?.not_undef()?)?;
837837

838838
let result = remove_dir(path).map(|_| 0i32);
839839

@@ -845,7 +845,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
845845

846846
this.check_no_isolation("opendir")?;
847847

848-
let name = this.read_os_str_from_c_str(this.read_scalar(name_op)?.not_undef()?)?;
848+
let name = this.read_path_from_c_str(this.read_scalar(name_op)?.not_undef()?)?;
849849

850850
let result = read_dir(name);
851851

@@ -899,7 +899,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
899899
let entry_place = this.deref_operand(entry_op)?;
900900
let name_place = this.mplace_field(entry_place, 4)?;
901901

902-
let file_name = dir_entry.file_name();
902+
let file_name = dir_entry.file_name(); // not a Path as there are no separators!
903903
let (name_fits, _) = this.write_os_str_to_c_str(
904904
&file_name,
905905
name_place.ptr,
@@ -987,7 +987,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
987987
let entry_place = this.deref_operand(entry_op)?;
988988
let name_place = this.mplace_field(entry_place, 5)?;
989989

990-
let file_name = dir_entry.file_name();
990+
let file_name = dir_entry.file_name(); // not a Path as there are no separators!
991991
let (name_fits, file_name_len) = this.write_os_str_to_c_str(
992992
&file_name,
993993
name_place.ptr,
@@ -1082,7 +1082,7 @@ struct FileMetadata {
10821082
impl FileMetadata {
10831083
fn from_path<'tcx, 'mir>(
10841084
ecx: &mut MiriEvalContext<'mir, 'tcx>,
1085-
path: PathBuf,
1085+
path: &Path,
10861086
follow_symlink: bool
10871087
) -> InterpResult<'tcx, Option<FileMetadata>> {
10881088
let metadata = if follow_symlink {

tests/compiletest.rs

+2
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ fn get_target() -> String {
110110
fn test_runner(_tests: &[&()]) {
111111
// Add a test env var to do environment communication tests.
112112
std::env::set_var("MIRI_ENV_VAR_TEST", "0");
113+
// Let the tests know where to store temp files (they might run for a different target, which can make this hard to find).
114+
std::env::set_var("MIRI_TEMP", std::env::temp_dir());
113115

114116
let target = get_target();
115117
miri_pass("tests/run-pass", &target);

0 commit comments

Comments
 (0)