diff --git a/src/helpers.rs b/src/helpers.rs index aa327b468b..8e379065b0 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -81,6 +81,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Helper function to get a `libc` constant as an `i32`. fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> { + // TODO: Cache the result. self.eval_libc(name)?.to_i32() } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index ecf24e2f20..c92e338ef7 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -196,6 +196,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Here we dispatch all the shims for foreign functions. If you have a platform specific // shim, add it to the corresponding submodule. match link_name { + // Standard C allocation "malloc" => { let size = this.read_scalar(args[0])?.to_machine_usize(this)?; let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C); @@ -220,6 +221,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(res, dest)?; } + // Rust allocation + // (Usually these would be forwarded to to `#[global_allocator]`; we instead implement a generic + // allocation that also checks that all conditions are met, such as not permitting zero-sized allocations.) "__rust_alloc" => { let size = this.read_scalar(args[0])?.to_machine_usize(this)?; let align = this.read_scalar(args[1])?.to_machine_usize(this)?; @@ -274,6 +278,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(new_ptr, dest)?; } + // C memory handling functions "memcmp" => { let left = this.read_scalar(args[0])?.not_undef()?; let right = this.read_scalar(args[1])?.not_undef()?; @@ -293,7 +298,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_int(result, Size::from_bits(32)), dest)?; } - "memrchr" => { let ptr = this.read_scalar(args[0])?.not_undef()?; let val = this.read_scalar(args[1])?.to_i32()? as u8; @@ -311,7 +315,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } } - "memchr" => { let ptr = this.read_scalar(args[0])?.not_undef()?; let val = this.read_scalar(args[1])?.to_i32()? as u8; @@ -328,7 +331,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } } - "strlen" => { let ptr = this.read_scalar(args[0])?.not_undef()?; let n = this.memory.read_c_str(ptr)?.len(); @@ -358,11 +360,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?; } - // underscore case for windows | "_hypotf" | "hypotf" | "atan2f" => { + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) // FIXME: Using host floats. let f1 = f32::from_bits(this.read_scalar(args[0])?.to_u32()?); let f2 = f32::from_bits(this.read_scalar(args[1])?.to_u32()?); @@ -373,7 +376,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; this.write_scalar(Scalar::from_u32(n.to_bits()), dest)?; } - | "cbrt" | "cosh" | "sinh" @@ -396,8 +398,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?; } - // underscore case for windows, here and below - // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) | "_hypot" | "hypot" | "atan2" @@ -412,11 +412,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; this.write_scalar(Scalar::from_u64(n.to_bits()), dest)?; } - // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. | "_ldexp" | "ldexp" | "scalbn" => { + // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. let x = this.read_scalar(args[0])?.to_f64()?; let exp = this.read_scalar(args[1])?.to_i32()?; @@ -434,6 +434,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_f64(res), dest)?; } + // Target-specific shims _ => match this.tcx.sess.target.target.target_os.as_str() { "linux" | "macos" => return posix::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret), "windows" => return windows::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret), diff --git a/src/shims/foreign_items/posix.rs b/src/shims/foreign_items/posix.rs index 425fe4b1b4..ce23bc4c97 100644 --- a/src/shims/foreign_items/posix.rs +++ b/src/shims/foreign_items/posix.rs @@ -24,22 +24,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let result = this.getenv(args[0])?; this.write_scalar(result, dest)?; } - "unsetenv" => { let result = this.unsetenv(args[0])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "setenv" => { let result = this.setenv(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "getcwd" => { let result = this.getcwd(args[0], args[1])?; this.write_scalar(result, dest)?; } - "chdir" => { let result = this.chdir(args[0])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; @@ -50,17 +46,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let result = this.open(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "fcntl" => { let result = this.fcntl(args[0], args[1], args.get(2).cloned())?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "read" => { let result = this.read(args[0], args[1], args[2])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "write" => { let fd = this.read_scalar(args[0])?.to_i32()?; let buf = this.read_scalar(args[1])?.not_undef()?; @@ -94,43 +87,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Now, `result` is the value we return back to the program. this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "unlink" => { let result = this.unlink(args[0])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "symlink" => { let result = this.symlink(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "rename" => { let result = this.rename(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "mkdir" => { let result = this.mkdir(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "rmdir" => { let result = this.rmdir(args[0])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "closedir" => { let result = this.closedir(args[0])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "lseek" | "lseek64" => { let result = this.lseek64(args[0], args[1], args[2])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - // Other shims + // Allocation "posix_memalign" => { let ret = this.deref_operand(args[0])?; let align = this.read_scalar(args[1])?.to_machine_usize(this)?; @@ -159,6 +145,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } + // Dynamic symbol loading "dlsym" => { let _handle = this.read_scalar(args[0])?; let symbol = this.read_scalar(args[1])?.not_undef()?; @@ -173,7 +160,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - // Hook pthread calls that go to the thread-local storage memory subsystem. + // Querying system information + "sysconf" => { + let name = this.read_scalar(args[0])?.to_i32()?; + + let sysconfs = &[ + ("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, dest.layout.size)), + ("_SC_NPROCESSORS_ONLN", Scalar::from_int(NUM_CPUS, dest.layout.size)), + ]; + let mut result = None; + for &(sysconf_name, value) in sysconfs { + let sysconf_name = this.eval_libc_i32(sysconf_name)?; + if sysconf_name == name { + result = Some(value); + break; + } + } + if let Some(result) = result { + this.write_scalar(result, dest)?; + } else { + throw_unsup_format!("unimplemented sysconf name: {}", name) + } + } + + // Thread-local storage "pthread_key_create" => { let key_place = this.deref_operand(args[0])?; @@ -220,36 +230,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } - // Stack size/address stuff. - | "pthread_attr_init" - | "pthread_attr_destroy" - | "pthread_self" - | "pthread_attr_setstacksize" => { - this.write_null(dest)?; + // Better error for attempts to create a thread + "pthread_create" => { + throw_unsup_format!("Miri does not support threading"); } - "pthread_attr_getstack" => { - let addr_place = this.deref_operand(args[1])?; - let size_place = this.deref_operand(args[2])?; - this.write_scalar( - Scalar::from_uint(STACK_ADDR, addr_place.layout.size), - addr_place.into(), - )?; - this.write_scalar( - Scalar::from_uint(STACK_SIZE, size_place.layout.size), - size_place.into(), - )?; - - // Return success (`0`). + // Miscellaneous + "isatty" => { + let _fd = this.read_scalar(args[0])?.to_i32()?; + // "returns 1 if fd is an open file descriptor referring to a terminal; otherwise 0 is returned, and errno is set to indicate the error" + // FIXME: we just say nothing is a terminal. + let enotty = this.eval_libc("ENOTTY")?; + this.set_last_error(enotty)?; this.write_null(dest)?; } - - // We don't support threading. - "pthread_create" => { - throw_unsup_format!("Miri does not support threading"); + "pthread_atfork" => { + let _prepare = this.read_scalar(args[0])?.not_undef()?; + let _parent = this.read_scalar(args[1])?.not_undef()?; + let _child = this.read_scalar(args[1])?.not_undef()?; + // We do not support forking, so there is nothing to do here. + this.write_null(dest)?; } - // Stub out calls for condvar, mutex and rwlock, to just return `0`. + // Incomplete shims that we "stub out" just to get pre-main initialziation code to work. + // These shims are enabled only when the caller is in the standard library. + | "pthread_attr_init" + | "pthread_attr_destroy" + | "pthread_self" + | "pthread_attr_setstacksize" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { + this.write_null(dest)?; + } | "pthread_mutexattr_init" | "pthread_mutexattr_settype" | "pthread_mutex_init" @@ -265,68 +275,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "pthread_condattr_setclock" | "pthread_cond_init" | "pthread_condattr_destroy" - | "pthread_cond_destroy" + | "pthread_cond_destroy" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { this.write_null(dest)?; } - - // We don't support fork so we don't have to do anything for atfork. - "pthread_atfork" => { - this.write_null(dest)?; - } - - // Some things needed for `sys::thread` initialization to go through. | "signal" | "sigaction" | "sigaltstack" + | "mprotect" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - this.write_scalar(Scalar::from_int(0, dest.layout.size), dest)?; - } - - "sysconf" => { - let name = this.read_scalar(args[0])?.to_i32()?; - - trace!("sysconf() called with name {}", name); - // TODO: Cache the sysconf integers via Miri's global cache. - let sysconfs = &[ - ("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, dest.layout.size)), - ("_SC_GETPW_R_SIZE_MAX", Scalar::from_int(-1, dest.layout.size)), - ("_SC_NPROCESSORS_ONLN", Scalar::from_int(NUM_CPUS, dest.layout.size)), - ]; - let mut result = None; - for &(sysconf_name, value) in sysconfs { - let sysconf_name = this.eval_libc_i32(sysconf_name)?; - if sysconf_name == name { - result = Some(value); - break; - } - } - if let Some(result) = result { - this.write_scalar(result, dest)?; - } else { - throw_unsup_format!("unimplemented sysconf name: {}", name) - } - } - - "isatty" => { - this.write_null(dest)?; - } - - "posix_fadvise" => { - // fadvise is only informational, we can ignore it. - this.write_null(dest)?; - } - - "mmap" => { - // This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value. - let addr = this.read_scalar(args[0])?.not_undef()?; - this.write_scalar(addr, dest)?; - } - - "mprotect" => { this.write_null(dest)?; } + // Platform-specific shims _ => { match this.tcx.sess.target.target.target_os.as_str() { "linux" => return linux::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret), diff --git a/src/shims/foreign_items/posix/linux.rs b/src/shims/foreign_items/posix/linux.rs index 82928c9bc1..88a18e3e75 100644 --- a/src/shims/foreign_items/posix/linux.rs +++ b/src/shims/foreign_items/posix/linux.rs @@ -13,47 +13,65 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); match link_name { + // errno "__errno_location" => { let errno_place = this.machine.last_error.unwrap(); this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?; } // File related shims (but also see "syscall" below for statx) - - // The only reason this is not in the `posix` module is because the `macos` item has a - // different name. + // These symbols have different names on Linux and macOS, which is the only reason they are not + // in the `posix` module. "close" => { let result = this.close(args[0])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - - // The only reason this is not in the `posix` module is because the `macos` item has a - // different name. "opendir" => { let result = this.opendir(args[0])?; this.write_scalar(result, dest)?; } - - // The `macos` module has a parallel foreign item, `readdir_r`, which uses a different - // struct layout. "readdir64_r" => { let result = this.linux_readdir64_r(args[0], args[1], args[2])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } + // Linux-only + "posix_fadvise" => { + let _fd = this.read_scalar(args[0])?.to_i32()?; + let _offset = this.read_scalar(args[1])?.to_machine_isize(this)?; + let _len = this.read_scalar(args[2])?.to_machine_isize(this)?; + let _advice = this.read_scalar(args[3])?.to_i32()?; + // fadvise is only informational, we can ignore it. + this.write_null(dest)?; + } // Time related shims - - // This is a POSIX function but it has only been tested on linux. "clock_gettime" => { + // This is a POSIX function but it has only been tested on linux. let result = this.clock_gettime(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - // Other shims - "pthread_getattr_np" => { + // Querying system information + "pthread_attr_getstack" => { + // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here. + let _attr_place = this.deref_operand(args[0])?; + let addr_place = this.deref_operand(args[1])?; + let size_place = this.deref_operand(args[2])?; + + this.write_scalar( + Scalar::from_uint(STACK_ADDR, addr_place.layout.size), + addr_place.into(), + )?; + this.write_scalar( + Scalar::from_uint(STACK_SIZE, size_place.layout.size), + size_place.into(), + )?; + + // Return success (`0`). this.write_null(dest)?; } + // Dynamically invoked syscalls "syscall" => { let sys_getrandom = this .eval_libc("SYS_getrandom")? @@ -67,15 +85,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap). id if id == sys_getrandom => { - // The first argument is the syscall id, - // so skip over it. + // The first argument is the syscall id, so skip over it. getrandom(this, &args[1..], dest)?; } // `statx` is used by `libstd` to retrieve metadata information on `linux` // instead of using `stat`,`lstat` or `fstat` as on `macos`. id if id == sys_statx => { - // The first argument is the syscall id, - // so skip over it. + // The first argument is the syscall id, so skip over it. let result = this.linux_statx(args[1], args[2], args[3], args[4], args[5])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } @@ -83,15 +99,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } + // Miscelanneous "getrandom" => { getrandom(this, args, dest)?; } - "sched_getaffinity" => { - // Return an error; `num_cpus` then falls back to `sysconf`. + let _pid = this.read_scalar(args[0])?.to_i32()?; + let _cpusetsize = this.read_scalar(args[1])?.to_machine_usize(this)?; + let _mask = this.deref_operand(args[2])?; + // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`. + let einval = this.eval_libc("EINVAL")?; + this.set_last_error(einval)?; this.write_scalar(Scalar::from_int(-1, dest.layout.size), dest)?; } + // Incomplete shims that we "stub out" just to get pre-main initialziation code to work. + // These shims are enabled only when the caller is in the standard library. + "pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { + this.write_null(dest)?; + } + _ => throw_unsup_format!("can't call foreign function: {}", link_name), }; diff --git a/src/shims/foreign_items/posix/macos.rs b/src/shims/foreign_items/posix/macos.rs index 325be877d0..2261b1c4cf 100644 --- a/src/shims/foreign_items/posix/macos.rs +++ b/src/shims/foreign_items/posix/macos.rs @@ -13,44 +13,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); match link_name { + // errno "__error" => { let errno_place = this.machine.last_error.unwrap(); this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?; } // File related shims - - // The only reason this is not in the `posix` module is because the `linux` item has a - // different name. "close$NOCANCEL" => { let result = this.close(args[0])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "stat$INODE64" => { let result = this.macos_stat(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "lstat$INODE64" => { let result = this.macos_lstat(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - "fstat$INODE64" => { let result = this.macos_fstat(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } - - // The only reason this is not in the `posix` module is because the `linux` item has a - // different name. "opendir$INODE64" => { let result = this.opendir(args[0])?; this.write_scalar(result, dest)?; } - - // The `linux` module has a parallel foreign item, `readdir64_r`, which uses a - // different struct layout. "readdir_r$INODE64" => { let result = this.macos_readdir_r(args[0], args[1], args[2])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; @@ -71,21 +60,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_uint(result, dest.layout.size), dest)?; } - // Other shims - "pthread_attr_get_np" => { - this.write_null(dest)?; - } - - "pthread_get_stackaddr_np" => { - let stack_addr = Scalar::from_uint(STACK_ADDR, dest.layout.size); - this.write_scalar(stack_addr, dest)?; + // Access to command-line arguments + "_NSGetArgc" => { + this.write_scalar(this.machine.argc.expect("machine must be initialized"), dest)?; } - - "pthread_get_stacksize_np" => { - let stack_size = Scalar::from_uint(STACK_SIZE, dest.layout.size); - this.write_scalar(stack_size, dest)?; + "_NSGetArgv" => { + this.write_scalar(this.machine.argv.expect("machine must be initialized"), dest)?; } + // Thread-local storage "_tlv_atexit" => { let dtor = this.read_scalar(args[0])?.not_undef()?; let dtor = this.memory.get_fn(dtor)?.as_instance()?; @@ -93,12 +76,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.machine.tls.set_global_dtor(dtor, data)?; } - "_NSGetArgc" => { - this.write_scalar(this.machine.argc.expect("machine must be initialized"), dest)?; + // Querying system information + "pthread_get_stackaddr_np" => { + let _thread = this.read_scalar(args[0])?.not_undef()?; + let stack_addr = Scalar::from_uint(STACK_ADDR, dest.layout.size); + this.write_scalar(stack_addr, dest)?; + } + "pthread_get_stacksize_np" => { + let _thread = this.read_scalar(args[0])?.not_undef()?; + let stack_size = Scalar::from_uint(STACK_SIZE, dest.layout.size); + this.write_scalar(stack_size, dest)?; } - "_NSGetArgv" => { - this.write_scalar(this.machine.argv.expect("machine must be initialized"), dest)?; + // Incomplete shims that we "stub out" just to get pre-main initialziation code to work. + // These shims are enabled only when the caller is in the standard library. + "mmap" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { + // This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value. + let addr = this.read_scalar(args[0])?.not_undef()?; + this.write_scalar(addr, dest)?; } _ => throw_unsup_format!("can't call foreign function: {}", link_name), diff --git a/src/shims/foreign_items/windows.rs b/src/shims/foreign_items/windows.rs index 9e71ba7d90..9a626fe75e 100644 --- a/src/shims/foreign_items/windows.rs +++ b/src/shims/foreign_items/windows.rs @@ -14,12 +14,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); + // Windows API stubs. + // HANDLE = isize + // DWORD = ULONG = u32 + // BOOL = i32 match link_name { - // Windows API stubs. - // HANDLE = isize - // DWORD = ULONG = u32 - // BOOL = i32 - // Environment related shims "GetEnvironmentVariableW" => { let result = this.GetEnvironmentVariableW(args[0], args[1], args[2])?; @@ -42,6 +41,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } // File related shims + "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_machine_isize(this)?; let buf = this.read_scalar(args[1])?.not_undef()?; @@ -61,9 +66,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; res.ok().map(|n| n as u32) } else { - eprintln!("Miri: Ignored output to handle {}", handle); - // Pretend it all went well. - Some(n) + throw_unsup_format!("on Windows, writing to anything except stdout/stderr is not supported") }; // If there was no error, write back how much was written. if let Some(n) = written { @@ -76,11 +79,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx )?; } - // Other shims - "GetProcessHeap" => { - // Just fake a HANDLE - this.write_scalar(Scalar::from_int(1, this.pointer_size()), dest)?; - } + // Allocation "HeapAlloc" => { let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?; let flags = this.read_scalar(args[1])?.to_u32()?; @@ -105,6 +104,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(res, dest)?; } + // errno "SetLastError" => { this.set_last_error(this.read_scalar(args[0])?.not_undef()?)?; } @@ -113,30 +113,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(last_error, dest)?; } - "AddVectoredExceptionHandler" => { - // Any non zero value works for the stdlib. This is just used for stack overflows anyway. - this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?; - } - - | "InitializeCriticalSection" - | "EnterCriticalSection" - | "LeaveCriticalSection" - | "DeleteCriticalSection" - => { - // Nothing to do, not even a return value. - // (Windows locks are reentrant, and we have only 1 thread, - // so not doing any futher checks here is at least not incorrect.) - } - - | "GetModuleHandleW" - | "GetProcAddress" - | "GetConsoleScreenBufferInfo" - | "SetConsoleTextAttribute" - => { - // Pretend these do not exist / nothing happened, by returning zero. - this.write_null(dest)?; - } - + // Querying system information "GetSystemInfo" => { let system_info = this.deref_operand(args[0])?; // Initialize with `0`. @@ -150,6 +127,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_int(NUM_CPUS, dword_size), num_cpus.into())?; } + // Thread-local storage "TlsAlloc" => { // This just creates a key; Windows does not natively support TLS destructors. @@ -170,33 +148,72 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // 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)?; - } - "GetConsoleMode" => { - // Everything is a pipe. - this.write_null(dest)?; - } + + // Access to command-line arguments "GetCommandLineW" => { this.write_scalar( this.machine.cmd_line.expect("machine must be initialized"), dest, )?; } - // The actual name of 'RtlGenRandom' + + // Miscellaneous "SystemFunction036" => { + // The actual name of 'RtlGenRandom' let ptr = this.read_scalar(args[0])?.not_undef()?; let len = this.read_scalar(args[1])?.to_u32()?; this.gen_random(ptr, len.into())?; this.write_scalar(Scalar::from_bool(true), dest)?; } - // We don't support threading. + "GetConsoleScreenBufferInfo" => { + // `term` needs this, so we fake it. + let _console = this.read_scalar(args[0])?.to_machine_isize(this)?; + let _buffer_info = this.deref_operand(args[1])?; + // Indicate an error. + // FIXME: we should set last_error, but to what? + this.write_null(dest)?; + } + "GetConsoleMode" => { + // Windows "isatty" (in libtest) needs this, so we fake it. + let _console = this.read_scalar(args[0])?.to_machine_isize(this)?; + let _mode = this.deref_operand(args[1])?; + // Indicate an error. + // FIXME: we should set last_error, but to what? + this.write_null(dest)?; + } + + // Better error for attempts to create a thread "CreateThread" => { throw_unsup_format!("Miri does not support threading"); } + + // Incomplete shims that we "stub out" just to get pre-main initialziation code to work. + // These shims are enabled only when the caller is in the standard library. + "GetProcessHeap" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { + // Just fake a HANDLE + this.write_scalar(Scalar::from_int(1, this.pointer_size()), dest)?; + } + | "GetModuleHandleW" + | "GetProcAddress" + | "SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::") + => { + // Pretend these do not exist / nothing happened, by returning zero. + this.write_null(dest)?; + } + "AddVectoredExceptionHandler" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { + // Any non zero value works for the stdlib. This is just used for stack overflows anyway. + this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?; + } + | "InitializeCriticalSection" + | "EnterCriticalSection" + | "LeaveCriticalSection" + | "DeleteCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::") + => { + // Nothing to do, not even a return value. + // (Windows locks are reentrant, and we have only 1 thread, + // so not doing any futher checks here is at least not incorrect.) + } + _ => throw_unsup_format!("can't call foreign function: {}", link_name), }