Auto merge of #1298 - toc-the-younger:check-arg-count, r=RalfJung
Check that shims are called with the correct number of arguments Fixes #1272 Some shims don't use all their arguments, I've assumed they are being called in tests with the correct number of arguments here.
This commit is contained in:
commit
2f974f6e1f
15 changed files with 479 additions and 251 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::mem;
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
|
|
@ -471,6 +471,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
}
|
||||
|
||||
/// Check that the number of args is what we expect.
|
||||
pub fn check_arg_count<'a, 'tcx, const N: usize>(args: &'a [OpTy<'tcx, Tag>]) -> InterpResult<'tcx, &'a [OpTy<'tcx, Tag>; N]>
|
||||
where &'a [OpTy<'tcx, Tag>; N]: TryFrom<&'a [OpTy<'tcx, Tag>]> {
|
||||
if let Ok(ops) = args.try_into() {
|
||||
return Ok(ops);
|
||||
}
|
||||
throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N)
|
||||
}
|
||||
|
||||
pub fn immty_from_int_checked<'tcx>(
|
||||
int: impl Into<i128>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
#![warn(rust_2018_idioms)]
|
||||
#![allow(clippy::cast_lossless)]
|
||||
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(const_generics)]
|
||||
|
||||
extern crate rustc_apfloat;
|
||||
extern crate rustc_ast;
|
||||
#[macro_use] extern crate rustc_middle;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use rustc_middle::mir;
|
||||
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Dlsym {
|
||||
|
|
@ -35,8 +36,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
match dlsym {
|
||||
GetEntropy => {
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let len = this.read_scalar(args[1])?.to_machine_usize(this)?;
|
||||
let &[ptr, len] = check_arg_count(args)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let len = this.read_scalar(len)?.to_machine_usize(this)?;
|
||||
this.gen_random(ptr, len)?;
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use rustc_span::symbol::sym;
|
|||
use rustc_ast::attr;
|
||||
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
|
|
@ -139,8 +140,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "exit"
|
||||
| "ExitProcess"
|
||||
=> {
|
||||
let &[code] = check_arg_count(args)?;
|
||||
// it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway
|
||||
let code = this.read_scalar(args[0])?.to_i32()?;
|
||||
let code = this.read_scalar(code)?.to_i32()?;
|
||||
throw_machine_stop!(TerminationInfo::Exit(code.into()));
|
||||
}
|
||||
_ => throw_unsup_format!("can't call (diverging) foreign function: {}", link_name),
|
||||
|
|
@ -197,25 +199,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
match link_name {
|
||||
// Standard C allocation
|
||||
"malloc" => {
|
||||
let size = this.read_scalar(args[0])?.to_machine_usize(this)?;
|
||||
let &[size] = check_arg_count(args)?;
|
||||
let size = this.read_scalar(size)?.to_machine_usize(this)?;
|
||||
let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"calloc" => {
|
||||
let items = this.read_scalar(args[0])?.to_machine_usize(this)?;
|
||||
let len = this.read_scalar(args[1])?.to_machine_usize(this)?;
|
||||
let &[items, len] = check_arg_count(args)?;
|
||||
let items = this.read_scalar(items)?.to_machine_usize(this)?;
|
||||
let len = this.read_scalar(len)?.to_machine_usize(this)?;
|
||||
let size =
|
||||
items.checked_mul(len).ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?;
|
||||
let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"free" => {
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let &[ptr] = check_arg_count(args)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
this.free(ptr, MiriMemoryKind::C)?;
|
||||
}
|
||||
"realloc" => {
|
||||
let old_ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let new_size = this.read_scalar(args[1])?.to_machine_usize(this)?;
|
||||
let &[old_ptr, new_size] = check_arg_count(args)?;
|
||||
let old_ptr = this.read_scalar(old_ptr)?.not_undef()?;
|
||||
let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?;
|
||||
let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
|
@ -224,8 +230,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// (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)?;
|
||||
let &[size, align] = check_arg_count(args)?;
|
||||
let size = this.read_scalar(size)?.to_machine_usize(this)?;
|
||||
let align = this.read_scalar(align)?.to_machine_usize(this)?;
|
||||
Self::check_alloc_request(size, align)?;
|
||||
let ptr = this.memory.allocate(
|
||||
Size::from_bytes(size),
|
||||
|
|
@ -235,8 +242,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
this.write_scalar(ptr, dest)?;
|
||||
}
|
||||
"__rust_alloc_zeroed" => {
|
||||
let size = this.read_scalar(args[0])?.to_machine_usize(this)?;
|
||||
let align = this.read_scalar(args[1])?.to_machine_usize(this)?;
|
||||
let &[size, align] = check_arg_count(args)?;
|
||||
let size = this.read_scalar(size)?.to_machine_usize(this)?;
|
||||
let align = this.read_scalar(align)?.to_machine_usize(this)?;
|
||||
Self::check_alloc_request(size, align)?;
|
||||
let ptr = this.memory.allocate(
|
||||
Size::from_bytes(size),
|
||||
|
|
@ -248,9 +256,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
this.write_scalar(ptr, dest)?;
|
||||
}
|
||||
"__rust_dealloc" => {
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let old_size = this.read_scalar(args[1])?.to_machine_usize(this)?;
|
||||
let align = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||
let &[ptr, old_size, align] = check_arg_count(args)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?;
|
||||
let align = this.read_scalar(align)?.to_machine_usize(this)?;
|
||||
// No need to check old_size/align; we anyway check that they match the allocation.
|
||||
let ptr = this.force_ptr(ptr)?;
|
||||
this.memory.deallocate(
|
||||
|
|
@ -260,12 +269,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
)?;
|
||||
}
|
||||
"__rust_realloc" => {
|
||||
let old_size = this.read_scalar(args[1])?.to_machine_usize(this)?;
|
||||
let align = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||
let new_size = this.read_scalar(args[3])?.to_machine_usize(this)?;
|
||||
let &[ptr, old_size, align, new_size] = check_arg_count(args)?;
|
||||
let ptr = this.force_ptr(this.read_scalar(ptr)?.not_undef()?)?;
|
||||
let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?;
|
||||
let align = this.read_scalar(align)?.to_machine_usize(this)?;
|
||||
let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?;
|
||||
Self::check_alloc_request(new_size, align)?;
|
||||
// No need to check old_size; we anyway check that they match the allocation.
|
||||
let ptr = this.force_ptr(this.read_scalar(args[0])?.not_undef()?)?;
|
||||
let align = Align::from_bytes(align).unwrap();
|
||||
let new_ptr = this.memory.reallocate(
|
||||
ptr,
|
||||
|
|
@ -279,9 +289,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// C memory handling functions
|
||||
"memcmp" => {
|
||||
let left = this.read_scalar(args[0])?.not_undef()?;
|
||||
let right = this.read_scalar(args[1])?.not_undef()?;
|
||||
let n = Size::from_bytes(this.read_scalar(args[2])?.to_machine_usize(this)?);
|
||||
let &[left, right, n] = check_arg_count(args)?;
|
||||
let left = this.read_scalar(left)?.not_undef()?;
|
||||
let right = this.read_scalar(right)?.not_undef()?;
|
||||
let n = Size::from_bytes(this.read_scalar(n)?.to_machine_usize(this)?);
|
||||
|
||||
let result = {
|
||||
let left_bytes = this.memory.read_bytes(left, n)?;
|
||||
|
|
@ -298,9 +309,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"memrchr" => {
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let val = this.read_scalar(args[1])?.to_i32()? as u8;
|
||||
let num = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||
let &[ptr, val, num] = check_arg_count(args)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let val = this.read_scalar(val)?.to_i32()? as u8;
|
||||
let num = this.read_scalar(num)?.to_machine_usize(this)?;
|
||||
if let Some(idx) = this
|
||||
.memory
|
||||
.read_bytes(ptr, Size::from_bytes(num))?
|
||||
|
|
@ -315,9 +327,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
}
|
||||
"memchr" => {
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let val = this.read_scalar(args[1])?.to_i32()? as u8;
|
||||
let num = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||
let &[ptr, val, num] = check_arg_count(args)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let val = this.read_scalar(val)?.to_i32()? as u8;
|
||||
let num = this.read_scalar(num)?.to_machine_usize(this)?;
|
||||
let idx = this
|
||||
.memory
|
||||
.read_bytes(ptr, Size::from_bytes(num))?
|
||||
|
|
@ -331,7 +344,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
}
|
||||
"strlen" => {
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let &[ptr] = check_arg_count(args)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let n = this.memory.read_c_str(ptr)?.len();
|
||||
this.write_scalar(Scalar::from_machine_usize(u64::try_from(n).unwrap(), this), dest)?;
|
||||
}
|
||||
|
|
@ -345,8 +359,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "asinf"
|
||||
| "atanf"
|
||||
=> {
|
||||
let &[f] = check_arg_count(args)?;
|
||||
// FIXME: Using host floats.
|
||||
let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?);
|
||||
let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
|
||||
let f = match link_name {
|
||||
"cbrtf" => f.cbrt(),
|
||||
"coshf" => f.cosh(),
|
||||
|
|
@ -363,11 +378,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "hypotf"
|
||||
| "atan2f"
|
||||
=> {
|
||||
let &[f1, f2] = check_arg_count(args)?;
|
||||
// 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()?);
|
||||
let f1 = f32::from_bits(this.read_scalar(f1)?.to_u32()?);
|
||||
let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?);
|
||||
let n = match link_name {
|
||||
"_hypotf" | "hypotf" => f1.hypot(f2),
|
||||
"atan2f" => f1.atan2(f2),
|
||||
|
|
@ -383,8 +399,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "asin"
|
||||
| "atan"
|
||||
=> {
|
||||
let &[f] = check_arg_count(args)?;
|
||||
// FIXME: Using host floats.
|
||||
let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
|
||||
let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
|
||||
let f = match link_name {
|
||||
"cbrt" => f.cbrt(),
|
||||
"cosh" => f.cosh(),
|
||||
|
|
@ -401,9 +418,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "hypot"
|
||||
| "atan2"
|
||||
=> {
|
||||
let &[f1, f2] = check_arg_count(args)?;
|
||||
// FIXME: Using host floats.
|
||||
let f1 = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
|
||||
let f2 = f64::from_bits(this.read_scalar(args[1])?.to_u64()?);
|
||||
let f1 = f64::from_bits(this.read_scalar(f1)?.to_u64()?);
|
||||
let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?);
|
||||
let n = match link_name {
|
||||
"_hypot" | "hypot" => f1.hypot(f2),
|
||||
"atan2" => f1.atan2(f2),
|
||||
|
|
@ -415,9 +433,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "ldexp"
|
||||
| "scalbn"
|
||||
=> {
|
||||
let &[x, exp] = check_arg_count(args)?;
|
||||
// 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()?;
|
||||
let x = this.read_scalar(x)?.to_f64()?;
|
||||
let exp = this.read_scalar(exp)?.to_i32()?;
|
||||
|
||||
// Saturating cast to i16. Even those are outside the valid exponent range to
|
||||
// `scalbn` below will do its over/underflow handling.
|
||||
|
|
@ -435,6 +454,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Architecture-specific shims
|
||||
"llvm.x86.sse2.pause" if this.tcx.sess.target.target.arch == "x86" || this.tcx.sess.target.target.arch == "x86_64" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
this.sched_yield()?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use std::convert::TryFrom;
|
|||
use log::trace;
|
||||
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
use rustc_middle::mir;
|
||||
use rustc_target::abi::{Align, LayoutOf, Size};
|
||||
|
||||
|
|
@ -23,43 +24,51 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
match link_name {
|
||||
// Environment related shims
|
||||
"getenv" => {
|
||||
let result = this.getenv(args[0])?;
|
||||
let &[name] = check_arg_count(args)?;
|
||||
let result = this.getenv(name)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"unsetenv" => {
|
||||
let result = this.unsetenv(args[0])?;
|
||||
let &[name] = check_arg_count(args)?;
|
||||
let result = this.unsetenv(name)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"setenv" => {
|
||||
let result = this.setenv(args[0], args[1])?;
|
||||
let &[name, value, _overwrite] = check_arg_count(args)?;
|
||||
let result = this.setenv(name, value)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"getcwd" => {
|
||||
let result = this.getcwd(args[0], args[1])?;
|
||||
let &[buf, size] = check_arg_count(args)?;
|
||||
let result = this.getcwd(buf, size)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"chdir" => {
|
||||
let result = this.chdir(args[0])?;
|
||||
let &[path] = check_arg_count(args)?;
|
||||
let result = this.chdir(path)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
|
||||
// File related shims
|
||||
"open" | "open64" => {
|
||||
let result = this.open(args[0], args[1])?;
|
||||
let &[path, flag, _mode] = check_arg_count(args)?;
|
||||
let result = this.open(path, flag)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"fcntl" => {
|
||||
let result = this.fcntl(args[0], args[1], args.get(2).cloned())?;
|
||||
let result = this.fcntl(args)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"read" => {
|
||||
let result = this.read(args[0], args[1], args[2])?;
|
||||
let &[fd, buf, count] = check_arg_count(args)?;
|
||||
let result = this.read(fd, buf, count)?;
|
||||
this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
|
||||
}
|
||||
"write" => {
|
||||
let fd = this.read_scalar(args[0])?.to_i32()?;
|
||||
let buf = this.read_scalar(args[1])?.not_undef()?;
|
||||
let n = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||
let &[fd, buf, n] = check_arg_count(args)?;
|
||||
let fd = this.read_scalar(fd)?.to_i32()?;
|
||||
let buf = this.read_scalar(buf)?.not_undef()?;
|
||||
let n = this.read_scalar(n)?.to_machine_usize(this)?;
|
||||
trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
|
||||
let result = if fd == 1 || fd == 2 {
|
||||
// stdout/stderr
|
||||
|
|
@ -84,46 +93,55 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
Err(_) => -1,
|
||||
}
|
||||
} else {
|
||||
this.write(args[0], args[1], args[2])?
|
||||
let &[fd, buf, count] = check_arg_count(args)?;
|
||||
this.write(fd, buf, count)?
|
||||
};
|
||||
// Now, `result` is the value we return back to the program.
|
||||
this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
|
||||
}
|
||||
"unlink" => {
|
||||
let result = this.unlink(args[0])?;
|
||||
let &[path] = check_arg_count(args)?;
|
||||
let result = this.unlink(path)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"symlink" => {
|
||||
let result = this.symlink(args[0], args[1])?;
|
||||
let &[target, linkpath] = check_arg_count(args)?;
|
||||
let result = this.symlink(target, linkpath)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"rename" => {
|
||||
let result = this.rename(args[0], args[1])?;
|
||||
let &[oldpath, newpath] = check_arg_count(args)?;
|
||||
let result = this.rename(oldpath, newpath)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"mkdir" => {
|
||||
let result = this.mkdir(args[0], args[1])?;
|
||||
let &[path, mode] = check_arg_count(args)?;
|
||||
let result = this.mkdir(path, mode)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"rmdir" => {
|
||||
let result = this.rmdir(args[0])?;
|
||||
let &[path] = check_arg_count(args)?;
|
||||
let result = this.rmdir(path)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"closedir" => {
|
||||
let result = this.closedir(args[0])?;
|
||||
let &[dirp] = check_arg_count(args)?;
|
||||
let result = this.closedir(dirp)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"lseek" | "lseek64" => {
|
||||
let result = this.lseek64(args[0], args[1], args[2])?;
|
||||
let &[fd, offset, whence] = check_arg_count(args)?;
|
||||
let result = this.lseek64(fd, offset, whence)?;
|
||||
// "lseek" is only used on macOS which is 64bit-only, so `i64` always works.
|
||||
this.write_scalar(Scalar::from_i64(result), dest)?;
|
||||
}
|
||||
|
||||
// Allocation
|
||||
"posix_memalign" => {
|
||||
let ret = this.deref_operand(args[0])?;
|
||||
let align = this.read_scalar(args[1])?.to_machine_usize(this)?;
|
||||
let size = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||
let &[ret, align, size] = check_arg_count(args)?;
|
||||
let ret = this.deref_operand(ret)?;
|
||||
let align = this.read_scalar(align)?.to_machine_usize(this)?;
|
||||
let size = this.read_scalar(size)?.to_machine_usize(this)?;
|
||||
// Align must be power of 2, and also at least ptr-sized (POSIX rules).
|
||||
if !align.is_power_of_two() {
|
||||
throw_ub_format!("posix_memalign: alignment must be a power of two, but is {}", align);
|
||||
|
|
@ -150,8 +168,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Dynamic symbol loading
|
||||
"dlsym" => {
|
||||
let _handle = this.read_scalar(args[0])?;
|
||||
let symbol = this.read_scalar(args[1])?.not_undef()?;
|
||||
let &[handle, symbol] = check_arg_count(args)?;
|
||||
this.read_scalar(handle)?.not_undef()?;
|
||||
let symbol = this.read_scalar(symbol)?.not_undef()?;
|
||||
let symbol_name = this.memory.read_c_str(symbol)?;
|
||||
let err = format!("bad c unicode symbol: {:?}", symbol_name);
|
||||
let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
|
||||
|
|
@ -165,7 +184,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Querying system information
|
||||
"sysconf" => {
|
||||
let name = this.read_scalar(args[0])?.to_i32()?;
|
||||
let &[name] = check_arg_count(args)?;
|
||||
let name = this.read_scalar(name)?.to_i32()?;
|
||||
|
||||
let sysconfs = &[
|
||||
("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, this.pointer_size())),
|
||||
|
|
@ -188,17 +208,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Thread-local storage
|
||||
"pthread_key_create" => {
|
||||
let key_place = this.deref_operand(args[0])?;
|
||||
let &[key, dtor] = check_arg_count(args)?;
|
||||
let key_place = this.deref_operand(key)?;
|
||||
let dtor = this.read_scalar(dtor)?.not_undef()?;
|
||||
|
||||
// Extract the function type out of the signature (that seems easier than constructing it ourselves).
|
||||
let dtor = match this.test_null(this.read_scalar(args[1])?.not_undef()?)? {
|
||||
let dtor = match this.test_null(dtor)? {
|
||||
Some(dtor_ptr) => Some(this.memory.get_fn(dtor_ptr)?.as_instance()?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
// Figure out how large a pthread TLS key actually is.
|
||||
// To this end, deref the argument type. This is `libc::pthread_key_t`.
|
||||
let key_type = args[0].layout.ty
|
||||
let key_type = key.layout.ty
|
||||
.builtin_deref(true)
|
||||
.ok_or_else(|| err_ub_format!(
|
||||
"wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
|
||||
|
|
@ -214,21 +236,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
this.write_null(dest)?;
|
||||
}
|
||||
"pthread_key_delete" => {
|
||||
let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
|
||||
let &[key] = check_arg_count(args)?;
|
||||
let key = this.force_bits(this.read_scalar(key)?.not_undef()?, key.layout.size)?;
|
||||
this.machine.tls.delete_tls_key(key)?;
|
||||
// Return success (0)
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"pthread_getspecific" => {
|
||||
let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
|
||||
let &[key] = check_arg_count(args)?;
|
||||
let key = this.force_bits(this.read_scalar(key)?.not_undef()?, key.layout.size)?;
|
||||
let active_thread = this.get_active_thread()?;
|
||||
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
|
||||
this.write_scalar(ptr, dest)?;
|
||||
}
|
||||
"pthread_setspecific" => {
|
||||
let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
|
||||
let &[key, new_ptr] = check_arg_count(args)?;
|
||||
let key = this.force_bits(this.read_scalar(key)?.not_undef()?, key.layout.size)?;
|
||||
let active_thread = this.get_active_thread()?;
|
||||
let new_ptr = this.read_scalar(args[1])?.not_undef()?;
|
||||
let new_ptr = this.read_scalar(new_ptr)?.not_undef()?;
|
||||
this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
|
||||
|
||||
// Return success (`0`).
|
||||
|
|
@ -237,91 +262,106 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Synchronization primitives
|
||||
"pthread_mutexattr_init" => {
|
||||
let result = this.pthread_mutexattr_init(args[0])?;
|
||||
let &[attr] = check_arg_count(args)?;
|
||||
let result = this.pthread_mutexattr_init(attr)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_mutexattr_settype" => {
|
||||
let result = this.pthread_mutexattr_settype(args[0], args[1])?;
|
||||
let &[attr, kind] = check_arg_count(args)?;
|
||||
let result = this.pthread_mutexattr_settype(attr, kind)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_mutexattr_destroy" => {
|
||||
let result = this.pthread_mutexattr_destroy(args[0])?;
|
||||
let &[attr] = check_arg_count(args)?;
|
||||
let result = this.pthread_mutexattr_destroy(attr)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_mutex_init" => {
|
||||
let result = this.pthread_mutex_init(args[0], args[1])?;
|
||||
let &[mutex, attr] = check_arg_count(args)?;
|
||||
let result = this.pthread_mutex_init(mutex, attr)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_mutex_lock" => {
|
||||
let result = this.pthread_mutex_lock(args[0])?;
|
||||
let &[mutex] = check_arg_count(args)?;
|
||||
let result = this.pthread_mutex_lock(mutex)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_mutex_trylock" => {
|
||||
let result = this.pthread_mutex_trylock(args[0])?;
|
||||
let &[mutex] = check_arg_count(args)?;
|
||||
let result = this.pthread_mutex_trylock(mutex)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_mutex_unlock" => {
|
||||
let result = this.pthread_mutex_unlock(args[0])?;
|
||||
let &[mutex] = check_arg_count(args)?;
|
||||
let result = this.pthread_mutex_unlock(mutex)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_mutex_destroy" => {
|
||||
let result = this.pthread_mutex_destroy(args[0])?;
|
||||
let &[mutex] = check_arg_count(args)?;
|
||||
let result = this.pthread_mutex_destroy(mutex)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_rwlock_rdlock" => {
|
||||
let result = this.pthread_rwlock_rdlock(args[0])?;
|
||||
let &[rwlock] = check_arg_count(args)?;
|
||||
let result = this.pthread_rwlock_rdlock(rwlock)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_rwlock_tryrdlock" => {
|
||||
let result = this.pthread_rwlock_tryrdlock(args[0])?;
|
||||
let &[rwlock] = check_arg_count(args)?;
|
||||
let result = this.pthread_rwlock_tryrdlock(rwlock)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_rwlock_wrlock" => {
|
||||
let result = this.pthread_rwlock_wrlock(args[0])?;
|
||||
let &[rwlock] = check_arg_count(args)?;
|
||||
let result = this.pthread_rwlock_wrlock(rwlock)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_rwlock_trywrlock" => {
|
||||
let result = this.pthread_rwlock_trywrlock(args[0])?;
|
||||
let &[rwlock] = check_arg_count(args)?;
|
||||
let result = this.pthread_rwlock_trywrlock(rwlock)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_rwlock_unlock" => {
|
||||
let result = this.pthread_rwlock_unlock(args[0])?;
|
||||
let &[rwlock] = check_arg_count(args)?;
|
||||
let result = this.pthread_rwlock_unlock(rwlock)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_rwlock_destroy" => {
|
||||
let result = this.pthread_rwlock_destroy(args[0])?;
|
||||
let &[rwlock] = check_arg_count(args)?;
|
||||
let result = this.pthread_rwlock_destroy(rwlock)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
|
||||
// Threading
|
||||
"pthread_create" => {
|
||||
assert_eq!(args.len(), 4);
|
||||
let result = this.pthread_create(args[0], args[1], args[2], args[3])?;
|
||||
let &[thread, attr, start, arg] = check_arg_count(args)?;
|
||||
let result = this.pthread_create(thread, attr, start, arg)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_join" => {
|
||||
assert_eq!(args.len(), 2);
|
||||
let result = this.pthread_join(args[0], args[1])?;
|
||||
let &[thread, retval] = check_arg_count(args)?;
|
||||
let result = this.pthread_join(thread, retval)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_detach" => {
|
||||
assert_eq!(args.len(), 1);
|
||||
let result = this.pthread_detach(args[0])?;
|
||||
let &[thread] = check_arg_count(args)?;
|
||||
let result = this.pthread_detach(thread)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"pthread_self" => {
|
||||
assert_eq!(args.len(), 0);
|
||||
let &[] = check_arg_count(args)?;
|
||||
this.pthread_self(dest)?;
|
||||
}
|
||||
"sched_yield" => {
|
||||
assert_eq!(args.len(), 0);
|
||||
let &[] = check_arg_count(args)?;
|
||||
let result = this.sched_yield()?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
|
||||
// Miscellaneous
|
||||
"isatty" => {
|
||||
let _fd = this.read_scalar(args[0])?.to_i32()?;
|
||||
let &[fd] = check_arg_count(args)?;
|
||||
this.read_scalar(fd)?.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")?;
|
||||
|
|
@ -329,9 +369,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
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()?;
|
||||
let &[prepare, parent, child] = check_arg_count(args)?;
|
||||
this.read_scalar(prepare)?.not_undef()?;
|
||||
this.read_scalar(parent)?.not_undef()?;
|
||||
this.read_scalar(child)?.not_undef()?;
|
||||
// We do not support forking, so there is nothing to do here.
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
|
@ -351,7 +392,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
"pthread_attr_getguardsize" if this.frame().instance.to_string().starts_with("std::sys::unix::")
|
||||
=> {
|
||||
let guard_size = this.deref_operand(args[1])?;
|
||||
let &[_attr, guard_size] = check_arg_count(args)?;
|
||||
let guard_size = this.deref_operand(guard_size)?;
|
||||
let guard_size_layout = this.libc_ty_layout("size_t")?;
|
||||
this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), guard_size.into())?;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
use rustc_middle::mir;
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
|
|
@ -15,6 +16,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
match link_name {
|
||||
// errno
|
||||
"__errno_location" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
let errno_place = this.machine.last_error.unwrap();
|
||||
this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
|
||||
}
|
||||
|
|
@ -23,27 +25,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// 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])?;
|
||||
let &[fd] = check_arg_count(args)?;
|
||||
let result = this.close(fd)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"opendir" => {
|
||||
let result = this.opendir(args[0])?;
|
||||
let &[name] = check_arg_count(args)?;
|
||||
let result = this.opendir(name)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"readdir64_r" => {
|
||||
let result = this.linux_readdir64_r(args[0], args[1], args[2])?;
|
||||
let &[dirp, entry, result] = check_arg_count(args)?;
|
||||
let result = this.linux_readdir64_r(dirp, entry, result)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"ftruncate64" => {
|
||||
let result = this.ftruncate64(args[0], args[1])?;
|
||||
let &[fd, length] = check_arg_count(args)?;
|
||||
let result = this.ftruncate64(fd, length)?;
|
||||
this.write_scalar(Scalar::from_i32(result), 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()?;
|
||||
let &[fd, offset, len, advice] = check_arg_count(args)?;
|
||||
this.read_scalar(fd)?.to_i32()?;
|
||||
this.read_scalar(offset)?.to_machine_isize(this)?;
|
||||
this.read_scalar(len)?.to_machine_isize(this)?;
|
||||
this.read_scalar(advice)?.to_i32()?;
|
||||
// fadvise is only informational, we can ignore it.
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
|
@ -51,16 +58,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// Time related shims
|
||||
"clock_gettime" => {
|
||||
// This is a POSIX function but it has only been tested on linux.
|
||||
let result = this.clock_gettime(args[0], args[1])?;
|
||||
let &[clk_id, tp] = check_arg_count(args)?;
|
||||
let result = this.clock_gettime(clk_id, tp)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
|
||||
// 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])?;
|
||||
let &[attr_place, addr_place, size_place] = check_arg_count(args)?;
|
||||
this.deref_operand(attr_place)?;
|
||||
let addr_place = this.deref_operand(addr_place)?;
|
||||
let size_place = this.deref_operand(size_place)?;
|
||||
|
||||
this.write_scalar(
|
||||
Scalar::from_uint(STACK_ADDR, this.pointer_size()),
|
||||
|
|
@ -77,8 +86,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Threading
|
||||
"prctl" => {
|
||||
assert_eq!(args.len(), 5);
|
||||
let result = this.prctl(args[0], args[1], args[2], args[3], args[4])?;
|
||||
let &[option, arg2, arg3, arg4, arg5] = check_arg_count(args)?;
|
||||
let result = this.prctl(option, arg2, arg3, arg4, arg5)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
|
||||
|
|
@ -92,18 +101,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
.eval_libc("SYS_statx")?
|
||||
.to_machine_usize(this)?;
|
||||
|
||||
if args.is_empty() {
|
||||
throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
|
||||
}
|
||||
match this.read_scalar(args[0])?.to_machine_usize(this)? {
|
||||
// `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.
|
||||
getrandom(this, &args[1..], dest)?;
|
||||
let &[_, ptr, len, flags] = check_arg_count(args)?;
|
||||
getrandom(this, ptr, len, flags, 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.
|
||||
let result = this.linux_statx(args[1], args[2], args[3], args[4], args[5])?;
|
||||
let &[_, dirfd, pathname, flags, mask, statxbuf] = check_arg_count(args)?;
|
||||
let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
|
||||
this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
|
||||
}
|
||||
id => throw_unsup_format!("miri does not support syscall ID {}", id),
|
||||
|
|
@ -112,12 +126,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Miscelanneous
|
||||
"getrandom" => {
|
||||
getrandom(this, args, dest)?;
|
||||
let &[ptr, len, flags] = check_arg_count(args)?;
|
||||
getrandom(this, ptr, len, flags, dest)?;
|
||||
}
|
||||
"sched_getaffinity" => {
|
||||
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])?;
|
||||
let &[pid, cpusetsize, mask] = check_arg_count(args)?;
|
||||
this.read_scalar(pid)?.to_i32()?;
|
||||
this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
|
||||
this.deref_operand(mask)?;
|
||||
// FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
|
||||
let einval = this.eval_libc("EINVAL")?;
|
||||
this.set_last_error(einval)?;
|
||||
|
|
@ -127,6 +143,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// Incomplete shims that we "stub out" just to get pre-main initialization 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::") => {
|
||||
let &[_thread, _attr] = check_arg_count(args)?;
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
||||
|
|
@ -140,15 +157,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// Shims the linux `getrandom` syscall.
|
||||
fn getrandom<'tcx>(
|
||||
this: &mut MiriEvalContext<'_, 'tcx>,
|
||||
args: &[OpTy<'tcx, Tag>],
|
||||
ptr: OpTy<'tcx, Tag>,
|
||||
len: OpTy<'tcx, Tag>,
|
||||
flags: OpTy<'tcx, Tag>,
|
||||
dest: PlaceTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let len = this.read_scalar(args[1])?.to_machine_usize(this)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let len = this.read_scalar(len)?.to_machine_usize(this)?;
|
||||
|
||||
// The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
|
||||
// neither of which have any effect on our current PRNG.
|
||||
let _flags = this.read_scalar(args[2])?.to_i32()?;
|
||||
this.read_scalar(flags)?.to_i32()?;
|
||||
|
||||
this.gen_random(ptr, len)?;
|
||||
this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
use rustc_middle::mir;
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
|
|
@ -15,100 +16,119 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
match link_name {
|
||||
// errno
|
||||
"__error" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
let errno_place = this.machine.last_error.unwrap();
|
||||
this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
|
||||
}
|
||||
|
||||
// File related shims
|
||||
"close$NOCANCEL" => {
|
||||
let result = this.close(args[0])?;
|
||||
let &[result] = check_arg_count(args)?;
|
||||
let result = this.close(result)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"stat$INODE64" => {
|
||||
let result = this.macos_stat(args[0], args[1])?;
|
||||
let &[path, buf] = check_arg_count(args)?;
|
||||
let result = this.macos_stat(path, buf)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"lstat$INODE64" => {
|
||||
let result = this.macos_lstat(args[0], args[1])?;
|
||||
let &[path, buf] = check_arg_count(args)?;
|
||||
let result = this.macos_lstat(path, buf)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"fstat$INODE64" => {
|
||||
let result = this.macos_fstat(args[0], args[1])?;
|
||||
let &[fd, buf] = check_arg_count(args)?;
|
||||
let result = this.macos_fstat(fd, buf)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"opendir$INODE64" => {
|
||||
let result = this.opendir(args[0])?;
|
||||
let &[name] = check_arg_count(args)?;
|
||||
let result = this.opendir(name)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"readdir_r$INODE64" => {
|
||||
let result = this.macos_readdir_r(args[0], args[1], args[2])?;
|
||||
let &[dirp, entry, result] = check_arg_count(args)?;
|
||||
let result = this.macos_readdir_r(dirp, entry, result)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"ftruncate" => {
|
||||
let result = this.ftruncate64(args[0], args[1])?;
|
||||
let &[fd, length] = check_arg_count(args)?;
|
||||
let result = this.ftruncate64(fd, length)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
|
||||
// Environment related shims
|
||||
"_NSGetEnviron" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
this.write_scalar(this.machine.env_vars.environ.unwrap().ptr, dest)?;
|
||||
}
|
||||
|
||||
// Time related shims
|
||||
"gettimeofday" => {
|
||||
let result = this.gettimeofday(args[0], args[1])?;
|
||||
let &[tv, tz] = check_arg_count(args)?;
|
||||
let result = this.gettimeofday(tv, tz)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"mach_absolute_time" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
let result = this.mach_absolute_time()?;
|
||||
this.write_scalar(Scalar::from_u64(result), dest)?;
|
||||
}
|
||||
|
||||
"mach_timebase_info" => {
|
||||
let result = this.mach_timebase_info(args[0])?;
|
||||
let &[info] = check_arg_count(args)?;
|
||||
let result = this.mach_timebase_info(info)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
},
|
||||
|
||||
// Access to command-line arguments
|
||||
"_NSGetArgc" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
this.write_scalar(this.machine.argc.expect("machine must be initialized"), dest)?;
|
||||
}
|
||||
"_NSGetArgv" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
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, data] = check_arg_count(args)?;
|
||||
let dtor = this.read_scalar(dtor)?.not_undef()?;
|
||||
let dtor = this.memory.get_fn(dtor)?.as_instance()?;
|
||||
let data = this.read_scalar(args[1])?.not_undef()?;
|
||||
let data = this.read_scalar(data)?.not_undef()?;
|
||||
let active_thread = this.get_active_thread()?;
|
||||
this.machine.tls.set_macos_thread_dtor(active_thread, dtor, data)?;
|
||||
}
|
||||
|
||||
// Querying system information
|
||||
"pthread_get_stackaddr_np" => {
|
||||
let _thread = this.read_scalar(args[0])?.not_undef()?;
|
||||
let &[thread] = check_arg_count(args)?;
|
||||
this.read_scalar(thread)?.not_undef()?;
|
||||
let stack_addr = Scalar::from_uint(STACK_ADDR, this.pointer_size());
|
||||
this.write_scalar(stack_addr, dest)?;
|
||||
}
|
||||
"pthread_get_stacksize_np" => {
|
||||
let _thread = this.read_scalar(args[0])?.not_undef()?;
|
||||
let &[thread] = check_arg_count(args)?;
|
||||
this.read_scalar(thread)?.not_undef()?;
|
||||
let stack_size = Scalar::from_uint(STACK_SIZE, this.pointer_size());
|
||||
this.write_scalar(stack_size, dest)?;
|
||||
}
|
||||
|
||||
// Threading
|
||||
"pthread_setname_np" => {
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
this.pthread_setname_np(ptr)?;
|
||||
let &[name] = check_arg_count(args)?;
|
||||
let name = this.read_scalar(name)?.not_undef()?;
|
||||
this.pthread_setname_np(name)?;
|
||||
}
|
||||
|
||||
// Incomplete shims that we "stub out" just to get pre-main initialization 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()?;
|
||||
let &[addr, _, _, _, _, _] = check_arg_count(args)?;
|
||||
let addr = this.read_scalar(addr)?.not_undef()?;
|
||||
this.write_scalar(addr, dest)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use rustc_middle::mir;
|
|||
use rustc_target::abi::Size;
|
||||
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
|
|
@ -23,42 +24,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
match link_name {
|
||||
// Environment related shims
|
||||
"GetEnvironmentVariableW" => {
|
||||
let result = this.GetEnvironmentVariableW(args[0], args[1], args[2])?;
|
||||
let &[name, buf, size] = check_arg_count(args)?;
|
||||
let result = this.GetEnvironmentVariableW(name, buf, size)?;
|
||||
this.write_scalar(Scalar::from_u32(result), dest)?;
|
||||
}
|
||||
"SetEnvironmentVariableW" => {
|
||||
let result = this.SetEnvironmentVariableW(args[0], args[1])?;
|
||||
let &[name, value] = check_arg_count(args)?;
|
||||
let result = this.SetEnvironmentVariableW(name, value)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"GetEnvironmentStringsW" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
let result = this.GetEnvironmentStringsW()?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"FreeEnvironmentStringsW" => {
|
||||
let result = this.FreeEnvironmentStringsW(args[0])?;
|
||||
let &[env_block] = check_arg_count(args)?;
|
||||
let result = this.FreeEnvironmentStringsW(env_block)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"GetCurrentDirectoryW" => {
|
||||
let result = this.GetCurrentDirectoryW(args[0], args[1])?;
|
||||
let &[size, buf] = check_arg_count(args)?;
|
||||
let result = this.GetCurrentDirectoryW(size, buf)?;
|
||||
this.write_scalar(Scalar::from_u32(result), dest)?;
|
||||
}
|
||||
"SetCurrentDirectoryW" => {
|
||||
let result = this.SetCurrentDirectoryW(args[0])?;
|
||||
let &[path] = check_arg_count(args)?;
|
||||
let result = this.SetCurrentDirectoryW(path)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
|
||||
// File related shims
|
||||
"GetStdHandle" => {
|
||||
let which = this.read_scalar(args[0])?.to_i32()?;
|
||||
let &[which] = check_arg_count(args)?;
|
||||
let which = this.read_scalar(which)?.to_i32()?;
|
||||
// We just make this the identity function, so we know later in `WriteFile`
|
||||
// which one it is.
|
||||
this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
|
||||
}
|
||||
"WriteFile" => {
|
||||
let handle = this.read_scalar(args[0])?.to_machine_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])?;
|
||||
let &[handle, buf, n, written_ptr, _overlapped] = check_arg_count(args)?;
|
||||
let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
|
||||
let buf = this.read_scalar(buf)?.not_undef()?;
|
||||
let n = this.read_scalar(n)?.to_u32()?;
|
||||
let written_place = this.deref_operand(written_ptr)?;
|
||||
// Spec says to always write `0` first.
|
||||
this.write_null(written_place.into())?;
|
||||
let written = if handle == -11 || handle == -12 {
|
||||
|
|
@ -88,41 +97,48 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Allocation
|
||||
"HeapAlloc" => {
|
||||
let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
|
||||
let flags = this.read_scalar(args[1])?.to_u32()?;
|
||||
let size = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||
let &[handle, flags, size] = check_arg_count(args)?;
|
||||
this.read_scalar(handle)?.to_machine_isize(this)?;
|
||||
let flags = this.read_scalar(flags)?.to_u32()?;
|
||||
let size = this.read_scalar(size)?.to_machine_usize(this)?;
|
||||
let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
|
||||
let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"HeapFree" => {
|
||||
let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
|
||||
let _flags = this.read_scalar(args[1])?.to_u32()?;
|
||||
let ptr = this.read_scalar(args[2])?.not_undef()?;
|
||||
let &[handle, flags, ptr] = check_arg_count(args)?;
|
||||
this.read_scalar(handle)?.to_machine_isize(this)?;
|
||||
this.read_scalar(flags)?.to_u32()?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
this.free(ptr, MiriMemoryKind::WinHeap)?;
|
||||
this.write_scalar(Scalar::from_i32(1), dest)?;
|
||||
}
|
||||
"HeapReAlloc" => {
|
||||
let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
|
||||
let _flags = this.read_scalar(args[1])?.to_u32()?;
|
||||
let ptr = this.read_scalar(args[2])?.not_undef()?;
|
||||
let size = this.read_scalar(args[3])?.to_machine_usize(this)?;
|
||||
let &[handle, flags, ptr, size] = check_arg_count(args)?;
|
||||
this.read_scalar(handle)?.to_machine_isize(this)?;
|
||||
this.read_scalar(flags)?.to_u32()?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let size = this.read_scalar(size)?.to_machine_usize(this)?;
|
||||
let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
// errno
|
||||
"SetLastError" => {
|
||||
this.set_last_error(this.read_scalar(args[0])?.not_undef()?)?;
|
||||
let &[error] = check_arg_count(args)?;
|
||||
let error = this.read_scalar(error)?.not_undef()?;
|
||||
this.set_last_error(error)?;
|
||||
}
|
||||
"GetLastError" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
let last_error = this.get_last_error()?;
|
||||
this.write_scalar(last_error, dest)?;
|
||||
}
|
||||
|
||||
// Querying system information
|
||||
"GetSystemInfo" => {
|
||||
let system_info = this.deref_operand(args[0])?;
|
||||
let &[system_info] = check_arg_count(args)?;
|
||||
let system_info = this.deref_operand(system_info)?;
|
||||
// Initialize with `0`.
|
||||
this.memory.write_bytes(
|
||||
system_info.ptr,
|
||||
|
|
@ -139,19 +155,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// This just creates a key; Windows does not natively support TLS destructors.
|
||||
|
||||
// Create key and return it.
|
||||
let &[] = check_arg_count(args)?;
|
||||
let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
|
||||
this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
|
||||
}
|
||||
"TlsGetValue" => {
|
||||
let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
|
||||
let &[key] = check_arg_count(args)?;
|
||||
let key = u128::from(this.read_scalar(key)?.to_u32()?);
|
||||
let active_thread = this.get_active_thread()?;
|
||||
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
|
||||
this.write_scalar(ptr, dest)?;
|
||||
}
|
||||
"TlsSetValue" => {
|
||||
let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
|
||||
let &[key, new_ptr] = check_arg_count(args)?;
|
||||
let key = u128::from(this.read_scalar(key)?.to_u32()?);
|
||||
let active_thread = this.get_active_thread()?;
|
||||
let new_ptr = this.read_scalar(args[1])?.not_undef()?;
|
||||
let new_ptr = this.read_scalar(new_ptr)?.not_undef()?;
|
||||
this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
|
||||
|
||||
// Return success (`1`).
|
||||
|
|
@ -160,6 +179,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Access to command-line arguments
|
||||
"GetCommandLineW" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
this.write_scalar(
|
||||
this.machine.cmd_line.expect("machine must be initialized"),
|
||||
dest,
|
||||
|
|
@ -168,42 +188,52 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Time related shims
|
||||
"GetSystemTimeAsFileTime" => {
|
||||
this.GetSystemTimeAsFileTime(args[0])?;
|
||||
#[allow(non_snake_case)]
|
||||
let &[LPFILETIME] = check_arg_count(args)?;
|
||||
this.GetSystemTimeAsFileTime(LPFILETIME)?;
|
||||
}
|
||||
"QueryPerformanceCounter" => {
|
||||
let result = this.QueryPerformanceCounter(args[0])?;
|
||||
#[allow(non_snake_case)]
|
||||
let &[lpPerformanceCount] = check_arg_count(args)?;
|
||||
let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"QueryPerformanceFrequency" => {
|
||||
let result = this.QueryPerformanceFrequency(args[0])?;
|
||||
#[allow(non_snake_case)]
|
||||
let &[lpFrequency] = check_arg_count(args)?;
|
||||
let result = this.QueryPerformanceFrequency(lpFrequency)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
|
||||
// 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()?;
|
||||
let &[ptr, len] = check_arg_count(args)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let len = this.read_scalar(len)?.to_u32()?;
|
||||
this.gen_random(ptr, len.into())?;
|
||||
this.write_scalar(Scalar::from_bool(true), dest)?;
|
||||
}
|
||||
"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])?;
|
||||
let &[console, buffer_info] = check_arg_count(args)?;
|
||||
this.read_scalar(console)?.to_machine_isize(this)?;
|
||||
this.deref_operand(buffer_info)?;
|
||||
// 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])?;
|
||||
let &[console, mode] = check_arg_count(args)?;
|
||||
this.read_scalar(console)?.to_machine_isize(this)?;
|
||||
this.deref_operand(mode)?;
|
||||
// Indicate an error.
|
||||
// FIXME: we should set last_error, but to what?
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"SwitchToThread" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
// Note that once Miri supports concurrency, this will need to return a nonzero
|
||||
// value if this call does result in switching to another thread.
|
||||
this.write_null(dest)?;
|
||||
|
|
@ -217,17 +247,34 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// Incomplete shims that we "stub out" just to get pre-main initialization 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::") => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
// Just fake a HANDLE
|
||||
this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
|
||||
}
|
||||
| "GetModuleHandleW"
|
||||
| "GetProcAddress"
|
||||
| "SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::")
|
||||
"GetModuleHandleW" if this.frame().instance.to_string().starts_with("std::sys::windows::")
|
||||
=> {
|
||||
// Pretend these do not exist / nothing happened, by returning zero.
|
||||
#[allow(non_snake_case)]
|
||||
let &[_lpModuleName] = check_arg_count(args)?;
|
||||
// Pretend this does not exist / nothing happened, by returning zero.
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"GetProcAddress" if this.frame().instance.to_string().starts_with("std::sys::windows::")
|
||||
=> {
|
||||
#[allow(non_snake_case)]
|
||||
let &[_hModule, _lpProcName] = check_arg_count(args)?;
|
||||
// Pretend this does not exist / nothing happened, by returning zero.
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::")
|
||||
=> {
|
||||
#[allow(non_snake_case)]
|
||||
let &[_hConsoleOutput, _wAttribute] = check_arg_count(args)?;
|
||||
// Pretend these does not exist / nothing happened, by returning zero.
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"AddVectoredExceptionHandler" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
|
||||
#[allow(non_snake_case)]
|
||||
let &[_First, _Handler] = check_arg_count(args)?;
|
||||
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
|
||||
this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
|
||||
}
|
||||
|
|
@ -236,6 +283,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "LeaveCriticalSection"
|
||||
| "DeleteCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::")
|
||||
=> {
|
||||
#[allow(non_snake_case)]
|
||||
let &[_lpCriticalSection] = check_arg_count(args)?;
|
||||
assert_eq!(this.get_total_thread_count()?, 1, "concurrency on Windows not supported");
|
||||
// Nothing to do, not even a return value.
|
||||
// (Windows locks are reentrant, and we have only 1 thread,
|
||||
|
|
@ -243,6 +292,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
"TryEnterCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::")
|
||||
=> {
|
||||
#[allow(non_snake_case)]
|
||||
let &[_lpCriticalSection] = check_arg_count(args)?;
|
||||
assert_eq!(this.get_total_thread_count()?, 1, "concurrency on Windows not supported");
|
||||
// There is only one thread, so this always succeeds and returns TRUE
|
||||
this.write_scalar(Scalar::from_i32(1), dest)?;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rustc_target::abi::{Align, LayoutOf, Size};
|
|||
|
||||
use crate::stacked_borrows::Tag;
|
||||
use crate::*;
|
||||
use helpers::{immty_from_int_checked, immty_from_uint_checked};
|
||||
use helpers::{check_arg_count, immty_from_int_checked, immty_from_uint_checked};
|
||||
use shims::time::system_time_to_duration;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -322,22 +322,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
fn fcntl(
|
||||
&mut self,
|
||||
fd_op: OpTy<'tcx, Tag>,
|
||||
cmd_op: OpTy<'tcx, Tag>,
|
||||
start_op: Option<OpTy<'tcx, Tag>>,
|
||||
args: &[OpTy<'tcx, Tag>],
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.check_no_isolation("fcntl")?;
|
||||
|
||||
let fd = this.read_scalar(fd_op)?.to_i32()?;
|
||||
let cmd = this.read_scalar(cmd_op)?.to_i32()?;
|
||||
if args.len() < 2 {
|
||||
throw_ub_format!("incorrect number of arguments for fcntl: got {}, expected at least 2", args.len());
|
||||
}
|
||||
let cmd = this.read_scalar(args[1])?.to_i32()?;
|
||||
// We only support getting the flags for a descriptor.
|
||||
if cmd == this.eval_libc_i32("F_GETFD")? {
|
||||
// Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
|
||||
// `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
|
||||
// always sets this flag when opening a file. However we still need to check that the
|
||||
// file itself is open.
|
||||
let &[fd, _] = check_arg_count(args)?;
|
||||
let fd = this.read_scalar(fd)?.to_i32()?;
|
||||
if this.machine.file_handler.handles.contains_key(&fd) {
|
||||
Ok(this.eval_libc_i32("FD_CLOEXEC")?)
|
||||
} else {
|
||||
|
|
@ -350,15 +352,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// because exec() isn't supported. The F_DUPFD and F_DUPFD_CLOEXEC commands only
|
||||
// differ in whether the FD_CLOEXEC flag is pre-set on the new file descriptor,
|
||||
// thus they can share the same implementation here.
|
||||
let &[fd, _, start] = check_arg_count(args)?;
|
||||
let fd = this.read_scalar(fd)?.to_i32()?;
|
||||
let start = this.read_scalar(start)?.to_i32()?;
|
||||
if fd < MIN_NORMAL_FILE_FD {
|
||||
throw_unsup_format!("duplicating file descriptors for stdin, stdout, or stderr is not supported")
|
||||
}
|
||||
let start_op = start_op.ok_or_else(|| {
|
||||
err_unsup_format!(
|
||||
"fcntl with command F_DUPFD or F_DUPFD_CLOEXEC requires a third argument"
|
||||
)
|
||||
})?;
|
||||
let start = this.read_scalar(start_op)?.to_i32()?;
|
||||
let fh = &mut this.machine.file_handler;
|
||||
let (file_result, writable) = match fh.handles.get(&fd) {
|
||||
Some(FileHandle { file, writable }) => (file.try_clone(), *writable),
|
||||
|
|
@ -1064,7 +1063,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
|
||||
fn ftruncate64(
|
||||
&mut self, fd_op: OpTy<'tcx, Tag>,
|
||||
&mut self,
|
||||
fd_op: OpTy<'tcx, Tag>,
|
||||
length_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use rustc_apfloat::{Float, Round};
|
|||
use rustc_target::abi::{Align, LayoutOf, Size};
|
||||
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
|
|
@ -45,16 +46,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "copy"
|
||||
| "copy_nonoverlapping"
|
||||
=> {
|
||||
let &[src, dest, count] = check_arg_count(args)?;
|
||||
let elem_ty = substs.type_at(0);
|
||||
let elem_layout = this.layout_of(elem_ty)?;
|
||||
let count = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||
let count = this.read_scalar(count)?.to_machine_usize(this)?;
|
||||
let elem_align = elem_layout.align.abi;
|
||||
|
||||
let size = elem_layout.size.checked_mul(count, this)
|
||||
.ok_or_else(|| err_ub_format!("overflow computing total size of `{}`", intrinsic_name))?;
|
||||
let src = this.read_scalar(args[0])?.not_undef()?;
|
||||
let src = this.read_scalar(src)?.not_undef()?;
|
||||
let src = this.memory.check_ptr_access(src, size, elem_align)?;
|
||||
let dest = this.read_scalar(args[1])?.not_undef()?;
|
||||
let dest = this.read_scalar(dest)?.not_undef()?;
|
||||
let dest = this.memory.check_ptr_access(dest, size, elem_align)?;
|
||||
|
||||
if let (Some(src), Some(dest)) = (src, dest) {
|
||||
|
|
@ -68,25 +70,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
|
||||
"move_val_init" => {
|
||||
let place = this.deref_operand(args[0])?;
|
||||
this.copy_op(args[1], place.into())?;
|
||||
let &[place, dest] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
this.copy_op(dest, place.into())?;
|
||||
}
|
||||
|
||||
"volatile_load" => {
|
||||
let place = this.deref_operand(args[0])?;
|
||||
let &[place] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
this.copy_op(place.into(), dest)?;
|
||||
}
|
||||
"volatile_store" => {
|
||||
let place = this.deref_operand(args[0])?;
|
||||
this.copy_op(args[1], place.into())?;
|
||||
let &[place, dest] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
this.copy_op(dest, place.into())?;
|
||||
}
|
||||
|
||||
"write_bytes" => {
|
||||
let &[ptr, val_byte, count] = check_arg_count(args)?;
|
||||
let ty = substs.type_at(0);
|
||||
let ty_layout = this.layout_of(ty)?;
|
||||
let val_byte = this.read_scalar(args[1])?.to_u8()?;
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let count = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||
let val_byte = this.read_scalar(val_byte)?.to_u8()?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let count = this.read_scalar(count)?.to_machine_usize(this)?;
|
||||
let byte_count = ty_layout.size.checked_mul(count, this)
|
||||
.ok_or_else(|| err_ub_format!("overflow computing total size of `write_bytes`"))?;
|
||||
this.memory
|
||||
|
|
@ -95,8 +101,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Pointer arithmetic
|
||||
"arith_offset" => {
|
||||
let offset = this.read_scalar(args[1])?.to_machine_isize(this)?;
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let &[ptr, offset] = check_arg_count(args)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let offset = this.read_scalar(offset)?.to_machine_isize(this)?;
|
||||
|
||||
let pointee_ty = substs.type_at(0);
|
||||
let pointee_size = i64::try_from(this.layout_of(pointee_ty)?.size.bytes()).unwrap();
|
||||
|
|
@ -105,8 +112,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
this.write_scalar(result_ptr, dest)?;
|
||||
}
|
||||
"offset" => {
|
||||
let offset = this.read_scalar(args[1])?.to_machine_isize(this)?;
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let &[ptr, offset] = check_arg_count(args)?;
|
||||
let ptr = this.read_scalar(ptr)?.not_undef()?;
|
||||
let offset = this.read_scalar(offset)?.to_machine_isize(this)?;
|
||||
let result_ptr = this.pointer_offset_inbounds(ptr, substs.type_at(0), offset)?;
|
||||
this.write_scalar(result_ptr, dest)?;
|
||||
}
|
||||
|
|
@ -127,8 +135,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "truncf32"
|
||||
| "roundf32"
|
||||
=> {
|
||||
let &[f] = check_arg_count(args)?;
|
||||
// FIXME: Using host floats.
|
||||
let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?);
|
||||
let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
|
||||
let f = match intrinsic_name {
|
||||
"sinf32" => f.sin(),
|
||||
"fabsf32" => f.abs(),
|
||||
|
|
@ -163,8 +172,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "truncf64"
|
||||
| "roundf64"
|
||||
=> {
|
||||
let &[f] = check_arg_count(args)?;
|
||||
// FIXME: Using host floats.
|
||||
let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
|
||||
let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
|
||||
let f = match intrinsic_name {
|
||||
"sinf64" => f.sin(),
|
||||
"fabsf64" => f.abs(),
|
||||
|
|
@ -191,8 +201,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "fdiv_fast"
|
||||
| "frem_fast"
|
||||
=> {
|
||||
let a = this.read_immediate(args[0])?;
|
||||
let b = this.read_immediate(args[1])?;
|
||||
let &[a, b] = check_arg_count(args)?;
|
||||
let a = this.read_immediate(a)?;
|
||||
let b = this.read_immediate(b)?;
|
||||
let op = match intrinsic_name {
|
||||
"fadd_fast" => mir::BinOp::Add,
|
||||
"fsub_fast" => mir::BinOp::Sub,
|
||||
|
|
@ -209,8 +220,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "maxnumf32"
|
||||
| "copysignf32"
|
||||
=> {
|
||||
let a = this.read_scalar(args[0])?.to_f32()?;
|
||||
let b = this.read_scalar(args[1])?.to_f32()?;
|
||||
let &[a, b] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f32()?;
|
||||
let b = this.read_scalar(b)?.to_f32()?;
|
||||
let res = match intrinsic_name {
|
||||
"minnumf32" => a.min(b),
|
||||
"maxnumf32" => a.max(b),
|
||||
|
|
@ -225,8 +237,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "maxnumf64"
|
||||
| "copysignf64"
|
||||
=> {
|
||||
let a = this.read_scalar(args[0])?.to_f64()?;
|
||||
let b = this.read_scalar(args[1])?.to_f64()?;
|
||||
let &[a, b] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f64()?;
|
||||
let b = this.read_scalar(b)?.to_f64()?;
|
||||
let res = match intrinsic_name {
|
||||
"minnumf64" => a.min(b),
|
||||
"maxnumf64" => a.max(b),
|
||||
|
|
@ -235,53 +248,60 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
};
|
||||
this.write_scalar(Scalar::from_f64(res), dest)?;
|
||||
}
|
||||
|
||||
|
||||
"powf32" => {
|
||||
let &[f, f2] = check_arg_count(args)?;
|
||||
// FIXME: Using host floats.
|
||||
let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?);
|
||||
let f2 = f32::from_bits(this.read_scalar(args[1])?.to_u32()?);
|
||||
let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
|
||||
let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?);
|
||||
this.write_scalar(Scalar::from_u32(f.powf(f2).to_bits()), dest)?;
|
||||
}
|
||||
|
||||
"powf64" => {
|
||||
let &[f, f2] = check_arg_count(args)?;
|
||||
// FIXME: Using host floats.
|
||||
let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
|
||||
let f2 = f64::from_bits(this.read_scalar(args[1])?.to_u64()?);
|
||||
let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
|
||||
let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?);
|
||||
this.write_scalar(Scalar::from_u64(f.powf(f2).to_bits()), dest)?;
|
||||
}
|
||||
|
||||
"fmaf32" => {
|
||||
let a = this.read_scalar(args[0])?.to_f32()?;
|
||||
let b = this.read_scalar(args[1])?.to_f32()?;
|
||||
let c = this.read_scalar(args[2])?.to_f32()?;
|
||||
let &[a, b, c] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f32()?;
|
||||
let b = this.read_scalar(b)?.to_f32()?;
|
||||
let c = this.read_scalar(c)?.to_f32()?;
|
||||
let res = a.mul_add(b, c).value;
|
||||
this.write_scalar(Scalar::from_f32(res), dest)?;
|
||||
}
|
||||
|
||||
"fmaf64" => {
|
||||
let a = this.read_scalar(args[0])?.to_f64()?;
|
||||
let b = this.read_scalar(args[1])?.to_f64()?;
|
||||
let c = this.read_scalar(args[2])?.to_f64()?;
|
||||
let &[a, b, c] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f64()?;
|
||||
let b = this.read_scalar(b)?.to_f64()?;
|
||||
let c = this.read_scalar(c)?.to_f64()?;
|
||||
let res = a.mul_add(b, c).value;
|
||||
this.write_scalar(Scalar::from_f64(res), dest)?;
|
||||
}
|
||||
|
||||
"powif32" => {
|
||||
let &[f, i] = check_arg_count(args)?;
|
||||
// FIXME: Using host floats.
|
||||
let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?);
|
||||
let i = this.read_scalar(args[1])?.to_i32()?;
|
||||
let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
this.write_scalar(Scalar::from_u32(f.powi(i).to_bits()), dest)?;
|
||||
}
|
||||
|
||||
"powif64" => {
|
||||
let &[f, i] = check_arg_count(args)?;
|
||||
// FIXME: Using host floats.
|
||||
let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
|
||||
let i = this.read_scalar(args[1])?.to_i32()?;
|
||||
let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?;
|
||||
}
|
||||
|
||||
"float_to_int_unchecked" => {
|
||||
let val = this.read_immediate(args[0])?;
|
||||
let &[val] = check_arg_count(args)?;
|
||||
let val = this.read_immediate(val)?;
|
||||
|
||||
let res = match val.layout.ty.kind {
|
||||
ty::Float(FloatTy::F32) => {
|
||||
|
|
@ -302,7 +322,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "atomic_load_relaxed"
|
||||
| "atomic_load_acq"
|
||||
=> {
|
||||
let place = this.deref_operand(args[0])?;
|
||||
let &[place] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
let val = this.read_scalar(place.into())?; // make sure it fits into a scalar; otherwise it cannot be atomic
|
||||
|
||||
// Check alignment requirements. Atomics must always be aligned to their size,
|
||||
|
|
@ -319,8 +340,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "atomic_store_relaxed"
|
||||
| "atomic_store_rel"
|
||||
=> {
|
||||
let place = this.deref_operand(args[0])?;
|
||||
let val = this.read_scalar(args[1])?; // make sure it fits into a scalar; otherwise it cannot be atomic
|
||||
let &[place, val] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
let val = this.read_scalar(val)?; // make sure it fits into a scalar; otherwise it cannot be atomic
|
||||
|
||||
// Check alignment requirements. Atomics must always be aligned to their size,
|
||||
// even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
|
||||
|
|
@ -345,8 +367,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
|
||||
_ if intrinsic_name.starts_with("atomic_xchg") => {
|
||||
let place = this.deref_operand(args[0])?;
|
||||
let new = this.read_scalar(args[1])?;
|
||||
let &[place, new] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
let new = this.read_scalar(new)?;
|
||||
let old = this.read_scalar(place.into())?;
|
||||
|
||||
// Check alignment requirements. Atomics must always be aligned to their size,
|
||||
|
|
@ -360,9 +383,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
|
||||
_ if intrinsic_name.starts_with("atomic_cxchg") => {
|
||||
let place = this.deref_operand(args[0])?;
|
||||
let expect_old = this.read_immediate(args[1])?; // read as immediate for the sake of `binary_op()`
|
||||
let new = this.read_scalar(args[2])?;
|
||||
let &[place, expect_old, new] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
let expect_old = this.read_immediate(expect_old)?; // read as immediate for the sake of `binary_op()`
|
||||
let new = this.read_scalar(new)?;
|
||||
let old = this.read_immediate(place.into())?; // read as immediate for the sake of `binary_op()`
|
||||
|
||||
// Check alignment requirements. Atomics must always be aligned to their size,
|
||||
|
|
@ -414,11 +438,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "atomic_xsub_acqrel"
|
||||
| "atomic_xsub_relaxed"
|
||||
=> {
|
||||
let place = this.deref_operand(args[0])?;
|
||||
let &[place, rhs] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
if !place.layout.ty.is_integral() {
|
||||
bug!("Atomic arithmetic operations only work on integer types");
|
||||
}
|
||||
let rhs = this.read_immediate(args[1])?;
|
||||
let rhs = this.read_immediate(rhs)?;
|
||||
let old = this.read_immediate(place.into())?;
|
||||
|
||||
// Check alignment requirements. Atomics must always be aligned to their size,
|
||||
|
|
@ -447,6 +472,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
"assert_inhabited" |
|
||||
"assert_zero_valid" |
|
||||
"assert_uninit_valid" => {
|
||||
let &[] = check_arg_count(args)?;
|
||||
let ty = substs.type_at(0);
|
||||
let layout = this.layout_of(ty)?;
|
||||
// Abort here because the caller might not be panic safe.
|
||||
|
|
@ -462,7 +488,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
|
||||
"min_align_of_val" => {
|
||||
let mplace = this.deref_operand(args[0])?;
|
||||
let &[mplace] = check_arg_count(args)?;
|
||||
let mplace = this.deref_operand(mplace)?;
|
||||
let (_, align) = this
|
||||
.size_and_align_of_mplace(mplace)?
|
||||
.expect("size_of_val called on extern type");
|
||||
|
|
@ -470,7 +497,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
}
|
||||
|
||||
"size_of_val" => {
|
||||
let mplace = this.deref_operand(args[0])?;
|
||||
let &[mplace] = check_arg_count(args)?;
|
||||
let mplace = this.deref_operand(mplace)?;
|
||||
let (size, _) = this
|
||||
.size_and_align_of_mplace(mplace)?
|
||||
.expect("size_of_val called on extern type");
|
||||
|
|
@ -479,17 +507,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// Other
|
||||
"assume" => {
|
||||
let cond = this.read_scalar(args[0])?.to_bool()?;
|
||||
let &[cond] = check_arg_count(args)?;
|
||||
let cond = this.read_scalar(cond)?.not_undef()?.to_bool()?;
|
||||
if !cond {
|
||||
throw_ub_format!("`assume` intrinsic called with `false`");
|
||||
}
|
||||
}
|
||||
|
||||
"exact_div" =>
|
||||
this.exact_div(this.read_immediate(args[0])?, this.read_immediate(args[1])?, dest)?,
|
||||
"exact_div" => {
|
||||
let &[num, denom] = check_arg_count(args)?;
|
||||
this.exact_div(this.read_immediate(num)?, this.read_immediate(denom)?, dest)?;
|
||||
}
|
||||
|
||||
"forget" => {
|
||||
// We get an argument... and forget about it.
|
||||
let &[_] = check_arg_count(args)?;
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -497,7 +529,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
| "unlikely"
|
||||
=> {
|
||||
// These just return their argument
|
||||
let b = this.read_immediate(args[0])?;
|
||||
let &[b] = check_arg_count(args)?;
|
||||
let b = this.read_immediate(b)?;
|
||||
this.write_immediate(*b, dest)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use log::trace;
|
|||
use rustc_middle::{mir, ty};
|
||||
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
|
|
@ -32,7 +33,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
// There are some more lang items we want to hook that CTFE does not hook (yet).
|
||||
if this.tcx.lang_items().align_offset_fn() == Some(instance.def.def_id()) {
|
||||
this.align_offset(args[0], args[1], ret, unwind)?;
|
||||
let &[ptr, align] = check_arg_count(args)?;
|
||||
this.align_offset(ptr, align, ret, unwind)?;
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use rustc_middle::{mir, ty};
|
|||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
|
||||
/// Holds all of the relevant data for when unwinding hits a `try` frame.
|
||||
#[derive(Debug)]
|
||||
|
|
@ -53,7 +54,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
trace!("miri_start_panic: {:?}", this.frame().instance);
|
||||
|
||||
// Get the raw pointer stored in arg[0] (the panic payload).
|
||||
let payload = this.read_scalar(args[0])?.not_undef()?;
|
||||
let &[payload] = check_arg_count(args)?;
|
||||
let payload = this.read_scalar(payload)?.not_undef()?;
|
||||
assert!(
|
||||
this.machine.panic_payload.is_none(),
|
||||
"the panic runtime should avoid double-panics"
|
||||
|
|
@ -86,9 +88,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
// a pointer to `Box<dyn Any + Send + 'static>`.
|
||||
|
||||
// Get all the arguments.
|
||||
let try_fn = this.read_scalar(args[0])?.not_undef()?;
|
||||
let data = this.read_scalar(args[1])?.not_undef()?;
|
||||
let catch_fn = this.read_scalar(args[2])?.not_undef()?;
|
||||
let &[try_fn, data, catch_fn] = check_arg_count(args)?;
|
||||
let try_fn = this.read_scalar(try_fn)?.not_undef()?;
|
||||
let data = this.read_scalar(data)?.not_undef()?;
|
||||
let catch_fn = this.read_scalar(catch_fn)?.not_undef()?;
|
||||
|
||||
// Now we make a function call, and pass `data` as first and only argument.
|
||||
let f_instance = this.memory.get_fn(try_fn)?.as_instance()?;
|
||||
|
|
|
|||
|
|
@ -121,12 +121,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
fn pthread_setname_np(
|
||||
&mut self,
|
||||
ptr: Scalar<Tag>,
|
||||
name: Scalar<Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
this.assert_target_os("macos", "pthread_setname_np");
|
||||
|
||||
let name = this.memory.read_c_str(ptr)?.to_owned();
|
||||
let name = this.memory.read_c_str(name)?.to_owned();
|
||||
this.set_active_thread_name(name)?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
12
tests/compile-fail/check_arg_count_too_few_args.rs
Normal file
12
tests/compile-fail/check_arg_count_too_few_args.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#![feature(core_intrinsics)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
fn main() {
|
||||
extern "C" {
|
||||
fn malloc() -> *mut std::ffi::c_void;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _ = malloc(); //~ ERROR Undefined Behavior: incorrect number of arguments: got 0, expected 1
|
||||
};
|
||||
}
|
||||
12
tests/compile-fail/check_arg_count_too_many_args.rs
Normal file
12
tests/compile-fail/check_arg_count_too_many_args.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#![feature(core_intrinsics)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
fn main() {
|
||||
extern "C" {
|
||||
fn malloc(_: i32, _: i32) -> *mut std::ffi::c_void;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _ = malloc(1, 2); //~ ERROR Undefined Behavior: incorrect number of arguments: got 2, expected 1
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue