4 use crate::helpers::check_arg_count;
5 use shims::posix::fs::EvalContextExt as _;
6 use shims::posix::linux::sync::futex;
7 use shims::posix::sync::EvalContextExt as _;
8 use shims::posix::thread::EvalContextExt as _;
10 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
11 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
12 fn emulate_foreign_item_by_name(
15 args: &[OpTy<'tcx, Tag>],
16 dest: PlaceTy<'tcx, Tag>,
17 _ret: mir::BasicBlock,
18 ) -> InterpResult<'tcx, bool> {
19 let this = self.eval_context_mut();
23 "__errno_location" => {
24 let &[] = check_arg_count(args)?;
25 let errno_place = this.last_error_place()?;
26 this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
29 // File related shims (but also see "syscall" below for statx)
30 // These symbols have different names on Linux and macOS, which is the only reason they are not
31 // in the `posix` module.
33 let &[fd] = check_arg_count(args)?;
34 let result = this.close(fd)?;
35 this.write_scalar(Scalar::from_i32(result), dest)?;
38 let &[name] = check_arg_count(args)?;
39 let result = this.opendir(name)?;
40 this.write_scalar(result, dest)?;
43 let &[dirp, entry, result] = check_arg_count(args)?;
44 let result = this.linux_readdir64_r(dirp, entry, result)?;
45 this.write_scalar(Scalar::from_i32(result), dest)?;
48 let &[fd, length] = check_arg_count(args)?;
49 let result = this.ftruncate64(fd, length)?;
50 this.write_scalar(Scalar::from_i32(result), dest)?;
54 let &[fd, offset, len, advice] = check_arg_count(args)?;
55 this.read_scalar(fd)?.to_i32()?;
56 this.read_scalar(offset)?.to_machine_isize(this)?;
57 this.read_scalar(len)?.to_machine_isize(this)?;
58 this.read_scalar(advice)?.to_i32()?;
59 // fadvise is only informational, we can ignore it.
60 this.write_null(dest)?;
62 "sync_file_range" => {
63 let &[fd, offset, nbytes, flags] = check_arg_count(args)?;
64 let result = this.sync_file_range(fd, offset, nbytes, flags)?;
65 this.write_scalar(Scalar::from_i32(result), dest)?;
70 // This is a POSIX function but it has only been tested on linux.
71 let &[clk_id, tp] = check_arg_count(args)?;
72 let result = this.clock_gettime(clk_id, tp)?;
73 this.write_scalar(Scalar::from_i32(result), dest)?;
76 // Querying system information
77 "pthread_attr_getstack" => {
78 // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here.
79 let &[attr_place, addr_place, size_place] = check_arg_count(args)?;
80 this.deref_operand(attr_place)?;
81 let addr_place = this.deref_operand(addr_place)?;
82 let size_place = this.deref_operand(size_place)?;
85 Scalar::from_uint(STACK_ADDR, this.pointer_size()),
89 Scalar::from_uint(STACK_SIZE, this.pointer_size()),
93 // Return success (`0`).
94 this.write_null(dest)?;
99 let &[option, arg2, arg3, arg4, arg5] = check_arg_count(args)?;
100 let result = this.prctl(option, arg2, arg3, arg4, arg5)?;
101 this.write_scalar(Scalar::from_i32(result), dest)?;
103 "pthread_condattr_setclock" => {
104 let &[attr, clock_id] = check_arg_count(args)?;
105 let result = this.pthread_condattr_setclock(attr, clock_id)?;
106 this.write_scalar(Scalar::from_i32(result), dest)?;
108 "pthread_condattr_getclock" => {
109 let &[attr, clock_id] = check_arg_count(args)?;
110 let result = this.pthread_condattr_getclock(attr, clock_id)?;
111 this.write_scalar(Scalar::from_i32(result), dest)?;
114 // Dynamically invoked syscalls
116 // The syscall variadic function is legal to call with more arguments than needed,
117 // extra arguments are simply ignored. However, all arguments need to be scalars;
118 // other types might be treated differently by the calling convention.
120 if !matches!(arg.layout.abi, rustc_target::abi::Abi::Scalar(_)) {
121 throw_ub_format!("`syscall` arguments must all have scalar layout, but {} does not", arg.layout.ty);
125 let sys_getrandom = this
126 .eval_libc("SYS_getrandom")?
127 .to_machine_usize(this)?;
130 .eval_libc("SYS_statx")?
131 .to_machine_usize(this)?;
134 .eval_libc("SYS_futex")?
135 .to_machine_usize(this)?;
138 throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
140 match this.read_scalar(args[0])?.to_machine_usize(this)? {
141 // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
142 // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
143 id if id == sys_getrandom => {
144 // The first argument is the syscall id, so skip over it.
146 throw_ub_format!("incorrect number of arguments for `getrandom` syscall: got {}, expected at least 4", args.len());
148 getrandom(this, args[1], args[2], args[3], dest)?;
150 // `statx` is used by `libstd` to retrieve metadata information on `linux`
151 // instead of using `stat`,`lstat` or `fstat` as on `macos`.
152 id if id == sys_statx => {
153 // The first argument is the syscall id, so skip over it.
155 throw_ub_format!("incorrect number of arguments for `statx` syscall: got {}, expected at least 6", args.len());
157 let result = this.linux_statx(args[1], args[2], args[3], args[4], args[5])?;
158 this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
160 // `futex` is used by some synchonization primitives.
161 id if id == sys_futex => {
162 futex(this, args, dest)?;
164 id => throw_unsup_format!("Miri does not support syscall ID {}", id),
170 let &[ptr, len, flags] = check_arg_count(args)?;
171 getrandom(this, ptr, len, flags, dest)?;
173 "sched_getaffinity" => {
174 let &[pid, cpusetsize, mask] = check_arg_count(args)?;
175 this.read_scalar(pid)?.to_i32()?;
176 this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
177 this.deref_operand(mask)?;
178 // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
179 let einval = this.eval_libc("EINVAL")?;
180 this.set_last_error(einval)?;
181 this.write_scalar(Scalar::from_i32(-1), dest)?;
184 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
185 // These shims are enabled only when the caller is in the standard library.
186 "pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
187 let &[_thread, _attr] = check_arg_count(args)?;
188 this.write_null(dest)?;
191 _ => throw_unsup_format!("can't call foreign function: {}", link_name),
198 // Shims the linux `getrandom` syscall.
200 this: &mut MiriEvalContext<'_, 'tcx>,
201 ptr: OpTy<'tcx, Tag>,
202 len: OpTy<'tcx, Tag>,
203 flags: OpTy<'tcx, Tag>,
204 dest: PlaceTy<'tcx, Tag>,
205 ) -> InterpResult<'tcx> {
206 let ptr = this.read_scalar(ptr)?.check_init()?;
207 let len = this.read_scalar(len)?.to_machine_usize(this)?;
209 // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
210 // neither of which have any effect on our current PRNG.
211 // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
212 let _flags = this.read_scalar(flags)?.to_i32();
214 this.gen_random(ptr, len)?;
215 this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;