Auto merge of #1263 - RalfJung:shims-limit, r=RalfJung
Limit shims to libstd where possible Also organize them better by category. Fixes https://github.com/rust-lang/miri/issues/1181 (by making mmap not callable from user code)
This commit is contained in:
commit
f0afdc35d2
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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)?;
|
||||
}
|
||||
"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`).
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
||||
// We don't support threading.
|
||||
// Better error for attempts to create a thread
|
||||
"pthread_create" => {
|
||||
throw_unsup_format!("Miri does not support threading");
|
||||
}
|
||||
|
||||
// Stub out calls for condvar, mutex and rwlock, to just return `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)?;
|
||||
}
|
||||
"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)?;
|
||||
}
|
||||
|
||||
// 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),
|
||||
|
@ -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<K, V>).
|
||||
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),
|
||||
};
|
||||
|
||||
|
@ -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)?;
|
||||
}
|
||||
|
||||
"pthread_get_stacksize_np" => {
|
||||
let stack_size = Scalar::from_uint(STACK_SIZE, dest.layout.size);
|
||||
this.write_scalar(stack_size, dest)?;
|
||||
// Access to command-line arguments
|
||||
"_NSGetArgc" => {
|
||||
this.write_scalar(this.machine.argc.expect("machine must be initialized"), 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),
|
||||
|
@ -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),
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user