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 // FIXME: The libc syscall() function is a variadic function.
117 // It's valid to call it with more arguments than a syscall
118 // needs, so none of these syscalls should use check_arg_count.
119 // It's even valid to call it with the wrong type of arguments,
120 // as long as they'd end up in the same place with the calling
121 // convention used. (E.g. using a `usize` instead of a pointer.)
122 // It's not directly clear which number, size, and type of arguments
123 // are acceptable in which cases and which aren't. (E.g. some
124 // types might take up the space of two registers.)
125 // So this needs to be researched first.
127 let sys_getrandom = this
128 .eval_libc("SYS_getrandom")?
129 .to_machine_usize(this)?;
132 .eval_libc("SYS_statx")?
133 .to_machine_usize(this)?;
136 .eval_libc("SYS_futex")?
137 .to_machine_usize(this)?;
140 throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
142 match this.read_scalar(args[0])?.to_machine_usize(this)? {
143 // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
144 // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
145 id if id == sys_getrandom => {
146 // The first argument is the syscall id, so skip over it.
147 let &[_, ptr, len, flags] = check_arg_count(args)?;
148 getrandom(this, ptr, len, flags, 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.
154 let &[_, dirfd, pathname, flags, mask, statxbuf] = check_arg_count(args)?;
155 let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
156 this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
158 // `futex` is used by some synchonization primitives.
159 id if id == sys_futex => {
160 futex(this, args, dest)?;
162 id => throw_unsup_format!("miri does not support syscall ID {}", id),
168 let &[ptr, len, flags] = check_arg_count(args)?;
169 getrandom(this, ptr, len, flags, dest)?;
171 "sched_getaffinity" => {
172 let &[pid, cpusetsize, mask] = check_arg_count(args)?;
173 this.read_scalar(pid)?.to_i32()?;
174 this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
175 this.deref_operand(mask)?;
176 // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
177 let einval = this.eval_libc("EINVAL")?;
178 this.set_last_error(einval)?;
179 this.write_scalar(Scalar::from_i32(-1), dest)?;
182 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
183 // These shims are enabled only when the caller is in the standard library.
184 "pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
185 let &[_thread, _attr] = check_arg_count(args)?;
186 this.write_null(dest)?;
189 _ => throw_unsup_format!("can't call foreign function: {}", link_name),
196 // Shims the linux `getrandom` syscall.
198 this: &mut MiriEvalContext<'_, 'tcx>,
199 ptr: OpTy<'tcx, Tag>,
200 len: OpTy<'tcx, Tag>,
201 flags: OpTy<'tcx, Tag>,
202 dest: PlaceTy<'tcx, Tag>,
203 ) -> InterpResult<'tcx> {
204 let ptr = this.read_scalar(ptr)?.check_init()?;
205 let len = this.read_scalar(len)?.to_machine_usize(this)?;
207 // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
208 // neither of which have any effect on our current PRNG.
209 this.read_scalar(flags)?.to_i32()?;
211 this.gen_random(ptr, len)?;
212 this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;