Skip to main content

miri/shims/unix/
foreign_items.rs

1use std::ffi::OsStr;
2use std::str;
3use std::time::Duration;
4
5use rustc_abi::Size;
6use rustc_middle::ty::Ty;
7use rustc_span::Symbol;
8use rustc_target::callconv::FnAbi;
9use rustc_target::spec::{Env, Os, Target};
10
11use self::shims::unix::android::foreign_items as android;
12use self::shims::unix::freebsd::foreign_items as freebsd;
13use self::shims::unix::linux::foreign_items as linux;
14use self::shims::unix::macos::foreign_items as macos;
15use self::shims::unix::netbsd::foreign_items as netbsd;
16use self::shims::unix::solarish::foreign_items as solarish;
17use crate::shims::alloc::EvalContextExt as _;
18use crate::shims::cpu_affinity::EvalContextExt as _;
19use crate::shims::files::FD_LIMIT;
20use crate::shims::unix::*;
21use crate::{shim_sig, *};
22
23pub fn is_dyn_sym(name: &str, target: &Target) -> bool {
24    match name {
25        // Used for (std and Miri) tests.
26        "strlen" => true,
27        // `signal` is set up as a weak symbol in `init_extern_statics` (on Android) so we might as
28        // well allow it in `dlsym`.
29        "signal" => true,
30        // needed at least on macOS to avoid file-based fallback in getrandom
31        "getentropy" | "getrandom" => true,
32        // `futimens` is set up as a weak symbol in `init_extern_statics` (on Android), so we
33        // allow it here too (it exists on all our Unix targets).
34        "futimens" => true,
35        // `preadv`/`pwritev` are set up as weak symbols in `init_extern_statics` (on Android,
36        // where std uses them via `weak!` since bionic only gained them in API level 24), so
37        // we allow them here too. They do not exist on Solaris.
38        "preadv" | "pwritev" if !matches!(target.os, Os::Solaris) => true,
39        // Give specific OSes a chance to allow their symbols.
40        _ =>
41            match target.os {
42                Os::Android => android::is_dyn_sym(name),
43                Os::FreeBsd => freebsd::is_dyn_sym(name),
44                Os::Linux => linux::is_dyn_sym(name, target),
45                Os::MacOs => macos::is_dyn_sym(name),
46                Os::Solaris | Os::Illumos => solarish::is_dyn_sym(name),
47                Os::NetBsd => netbsd::is_dyn_sym(name),
48                _ => false,
49            },
50    }
51}
52
53impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
54pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
55    // Querying system information
56    fn sysconf(&self, val: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
57        let ecx = self.eval_context_ref();
58
59        let name = ecx.read_scalar(val)?.to_i32()?;
60        // FIXME: Which of these are POSIX, and which are GNU/Linux?
61        // At least the names seem to all also exist on macOS.
62        static SYSCONFS: &[(&str, fn(&MiriInterpCx<'_>) -> i64)] = &[
63            ("_SC_PAGESIZE", |ecx| ecx.machine.page_size.try_into().unwrap()),
64            ("_SC_PAGE_SIZE", |ecx| ecx.machine.page_size.try_into().unwrap()),
65            ("_SC_NPROCESSORS_CONF", |ecx| ecx.machine.num_cpus.into()),
66            ("_SC_NPROCESSORS_ONLN", |ecx| ecx.machine.num_cpus.into()),
67            // 512 seems to be a reasonable default. The value is not critical, in
68            // the sense that getpwuid_r takes and checks the buffer length.
69            ("_SC_GETPW_R_SIZE_MAX", |_ecx| 512),
70            // We only hand out FDs in `0..FD_LIMIT`, making `FD_LIMIT` the max
71            // number of open FDs.
72            ("_SC_OPEN_MAX", |_ecx| FD_LIMIT.into()),
73            // Our hard-coded hostname is just 4 bytes so we don't need anything big here.
74            ("_SC_HOST_NAME_MAX", |_ecx| 255),
75        ];
76        for &(sysconf_name, value) in SYSCONFS {
77            let sysconf_name = ecx.eval_libc_i32(sysconf_name);
78            if sysconf_name == name {
79                let value = Scalar::from_target_isize(value(ecx), ecx);
80                return interp_ok(value);
81            }
82        }
83        throw_unsup_format!("unimplemented sysconf name: {}", name)
84    }
85
86    fn strerror_r(
87        &mut self,
88        errnum: &OpTy<'tcx>,
89        buf: &OpTy<'tcx>,
90        buflen: &OpTy<'tcx>,
91    ) -> InterpResult<'tcx, Scalar> {
92        let this = self.eval_context_mut();
93
94        let errnum = this.read_scalar(errnum)?;
95        let buf = this.read_pointer(buf)?;
96        let buflen = this.read_target_usize(buflen)?;
97        let error = this.try_errnum_to_io_error(errnum)?;
98        let formatted = match error {
99            Some(err) => format!("{err}"),
100            None => format!("<unknown errnum in strerror_r: {errnum}>"),
101        };
102        let (complete, _) = this.write_os_str_to_c_str(OsStr::new(&formatted), buf, buflen)?;
103        if complete {
104            interp_ok(Scalar::from_i32(0))
105        } else {
106            interp_ok(Scalar::from_i32(this.eval_libc_i32("ERANGE")))
107        }
108    }
109
110    fn emulate_foreign_item_inner(
111        &mut self,
112        link_name: Symbol,
113        abi: &FnAbi<'tcx, Ty<'tcx>>,
114        args: &[OpTy<'tcx>],
115        dest: &MPlaceTy<'tcx>,
116    ) -> InterpResult<'tcx, EmulateItemResult> {
117        let this = self.eval_context_mut();
118
119        if this.machine.communicate() {
120            // When isolation is disabled we need to check for new host I/O events before
121            // running any shimmed function. This is needed to ensure that the shim we
122            // execute has up-to-date information about host readiness (as reflected
123            // e.g. by epoll) even if the current thread never yields.
124
125            // Perform a non-blocking poll for newly available I/O events from the OS.
126            this.poll_and_unblock(Some(Duration::ZERO))?;
127        }
128
129        // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern.
130        match link_name.as_str() {
131            // Environment related shims
132            "getenv" => {
133                let [name] = &this
134                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> *_), (link_name, abi, args))?;
135                let result = this.getenv(name)?;
136                this.write_pointer(result, dest)?;
137            }
138            "unsetenv" => {
139                let [name] = &this
140                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), (link_name, abi, args))?;
141                let result = this.unsetenv(name)?;
142                this.write_scalar(result, dest)?;
143            }
144            "setenv" => {
145                let [name, value, overwrite] = &this.check_shim_sig(
146                    shim_sig!(extern "C" fn(*_, *_, i32) -> i32),
147                    (link_name, abi, args),
148                )?;
149                this.read_scalar(overwrite)?.to_i32()?;
150                let result = this.setenv(name, value)?;
151                this.write_scalar(result, dest)?;
152            }
153            "getcwd" => {
154                let [buf, size] = &this.check_shim_sig(
155                    shim_sig!(extern "C" fn(*_, usize) -> *_),
156                    (link_name, abi, args),
157                )?;
158                let result = this.getcwd(buf, size)?;
159                this.write_pointer(result, dest)?;
160            }
161            "gethostname" => {
162                let [name, len] = &this.check_shim_sig(
163                    shim_sig!(extern "C" fn(*_, usize) -> i32),
164                    (link_name, abi, args),
165                )?;
166                let result = this.gethostname(name, len)?;
167                this.write_scalar(result, dest)?;
168            }
169            "chdir" => {
170                let [path] = &this
171                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), (link_name, abi, args))?;
172                let result = this.chdir(path)?;
173                this.write_scalar(result, dest)?;
174            }
175            "getpid" => {
176                let [] = &this.check_shim_sig(
177                    shim_sig!(extern "C" fn() -> libc::pid_t),
178                    (link_name, abi, args),
179                )?;
180                let result = this.getpid()?;
181                this.write_scalar(result, dest)?;
182            }
183            "uname" => {
184                // Not all Unixes have the `uname` symbol, e.g. FreeBSD does not.
185                this.check_target_os(
186                    &[Os::Linux, Os::Android, Os::MacOs, Os::Solaris, Os::Illumos],
187                    link_name,
188                )?;
189
190                let [uname] = &this.check_shim_sig(
191                    shim_sig!(extern "C" fn(*libc::utsname) -> i32),
192                    (link_name, abi, args),
193                )?;
194                let result = this.uname(uname, None)?;
195                this.write_scalar(result, dest)?;
196            }
197            "sysconf" => {
198                let [val] = &this.check_shim_sig(
199                    shim_sig!(extern "C" fn(i32) -> isize),
200                    (link_name, abi, args),
201                )?;
202                let result = this.sysconf(val)?;
203                this.write_scalar(result, dest)?;
204            }
205
206            // File descriptors
207            "read" => {
208                let [fd, buf, count] = &this.check_shim_sig(
209                    shim_sig!(extern "C" fn(i32, *_, usize) -> isize),
210                    (link_name, abi, args),
211                )?;
212                let fd = this.read_scalar(fd)?.to_i32()?;
213                let buf = this.read_pointer(buf)?;
214                let count = this.read_target_usize(count)?;
215                this.read(fd, buf, count, None, dest)?;
216            }
217            "write" => {
218                let [fd, buf, n] = &this.check_shim_sig(
219                    shim_sig!(extern "C" fn(i32, *_, usize) -> isize),
220                    (link_name, abi, args),
221                )?;
222                let fd = this.read_scalar(fd)?.to_i32()?;
223                let buf = this.read_pointer(buf)?;
224                let count = this.read_target_usize(n)?;
225                trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
226                this.write(fd, buf, count, None, dest)?;
227            }
228            "readv" => {
229                let [fd, iov, iovcnt] = &this.check_shim_sig(
230                    shim_sig!(extern "C" fn(i32, *libc::iovec, i32) -> isize),
231                    (link_name, abi, args),
232                )?;
233                this.readv(fd, iov, iovcnt, None, dest)?;
234            }
235            "writev" => {
236                let [fd, iov, iovcnt] = &this.check_shim_sig(
237                    shim_sig!(extern "C" fn(i32, *libc::iovec, i32) -> isize),
238                    (link_name, abi, args),
239                )?;
240                this.writev(fd, iov, iovcnt, None, dest)?;
241            }
242            "pread" => {
243                let [fd, buf, count, offset] = &this.check_shim_sig(
244                    shim_sig!(extern "C" fn(i32, *_, usize, libc::off_t) -> isize),
245                    (link_name, abi, args),
246                )?;
247                let fd = this.read_scalar(fd)?.to_i32()?;
248                let buf = this.read_pointer(buf)?;
249                let count = this.read_target_usize(count)?;
250                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
251                this.read(fd, buf, count, Some(offset), dest)?;
252            }
253            "pwrite" => {
254                let [fd, buf, n, offset] = &this.check_shim_sig(
255                    shim_sig!(extern "C" fn(i32, *_, usize, libc::off_t) -> isize),
256                    (link_name, abi, args),
257                )?;
258                let fd = this.read_scalar(fd)?.to_i32()?;
259                let buf = this.read_pointer(buf)?;
260                let count = this.read_target_usize(n)?;
261                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
262                trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
263                this.write(fd, buf, count, Some(offset), dest)?;
264            }
265            "preadv" => {
266                let [fd, iov, iovcnt, offset] = &this.check_shim_sig(
267                    shim_sig!(extern "C" fn(i32, *libc::iovec, i32, libc::off_t) -> isize),
268                    (link_name, abi, args),
269                )?;
270                this.readv(fd, iov, iovcnt, Some(offset), dest)?;
271            }
272            "pwritev" => {
273                let [fd, iov, iovcnt, offset] = &this.check_shim_sig(
274                    shim_sig!(extern "C" fn(i32, *libc::iovec, i32, libc::off_t) -> isize),
275                    (link_name, abi, args),
276                )?;
277                this.writev(fd, iov, iovcnt, Some(offset), dest)?;
278            }
279
280            "close" => {
281                let [fd] = &this
282                    .check_shim_sig(shim_sig!(extern "C" fn(i32) -> i32), (link_name, abi, args))?;
283                let fd = this.read_scalar(fd)?.to_i32()?;
284                let result = this.close(fd)?;
285                this.write_scalar(result, dest)?;
286            }
287            "fcntl" => {
288                let ([fd_num, cmd], varargs) = this.check_shim_sig_variadic(
289                    shim_sig!(extern "C" fn(i32, i32, ...) -> i32),
290                    (link_name, abi, args),
291                )?;
292                let result = this.fcntl(&fd_num, &cmd, varargs)?;
293                this.write_scalar(result, dest)?;
294            }
295            "dup" => {
296                let [old_fd] = &this
297                    .check_shim_sig(shim_sig!(extern "C" fn(i32) -> i32), (link_name, abi, args))?;
298                let old_fd = this.read_scalar(old_fd)?.to_i32()?;
299                let new_fd = this.dup(old_fd)?;
300                this.write_scalar(new_fd, dest)?;
301            }
302            "dup2" => {
303                let [old_fd, new_fd] = &this.check_shim_sig(
304                    shim_sig!(extern "C" fn(i32, i32) -> i32),
305                    (link_name, abi, args),
306                )?;
307                let old_fd = this.read_scalar(old_fd)?.to_i32()?;
308                let new_fd = this.read_scalar(new_fd)?.to_i32()?;
309                let result = this.dup2(old_fd, new_fd)?;
310                this.write_scalar(result, dest)?;
311            }
312            "flock" => {
313                // Currently this function does not exist on all Unixes, e.g. on Solaris.
314                this.check_target_os(
315                    &[Os::Linux, Os::Android, Os::FreeBsd, Os::MacOs, Os::Illumos],
316                    link_name,
317                )?;
318
319                let [fd, op] = &this.check_shim_sig(
320                    shim_sig!(extern "C" fn(i32, i32) -> i32),
321                    (link_name, abi, args),
322                )?;
323                let fd = this.read_scalar(fd)?.to_i32()?;
324                let op = this.read_scalar(op)?.to_i32()?;
325                let result = this.flock(fd, op)?;
326                this.write_scalar(result, dest)?;
327            }
328            "ioctl" => {
329                // The type of `op` depends on the libc. :(
330                // glibc, BSD use `unsigned long`, the rest uses `int`.
331                let op_is_ulong = match this.tcx.sess.target.os {
332                    Os::FreeBsd | Os::NetBsd | Os::MacOs => true,
333                    Os::Linux if this.tcx.sess.target.env == Env::Gnu => true,
334                    _ => false,
335                };
336                let ([fd, op], varargs) = this.check_shim_sig_variadic(
337                    if op_is_ulong {
338                        shim_sig!(extern "C" fn(i32, usize, ...) -> i32)
339                    } else {
340                        shim_sig!(extern "C" fn(i32, i32, ...) -> i32)
341                    },
342                    (link_name, abi, args),
343                )?;
344                let result = this.ioctl(&fd, &op, varargs)?;
345                this.write_scalar(result, dest)?;
346            }
347
348            // File and file system access
349            "open" => {
350                // `open` is variadic, the third argument is only present when the second argument
351                // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
352                let ([path_raw, flag], varargs) = this.check_shim_sig_variadic(
353                    shim_sig!(extern "C" fn(*_, i32, ...) -> i32),
354                    (link_name, abi, args),
355                )?;
356                let result = this.open(None, &path_raw, &flag, varargs)?;
357                this.write_scalar(result, dest)?;
358            }
359            "openat" => {
360                let ([dirfd, path_raw, flag], varargs) = this.check_shim_sig_variadic(
361                    shim_sig!(extern "C" fn(i32, *_, i32, ...) -> i32),
362                    (link_name, abi, args),
363                )?;
364                let result = this.open(Some(&dirfd), &path_raw, &flag, varargs)?;
365                this.write_scalar(result, dest)?;
366            }
367            "unlink" => {
368                // FIXME: This does not have a direct test (#3179).
369                let [path] = &this
370                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), (link_name, abi, args))?;
371                let result = this.unlink(path)?;
372                this.write_scalar(result, dest)?;
373            }
374            "symlink" => {
375                // FIXME: This does not have a direct test (#3179).
376                let [target, linkpath] = &this.check_shim_sig(
377                    shim_sig!(extern "C" fn(*_, *_) -> i32),
378                    (link_name, abi, args),
379                )?;
380                let result = this.symlink(target, linkpath)?;
381                this.write_scalar(result, dest)?;
382            }
383            "linkat" => {
384                let [oldfd, oldpath, newfd, newpath, flags] = &this.check_shim_sig(
385                    shim_sig!(extern "C" fn(i32, *_, i32, *_, i32) -> i32),
386                    (link_name, abi, args),
387                )?;
388                let result = this.linkat(oldfd, oldpath, newfd, newpath, flags)?;
389                this.write_scalar(result, dest)?;
390            }
391            "fstat" => {
392                let [fd, buf] = &this.check_shim_sig(
393                    shim_sig!(extern "C" fn(i32, *libc::stat) -> i32),
394                    (link_name, abi, args),
395                )?;
396                let result = this.fstat(fd, buf)?;
397                this.write_scalar(result, dest)?;
398            }
399            "lstat" => {
400                let [path, buf] = &this.check_shim_sig(
401                    shim_sig!(extern "C" fn(*_, *libc::stat) -> i32),
402                    (link_name, abi, args),
403                )?;
404                let result = this.lstat(path, buf)?;
405                this.write_scalar(result, dest)?;
406            }
407            "stat" => {
408                let [path, buf] = &this.check_shim_sig(
409                    shim_sig!(extern "C" fn(*_, *libc::stat) -> i32),
410                    (link_name, abi, args),
411                )?;
412                let result = this.stat(path, buf)?;
413                this.write_scalar(result, dest)?;
414            }
415            "fstatat" => {
416                let [dirfd, path, buf, flags] = &this.check_shim_sig(
417                    shim_sig!(extern "C" fn(i32, *_, *libc::stat, i32) -> i32),
418                    (link_name, abi, args),
419                )?;
420                let result = this.fstatat(dirfd, path, buf, flags)?;
421                this.write_scalar(result, dest)?;
422            }
423            "chmod" => {
424                let [path, mode] = &this.check_shim_sig(
425                    shim_sig!(extern "C" fn(*_, libc::mode_t) -> i32),
426                    (link_name, abi, args),
427                )?;
428                let result = this.chmod(path, mode)?;
429                this.write_scalar(result, dest)?;
430            }
431            "fchmod" => {
432                let [fd, mode] = &this.check_shim_sig(
433                    shim_sig!(extern "C" fn(i32, libc::mode_t) -> i32),
434                    (link_name, abi, args),
435                )?;
436                let result = this.fchmod(fd, mode)?;
437                this.write_scalar(result, dest)?;
438            }
439            "rename" => {
440                let [oldpath, newpath] = &this.check_shim_sig(
441                    shim_sig!(extern "C" fn(*_, *_) -> i32),
442                    (link_name, abi, args),
443                )?;
444                let result = this.rename(oldpath, newpath)?;
445                this.write_scalar(result, dest)?;
446            }
447            "mkdir" => {
448                let [path, mode] = &this.check_shim_sig(
449                    shim_sig!(extern "C" fn(*_, libc::mode_t) -> i32),
450                    (link_name, abi, args),
451                )?;
452                let result = this.mkdir(path, mode)?;
453                this.write_scalar(result, dest)?;
454            }
455            "rmdir" => {
456                let [path] = &this
457                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), (link_name, abi, args))?;
458                let result = this.rmdir(path)?;
459                this.write_scalar(result, dest)?;
460            }
461            "opendir" => {
462                let [name] = &this
463                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> *_), (link_name, abi, args))?;
464                let result = this.opendir(name)?;
465                this.write_scalar(result, dest)?;
466            }
467            "closedir" => {
468                let [dirp] = &this
469                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), (link_name, abi, args))?;
470                let result = this.closedir(dirp)?;
471                this.write_scalar(result, dest)?;
472            }
473            "readdir" => {
474                let [dirp] = &this
475                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> *_), (link_name, abi, args))?;
476                this.readdir(dirp, dest, "dirent")?;
477            }
478            "dirfd" => {
479                let [dirp] = &this
480                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), (link_name, abi, args))?;
481                this.dirfd(dirp, dest)?;
482            }
483            "lseek" => {
484                let [fd, offset, whence] = &this.check_shim_sig(
485                    shim_sig!(extern "C" fn(i32, libc::off_t, i32) -> libc::off_t),
486                    (link_name, abi, args),
487                )?;
488                let fd = this.read_scalar(fd)?.to_i32()?;
489                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
490                let whence = this.read_scalar(whence)?.to_i32()?;
491                this.lseek(fd, offset, whence, dest)?;
492            }
493            "ftruncate" => {
494                let [fd, length] = &this.check_shim_sig(
495                    shim_sig!(extern "C" fn(i32, libc::off_t) -> i32),
496                    (link_name, abi, args),
497                )?;
498                let fd = this.read_scalar(fd)?.to_i32()?;
499                let length = this.read_scalar(length)?.to_int(length.layout.size)?;
500                let result = this.ftruncate64(fd, length)?;
501                this.write_scalar(result, dest)?;
502            }
503            "fsync" => {
504                let [fd] = &this
505                    .check_shim_sig(shim_sig!(extern "C" fn(i32) -> i32), (link_name, abi, args))?;
506                let result = this.fsync(fd)?;
507                this.write_scalar(result, dest)?;
508            }
509            "fdatasync" => {
510                // fdatasync is not supported by macos.
511                this.check_target_os(
512                    &[Os::Linux, Os::FreeBsd, Os::NetBsd, Os::Solaris, Os::Illumos, Os::Android],
513                    link_name,
514                )?;
515                let [fd] = &this
516                    .check_shim_sig(shim_sig!(extern "C" fn(i32) -> i32), (link_name, abi, args))?;
517                let result = this.fdatasync(fd)?;
518                this.write_scalar(result, dest)?;
519            }
520            "futimens" => {
521                let [fd, times] = &this.check_shim_sig(
522                    shim_sig!(extern "C" fn(i32, *libc::timespec) -> i32),
523                    (link_name, abi, args),
524                )?;
525                let result = this.futimens(fd, times)?;
526                this.write_scalar(result, dest)?;
527            }
528            "utimensat" => {
529                let [dirfd, path, times, flags] = &this.check_shim_sig(
530                    shim_sig!(extern "C" fn(i32, *_, *libc::timespec, i32) -> i32),
531                    (link_name, abi, args),
532                )?;
533                let result = this.utimensat(dirfd, path, times, flags)?;
534                this.write_scalar(result, dest)?;
535            }
536            "readlink" => {
537                let [pathname, buf, bufsize] = &this.check_shim_sig(
538                    shim_sig!(extern "C" fn(*_, *_, usize) -> isize),
539                    (link_name, abi, args),
540                )?;
541                let result = this.readlink(pathname, buf, bufsize)?;
542                this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
543            }
544            "posix_fadvise" => {
545                let [fd, offset, len, advice] = &this.check_shim_sig(
546                    shim_sig!(extern "C" fn(i32, libc::off_t, libc::off_t, i32) -> i32),
547                    (link_name, abi, args),
548                )?;
549                this.read_scalar(fd)?.to_i32()?;
550                this.read_scalar(offset)?.to_int(offset.layout.size)?;
551                this.read_scalar(len)?.to_int(len.layout.size)?;
552                this.read_scalar(advice)?.to_i32()?;
553                // fadvise is only informational, we can ignore it.
554                this.write_int(0, dest)?;
555            }
556
557            "posix_fallocate" => {
558                // posix_fallocate is not supported by macos.
559                this.check_target_os(
560                    &[Os::Linux, Os::FreeBsd, Os::Solaris, Os::Illumos, Os::Android],
561                    link_name,
562                )?;
563
564                let [fd, offset, len] = &this.check_shim_sig(
565                    shim_sig!(extern "C" fn(i32, libc::off_t, libc::off_t) -> i32),
566                    (link_name, abi, args),
567                )?;
568
569                let fd = this.read_scalar(fd)?.to_i32()?;
570                // We don't support platforms which have libc::off_t bigger than 64 bits.
571                let offset =
572                    i64::try_from(this.read_scalar(offset)?.to_int(offset.layout.size)?).unwrap();
573                let len = i64::try_from(this.read_scalar(len)?.to_int(len.layout.size)?).unwrap();
574
575                let result = this.posix_fallocate(fd, offset, len)?;
576                this.write_scalar(result, dest)?;
577            }
578
579            "realpath" => {
580                let [path, resolved_path] = &this.check_shim_sig(
581                    shim_sig!(extern "C" fn(*_, *_) -> *_),
582                    (link_name, abi, args),
583                )?;
584                let result = this.realpath(path, resolved_path)?;
585                this.write_scalar(result, dest)?;
586            }
587            "mkstemp" => {
588                let [template] = &this
589                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), (link_name, abi, args))?;
590                let result = this.mkstemp(template)?;
591                this.write_scalar(result, dest)?;
592            }
593
594            // Poll
595            "poll" => {
596                let [fds, nfds, timeout] = &this.check_shim_sig(
597                    shim_sig!(extern "C" fn(*libc::pollfd, libc::nfds_t, i32) -> i32),
598                    (link_name, abi, args),
599                )?;
600                this.poll(fds, nfds, timeout, dest)?;
601            }
602
603            // Sockets and pipes
604            "socketpair" => {
605                let [domain, type_, protocol, sv] = &this.check_shim_sig(
606                    shim_sig!(extern "C" fn(i32, i32, i32, *i32) -> i32),
607                    (link_name, abi, args),
608                )?;
609                let result = this.socketpair(domain, type_, protocol, sv)?;
610                this.write_scalar(result, dest)?;
611            }
612            "pipe" => {
613                let [pipefd] = &this.check_shim_sig(
614                    shim_sig!(extern "C" fn(*i32) -> i32),
615                    (link_name, abi, args),
616                )?;
617                let result = this.pipe2(pipefd, /*flags*/ None)?;
618                this.write_scalar(result, dest)?;
619            }
620            "pipe2" => {
621                // Currently this function does not exist on all Unixes, e.g. on macOS.
622                this.check_target_os(
623                    &[Os::Linux, Os::Android, Os::FreeBsd, Os::Solaris, Os::Illumos],
624                    link_name,
625                )?;
626
627                let [pipefd, flags] = &this.check_shim_sig(
628                    shim_sig!(extern "C" fn(*i32, i32) -> i32),
629                    (link_name, abi, args),
630                )?;
631                let result = this.pipe2(pipefd, Some(flags))?;
632                this.write_scalar(result, dest)?;
633            }
634            "socket" => {
635                let [domain, type_, protocol] = &this.check_shim_sig(
636                    shim_sig!(extern "C" fn(i32, i32, i32) -> i32),
637                    (link_name, abi, args),
638                )?;
639                let result = this.socket(domain, type_, protocol)?;
640                this.write_scalar(result, dest)?;
641            }
642            "bind" => {
643                let [socket, address, address_len] = &this.check_shim_sig(
644                    shim_sig!(extern "C" fn(i32, *libc::sockaddr, libc::socklen_t) -> i32),
645                    (link_name, abi, args),
646                )?;
647                let result = this.bind(socket, address, address_len)?;
648                this.write_scalar(result, dest)?;
649            }
650            "listen" => {
651                let [socket, backlog] = &this.check_shim_sig(
652                    shim_sig!(extern "C" fn(i32, i32) -> i32),
653                    (link_name, abi, args),
654                )?;
655                let result = this.listen(socket, backlog)?;
656                this.write_scalar(result, dest)?;
657            }
658            "accept" => {
659                let [socket, address, address_len] = &this.check_shim_sig(
660                    shim_sig!(extern "C" fn(i32, *libc::sockaddr, *libc::socklen_t) -> i32),
661                    (link_name, abi, args),
662                )?;
663                this.accept4(socket, address, address_len, /* flags */ None, dest)?;
664            }
665            "accept4" => {
666                let [socket, address, address_len, flags] = &this.check_shim_sig(
667                    shim_sig!(extern "C" fn(i32, *libc::sockaddr, *libc::socklen_t, i32) -> i32),
668                    (link_name, abi, args),
669                )?;
670                this.accept4(socket, address, address_len, Some(flags), dest)?;
671            }
672            "connect" => {
673                let [socket, address, address_len] = &this.check_shim_sig(
674                    shim_sig!(extern "C" fn(i32, *libc::sockaddr, libc::socklen_t) -> i32),
675                    (link_name, abi, args),
676                )?;
677                this.connect(socket, address, address_len, dest)?;
678            }
679            "send" => {
680                let [socket, buffer, length, flags] = &this.check_shim_sig(
681                    shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32) -> libc::ssize_t),
682                    (link_name, abi, args),
683                )?;
684                this.send(socket, buffer, length, flags, /* dest_addr */ None, dest)?;
685            }
686            "sendto" => {
687                let [socket, buffer, length, flags, dest_addr, dest_addr_len] = &this.check_shim_sig(
688                    shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32, *libc::sockaddr, libc::socklen_t) -> libc::ssize_t),
689                    (link_name, abi, args),
690                )?;
691                this.send(socket, buffer, length, flags, Some((dest_addr, dest_addr_len)), dest)?;
692            }
693            "recv" => {
694                let [socket, buffer, length, flags] = &this.check_shim_sig(
695                    shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32) -> libc::ssize_t),
696                    (link_name, abi, args),
697                )?;
698                this.recv(socket, buffer, length, flags, /* dest_addr */ None, dest)?;
699            }
700            "recvfrom" => {
701                let [socket, buffer, length, flags, dest_addr, dest_addr_len] = &this.check_shim_sig(
702                    shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32, *libc::sockaddr, *libc::socklen_t) -> libc::ssize_t),
703                    (link_name, abi, args),
704                )?;
705                this.recv(socket, buffer, length, flags, Some((dest_addr, dest_addr_len)), dest)?;
706            }
707            "setsockopt" => {
708                let [socket, level, option_name, option_value, option_len] = &this.check_shim_sig(
709                    shim_sig!(extern "C" fn(i32, i32, i32, *_, libc::socklen_t) -> i32),
710                    (link_name, abi, args),
711                )?;
712                let result =
713                    this.setsockopt(socket, level, option_name, option_value, option_len)?;
714                this.write_scalar(result, dest)?;
715            }
716            "getsockopt" => {
717                let [socket, level, option_name, option_value, option_len] = &this.check_shim_sig(
718                    shim_sig!(extern "C" fn(i32, i32, i32, *_, *_) -> i32),
719                    (link_name, abi, args),
720                )?;
721                let result =
722                    this.getsockopt(socket, level, option_name, option_value, option_len)?;
723                this.write_scalar(result, dest)?;
724            }
725            "getsockname" => {
726                let [socket, address, address_len] = &this.check_shim_sig(
727                    shim_sig!(extern "C" fn(i32, *libc::sockaddr, *libc::socklen_t) -> i32),
728                    (link_name, abi, args),
729                )?;
730                let result = this.getsockname(socket, address, address_len)?;
731                this.write_scalar(result, dest)?;
732            }
733            "getpeername" => {
734                let [socket, address, address_len] = &this.check_shim_sig(
735                    shim_sig!(extern "C" fn(i32, *libc::sockaddr, *libc::socklen_t) -> i32),
736                    (link_name, abi, args),
737                )?;
738                this.getpeername(socket, address, address_len, dest)?;
739            }
740            "shutdown" => {
741                let [sockfd, how] = &this.check_shim_sig(
742                    shim_sig!(extern "C" fn(i32, i32) -> i32),
743                    (link_name, abi, args),
744                )?;
745                let result = this.shutdown(sockfd, how)?;
746                this.write_scalar(result, dest)?;
747            }
748            "getaddrinfo" => {
749                let [node, service, hints, res] = &this.check_shim_sig(
750                    shim_sig!(extern "C" fn(*_, *_, *_, **_) -> i32),
751                    (link_name, abi, args),
752                )?;
753                let result = this.getaddrinfo(node, service, hints, res)?;
754                this.write_scalar(result, dest)?;
755            }
756            "freeaddrinfo" => {
757                let [res] = &this
758                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> ()), (link_name, abi, args))?;
759                this.freeaddrinfo(res)?;
760            }
761
762            // Time
763            "gettimeofday" => {
764                let [tv, tz] = &this.check_shim_sig(
765                    shim_sig!(extern "C" fn(*libc::timeval, *libc::timezone) -> i32),
766                    (link_name, abi, args),
767                )?;
768                let result = this.gettimeofday(tv, tz)?;
769                this.write_scalar(result, dest)?;
770            }
771            "localtime_r" => {
772                let [timep, result_op] = &this.check_shim_sig(
773                    shim_sig!(extern "C" fn(*libc::time_t, *libc::tm) -> *libc::tm),
774                    (link_name, abi, args),
775                )?;
776                let result = this.localtime_r(timep, result_op)?;
777                this.write_pointer(result, dest)?;
778            }
779            "clock_gettime" => {
780                let [clk_id, tp] = &this.check_shim_sig(
781                    shim_sig!(extern "C" fn(libc::clockid_t, *libc::timespec) -> i32),
782                    (link_name, abi, args),
783                )?;
784                this.clock_gettime(clk_id, tp, dest)?;
785            }
786
787            // Allocation
788            "posix_memalign" => {
789                let [memptr, align, size] = &this.check_shim_sig(
790                    shim_sig!(extern "C" fn(**_, usize, usize) -> i32),
791                    (link_name, abi, args),
792                )?;
793                let result = this.posix_memalign(memptr, align, size)?;
794                this.write_scalar(result, dest)?;
795            }
796
797            "mmap" => {
798                let [addr, length, prot, flags, fd, offset] = &this.check_shim_sig(
799                    shim_sig!(extern "C" fn(*_, usize, i32, i32, i32, libc::off_t) -> *_),
800                    (link_name, abi, args),
801                )?;
802                let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
803                let ptr = this.mmap(addr, length, prot, flags, fd, offset)?;
804                this.write_scalar(ptr, dest)?;
805            }
806            "munmap" => {
807                let [addr, length] = &this.check_shim_sig(
808                    shim_sig!(extern "C" fn(*_, usize) -> i32),
809                    (link_name, abi, args),
810                )?;
811                let result = this.munmap(addr, length)?;
812                this.write_scalar(result, dest)?;
813            }
814            "mprotect" => {
815                let [addr, length, prot] = &this.check_shim_sig(
816                    shim_sig!(extern "C" fn(*_, usize, i32) -> i32),
817                    (link_name, abi, args),
818                )?;
819                let result = this.mprotect(addr, length, prot)?;
820                this.write_scalar(result, dest)?;
821            }
822            "madvise" => {
823                let [addr, length, advice] = &this.check_shim_sig(
824                    shim_sig!(extern "C" fn(*_, usize, i32) -> i32),
825                    (link_name, abi, args),
826                )?;
827                let result = this.madvise(addr, length, advice)?;
828                this.write_scalar(result, dest)?;
829            }
830
831            "reallocarray" => {
832                // Currently this function does not exist on all Unixes, e.g. on macOS.
833                this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?;
834
835                let [ptr, nmemb, size] = &this.check_shim_sig(
836                    shim_sig!(extern "C" fn(*_, usize, usize) -> *_),
837                    (link_name, abi, args),
838                )?;
839                let ptr = this.read_pointer(ptr)?;
840                let nmemb = this.read_target_usize(nmemb)?;
841                let size = this.read_target_usize(size)?;
842                // reallocarray checks a possible overflow and returns ENOMEM
843                // if that happens.
844                //
845                // Linux: https://www.unix.com/man-page/linux/3/reallocarray/
846                // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray
847                match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) {
848                    None => {
849                        this.set_last_error(LibcError("ENOMEM"))?;
850                        this.write_int(0, dest)?;
851                    }
852                    Some(len) => {
853                        let res = this.realloc(ptr, len.bytes())?;
854                        this.write_pointer(res, dest)?;
855                    }
856                }
857            }
858            "aligned_alloc" => {
859                // This is a C11 function, we assume all Unixes have it.
860                // (MSVC explicitly does not support this.)
861                let [align, size] = &this.check_shim_sig(
862                    shim_sig!(extern "C" fn(usize, usize) -> *_),
863                    (link_name, abi, args),
864                )?;
865                let res = this.aligned_alloc(align, size)?;
866                this.write_pointer(res, dest)?;
867            }
868
869            // Dynamic symbol loading
870            "dlsym" => {
871                let [handle, symbol] = &this.check_shim_sig(
872                    shim_sig!(extern "C" fn(*_, *_) -> *_),
873                    (link_name, abi, args),
874                )?;
875                this.read_target_usize(handle)?;
876                let symbol = this.read_pointer(symbol)?;
877                let name = this.read_c_str(symbol)?;
878                let Ok(name) = str::from_utf8(name) else {
879                    throw_unsup_format!("dlsym: non UTF-8 symbol name not supported")
880                };
881                if this.is_dyn_sym(name) {
882                    let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name)));
883                    this.write_pointer(ptr, dest)?;
884                } else if let Some(&ptr) = this.machine.extern_statics.get(&Symbol::intern(name)) {
885                    this.write_pointer(ptr, dest)?;
886                } else {
887                    this.write_int(0, dest)?;
888                }
889            }
890
891            // Thread-local storage
892            "pthread_key_create" => {
893                let [key, dtor] = &this.check_shim_sig(
894                    shim_sig!(extern "C" fn(*libc::pthread_key_t, fn(..) -> _) -> i32),
895                    (link_name, abi, args),
896                )?;
897                let key_place = this.deref_pointer(key)?;
898                let dtor = this.read_pointer(dtor)?;
899
900                // Extract the function type out of the signature (that seems easier than constructing it ourselves).
901                let dtor = if !this.ptr_is_null(dtor)? {
902                    Some((
903                        this.get_ptr_fn(dtor)?.as_instance()?,
904                        this.machine.current_user_relevant_span(),
905                    ))
906                } else {
907                    None
908                };
909
910                // Create key and write it into the memory where `key_ptr` wants it.
911                let key = this.machine.tls.create_tls_key(dtor, key_place.layout.size)?;
912                this.write_scalar(Scalar::from_uint(key, key_place.layout.size), &key_place)?;
913
914                // Return success (`0`).
915                this.write_int(0, dest)?;
916            }
917            "pthread_key_delete" => {
918                let [key] = &this.check_shim_sig(
919                    shim_sig!(extern "C" fn(libc::pthread_key_t) -> i32),
920                    (link_name, abi, args),
921                )?;
922                let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
923                this.machine.tls.delete_tls_key(key)?;
924                // Return success (0)
925                this.write_int(0, dest)?;
926            }
927            "pthread_getspecific" => {
928                let [key] = &this.check_shim_sig(
929                    shim_sig!(extern "C" fn(libc::pthread_key_t) -> *_),
930                    (link_name, abi, args),
931                )?;
932                let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
933                let active_thread = this.active_thread();
934                let ptr = this.machine.tls.load_tls_key(key, active_thread)?;
935                this.write_pointer(ptr, dest)?;
936            }
937            "pthread_setspecific" => {
938                let [key, new_ptr] = &this.check_shim_sig(
939                    shim_sig!(extern "C" fn(libc::pthread_key_t, *_) -> i32),
940                    (link_name, abi, args),
941                )?;
942                let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
943                let active_thread = this.active_thread();
944                let new_data = this.read_pointer(new_ptr)?;
945                this.machine.tls.store_tls_key(key, active_thread, new_data)?;
946
947                // Return success (`0`).
948                this.write_int(0, dest)?;
949            }
950
951            // Synchronization primitives
952            "pthread_mutexattr_init" => {
953                let [attr] = &this.check_shim_sig(
954                    shim_sig!(extern "C" fn(*libc::pthread_mutexattr_t) -> i32),
955                    (link_name, abi, args),
956                )?;
957                this.pthread_mutexattr_init(attr)?;
958                this.write_int(0, dest)?;
959            }
960            "pthread_mutexattr_settype" => {
961                let [attr, kind] = &this.check_shim_sig(
962                    shim_sig!(extern "C" fn(*libc::pthread_mutexattr_t, i32) -> i32),
963                    (link_name, abi, args),
964                )?;
965                let result = this.pthread_mutexattr_settype(attr, kind)?;
966                this.write_scalar(result, dest)?;
967            }
968            "pthread_mutexattr_destroy" => {
969                let [attr] = &this.check_shim_sig(
970                    shim_sig!(extern "C" fn(*libc::pthread_mutexattr_t) -> i32),
971                    (link_name, abi, args),
972                )?;
973                this.pthread_mutexattr_destroy(attr)?;
974                this.write_int(0, dest)?;
975            }
976            "pthread_mutex_init" => {
977                let [mutex, attr] = &this.check_shim_sig(
978                    shim_sig!(extern "C" fn(*libc::pthread_mutex_t, *libc::pthread_mutexattr_t) -> i32),
979                    (link_name, abi, args),
980                )?;
981                this.pthread_mutex_init(mutex, attr)?;
982                this.write_int(0, dest)?;
983            }
984            "pthread_mutex_lock" => {
985                let [mutex] = &this.check_shim_sig(
986                    shim_sig!(extern "C" fn(*libc::pthread_mutex_t) -> i32),
987                    (link_name, abi, args),
988                )?;
989                this.pthread_mutex_lock(mutex, dest)?;
990            }
991            "pthread_mutex_trylock" => {
992                let [mutex] = &this.check_shim_sig(
993                    shim_sig!(extern "C" fn(*libc::pthread_mutex_t) -> i32),
994                    (link_name, abi, args),
995                )?;
996                let result = this.pthread_mutex_trylock(mutex)?;
997                this.write_scalar(result, dest)?;
998            }
999            "pthread_mutex_unlock" => {
1000                let [mutex] = &this.check_shim_sig(
1001                    shim_sig!(extern "C" fn(*libc::pthread_mutex_t) -> i32),
1002                    (link_name, abi, args),
1003                )?;
1004                let result = this.pthread_mutex_unlock(mutex)?;
1005                this.write_scalar(result, dest)?;
1006            }
1007            "pthread_mutex_destroy" => {
1008                let [mutex] = &this.check_shim_sig(
1009                    shim_sig!(extern "C" fn(*libc::pthread_mutex_t) -> i32),
1010                    (link_name, abi, args),
1011                )?;
1012                this.pthread_mutex_destroy(mutex)?;
1013                this.write_int(0, dest)?;
1014            }
1015            "pthread_rwlock_rdlock" => {
1016                let [rwlock] = &this.check_shim_sig(
1017                    shim_sig!(extern "C" fn(*libc::pthread_rwlock_t) -> i32),
1018                    (link_name, abi, args),
1019                )?;
1020                this.pthread_rwlock_rdlock(rwlock, dest)?;
1021            }
1022            "pthread_rwlock_tryrdlock" => {
1023                let [rwlock] = &this.check_shim_sig(
1024                    shim_sig!(extern "C" fn(*libc::pthread_rwlock_t) -> i32),
1025                    (link_name, abi, args),
1026                )?;
1027                let result = this.pthread_rwlock_tryrdlock(rwlock)?;
1028                this.write_scalar(result, dest)?;
1029            }
1030            "pthread_rwlock_wrlock" => {
1031                let [rwlock] = &this.check_shim_sig(
1032                    shim_sig!(extern "C" fn(*libc::pthread_rwlock_t) -> i32),
1033                    (link_name, abi, args),
1034                )?;
1035                this.pthread_rwlock_wrlock(rwlock, dest)?;
1036            }
1037            "pthread_rwlock_trywrlock" => {
1038                let [rwlock] = &this.check_shim_sig(
1039                    shim_sig!(extern "C" fn(*libc::pthread_rwlock_t) -> i32),
1040                    (link_name, abi, args),
1041                )?;
1042                let result = this.pthread_rwlock_trywrlock(rwlock)?;
1043                this.write_scalar(result, dest)?;
1044            }
1045            "pthread_rwlock_unlock" => {
1046                let [rwlock] = &this.check_shim_sig(
1047                    shim_sig!(extern "C" fn(*libc::pthread_rwlock_t) -> i32),
1048                    (link_name, abi, args),
1049                )?;
1050                this.pthread_rwlock_unlock(rwlock)?;
1051                this.write_int(0, dest)?;
1052            }
1053            "pthread_rwlock_destroy" => {
1054                let [rwlock] = &this.check_shim_sig(
1055                    shim_sig!(extern "C" fn(*libc::pthread_rwlock_t) -> i32),
1056                    (link_name, abi, args),
1057                )?;
1058                this.pthread_rwlock_destroy(rwlock)?;
1059                this.write_int(0, dest)?;
1060            }
1061            "pthread_condattr_init" => {
1062                let [attr] = &this.check_shim_sig(
1063                    shim_sig!(extern "C" fn(*libc::pthread_condattr_t) -> i32),
1064                    (link_name, abi, args),
1065                )?;
1066                this.pthread_condattr_init(attr)?;
1067                this.write_int(0, dest)?;
1068            }
1069            "pthread_condattr_setclock" => {
1070                let [attr, clock_id] = &this.check_shim_sig(
1071                    shim_sig!(extern "C" fn(*libc::pthread_condattr_t, i32) -> i32),
1072                    (link_name, abi, args),
1073                )?;
1074                let result = this.pthread_condattr_setclock(attr, clock_id)?;
1075                this.write_scalar(result, dest)?;
1076            }
1077            "pthread_condattr_getclock" => {
1078                let [attr, clock_id] = &this.check_shim_sig(
1079                    shim_sig!(extern "C" fn(*libc::pthread_condattr_t, *libc::clockid_t) -> i32),
1080                    (link_name, abi, args),
1081                )?;
1082                this.pthread_condattr_getclock(attr, clock_id)?;
1083                this.write_int(0, dest)?;
1084            }
1085            "pthread_condattr_destroy" => {
1086                let [attr] = &this.check_shim_sig(
1087                    shim_sig!(extern "C" fn(*libc::pthread_condattr_t) -> i32),
1088                    (link_name, abi, args),
1089                )?;
1090                this.pthread_condattr_destroy(attr)?;
1091                this.write_int(0, dest)?;
1092            }
1093            "pthread_cond_init" => {
1094                let [cond, attr] = &this.check_shim_sig(
1095                    shim_sig!(extern "C" fn(*libc::pthread_cond_t, *libc::pthread_condattr_t) -> i32),
1096                    (link_name, abi, args),
1097                )?;
1098                this.pthread_cond_init(cond, attr)?;
1099                this.write_int(0, dest)?;
1100            }
1101            "pthread_cond_signal" => {
1102                let [cond] = &this.check_shim_sig(
1103                    shim_sig!(extern "C" fn(*libc::pthread_cond_t) -> i32),
1104                    (link_name, abi, args),
1105                )?;
1106                this.pthread_cond_signal(cond)?;
1107                this.write_int(0, dest)?;
1108            }
1109            "pthread_cond_broadcast" => {
1110                let [cond] = &this.check_shim_sig(
1111                    shim_sig!(extern "C" fn(*libc::pthread_cond_t) -> i32),
1112                    (link_name, abi, args),
1113                )?;
1114                this.pthread_cond_broadcast(cond)?;
1115                this.write_int(0, dest)?;
1116            }
1117            "pthread_cond_wait" => {
1118                let [cond, mutex] = &this.check_shim_sig(
1119                    shim_sig!(extern "C" fn(*libc::pthread_cond_t, *libc::pthread_mutex_t) -> i32),
1120                    (link_name, abi, args),
1121                )?;
1122                this.pthread_cond_wait(cond, mutex, dest)?;
1123            }
1124            "pthread_cond_timedwait" => {
1125                let [cond, mutex, abstime] = &this.check_shim_sig(
1126                    shim_sig!(extern "C" fn(*libc::pthread_cond_t, *libc::pthread_mutex_t, *libc::timespec) -> i32),
1127                    (link_name, abi, args),
1128                )?;
1129                this.pthread_cond_timedwait(
1130                    cond, mutex, abstime, dest, /* macos_relative_np */ false,
1131                )?;
1132            }
1133            "pthread_cond_destroy" => {
1134                let [cond] = &this.check_shim_sig(
1135                    shim_sig!(extern "C" fn(*libc::pthread_cond_t) -> i32),
1136                    (link_name, abi, args),
1137                )?;
1138                this.pthread_cond_destroy(cond)?;
1139                this.write_int(0, dest)?;
1140            }
1141            "sem_init" => {
1142                let [sem, pshared, value] = &this.check_shim_sig(
1143                    shim_sig!(extern "C" fn(*libc::sem_t, i32, u32) -> i32),
1144                    (link_name, abi, args),
1145                )?;
1146                this.pthread_sem_init(sem, pshared, value, dest)?;
1147            }
1148            "sem_wait" => {
1149                let [sem] = &this.check_shim_sig(
1150                    shim_sig!(extern "C" fn(*libc::sem_t) -> i32),
1151                    (link_name, abi, args),
1152                )?;
1153                this.pthread_sem_wait(sem, dest)?;
1154            }
1155            "sem_timedwait" => {
1156                let [sem, abs_timeout] = &this.check_shim_sig(
1157                    shim_sig!(extern "C" fn(*libc::sem_t, *libc::timespec) -> i32),
1158                    (link_name, abi, args),
1159                )?;
1160                this.pthread_sem_timedwait(sem, abs_timeout, dest)?;
1161            }
1162            "sem_post" => {
1163                let [sem] = &this.check_shim_sig(
1164                    shim_sig!(extern "C" fn(*libc::sem_t) -> i32),
1165                    (link_name, abi, args),
1166                )?;
1167                this.pthread_sem_post(sem, dest)?;
1168            }
1169            "sem_destroy" => {
1170                let [sem] = &this.check_shim_sig(
1171                    shim_sig!(extern "C" fn(*libc::sem_t) -> i32),
1172                    (link_name, abi, args),
1173                )?;
1174                this.pthread_sem_destroy(sem, dest)?;
1175            }
1176
1177            // Threading
1178            "pthread_create" => {
1179                let [thread, attr, start, arg] = &this.check_shim_sig(
1180                    shim_sig!(extern "C" fn(*libc::pthread_t, *_, fn(..) -> _, *_) -> i32),
1181                    (link_name, abi, args),
1182                )?;
1183                this.pthread_create(thread, attr, start, arg)?;
1184                this.write_int(0, dest)?;
1185            }
1186            "pthread_join" => {
1187                let [thread, retval] = &this.check_shim_sig(
1188                    shim_sig!(extern "C" fn(libc::pthread_t, *_) -> i32),
1189                    (link_name, abi, args),
1190                )?;
1191                this.pthread_join(thread, retval, dest)?;
1192            }
1193            "pthread_detach" => {
1194                let [thread] = &this.check_shim_sig(
1195                    shim_sig!(extern "C" fn(libc::pthread_t) -> i32),
1196                    (link_name, abi, args),
1197                )?;
1198                let res = this.pthread_detach(thread)?;
1199                this.write_scalar(res, dest)?;
1200            }
1201            "pthread_self" => {
1202                let [] = &this.check_shim_sig(
1203                    shim_sig!(extern "C" fn() -> libc::pthread_t),
1204                    (link_name, abi, args),
1205                )?;
1206                let res = this.pthread_self()?;
1207                this.write_scalar(res, dest)?;
1208            }
1209            "sched_yield" => {
1210                // FIXME: This does not have a direct test (#3179).
1211                let [] = &this
1212                    .check_shim_sig(shim_sig!(extern "C" fn() -> i32), (link_name, abi, args))?;
1213                this.sched_yield()?;
1214                this.write_int(0, dest)?;
1215            }
1216            "nanosleep" => {
1217                let [duration, rem] = &this.check_shim_sig(
1218                    shim_sig!(extern "C" fn(*libc::timespec, *_) -> i32),
1219                    (link_name, abi, args),
1220                )?;
1221                let result = this.nanosleep(duration, rem)?;
1222                this.write_scalar(result, dest)?;
1223            }
1224            "clock_nanosleep" => {
1225                // Currently this function does not exist on all Unixes, e.g. on macOS.
1226                this.check_target_os(
1227                    &[Os::FreeBsd, Os::Linux, Os::Android, Os::Solaris, Os::Illumos],
1228                    link_name,
1229                )?;
1230
1231                let [clock_id, flags, req, rem] = &this.check_shim_sig(
1232                    shim_sig!(extern "C" fn(libc::clockid_t, i32, *libc::timespec, *_) -> i32),
1233                    (link_name, abi, args),
1234                )?;
1235                let result = this.clock_nanosleep(clock_id, flags, req, rem)?;
1236                this.write_scalar(result, dest)?;
1237            }
1238            "sched_getaffinity" => {
1239                // Currently this function does not exist on all Unixes, e.g. on macOS.
1240                this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?;
1241
1242                let [pid, cpusetsize, mask] = &this.check_shim_sig(
1243                    shim_sig!(extern "C" fn(libc::pid_t, usize, *_) -> i32),
1244                    (link_name, abi, args),
1245                )?;
1246                this.sched_getaffinity(pid, cpusetsize, mask, dest)?;
1247            }
1248            "sched_setaffinity" => {
1249                // Currently this function does not exist on all Unixes, e.g. on macOS.
1250                this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?;
1251
1252                let [pid, cpusetsize, mask] = &this.check_shim_sig(
1253                    shim_sig!(extern "C" fn(libc::pid_t, usize, *_) -> i32),
1254                    (link_name, abi, args),
1255                )?;
1256                this.sched_setaffinity(pid, cpusetsize, mask, dest)?;
1257            }
1258
1259            // Miscellaneous
1260            "isatty" => {
1261                let [fd] = &this
1262                    .check_shim_sig(shim_sig!(extern "C" fn(i32) -> i32), (link_name, abi, args))?;
1263                let result = this.isatty(fd)?;
1264                this.write_scalar(result, dest)?;
1265            }
1266            "pthread_atfork" => {
1267                // FIXME: This does not have a direct test (#3179).
1268                let [prepare, parent, child] = &this.check_shim_sig(
1269                    shim_sig!(extern "C" fn(fn(..) -> _, fn(..) -> _, fn(..) -> _) -> i32),
1270                    (link_name, abi, args),
1271                )?;
1272                this.read_pointer(prepare)?;
1273                this.read_pointer(parent)?;
1274                this.read_pointer(child)?;
1275                // We do not support forking, so there is nothing to do here.
1276                this.write_int(0, dest)?;
1277            }
1278            "strerror_r" => {
1279                let [errnum, buf, buflen] = &this.check_shim_sig(
1280                    shim_sig!(extern "C" fn(i32, *_, usize) -> i32),
1281                    (link_name, abi, args),
1282                )?;
1283                let result = this.strerror_r(errnum, buf, buflen)?;
1284                this.write_scalar(result, dest)?;
1285            }
1286            "getentropy" => {
1287                // This function is non-standard but exists with the same signature and behavior on
1288                // Linux, macOS, FreeBSD and Solaris/Illumos.
1289                this.check_target_os(
1290                    &[Os::Linux, Os::MacOs, Os::FreeBsd, Os::Illumos, Os::Solaris, Os::Android],
1291                    link_name,
1292                )?;
1293
1294                let [buf, bufsize] = &this.check_shim_sig(
1295                    shim_sig!(extern "C" fn(*_, usize) -> i32),
1296                    (link_name, abi, args),
1297                )?;
1298                let buf = this.read_pointer(buf)?;
1299                let bufsize = this.read_target_usize(bufsize)?;
1300
1301                // getentropy sets errno to EIO when the buffer size exceeds 256 bytes.
1302                // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=getentropy&sektion=3&format=html
1303                // Linux: https://man7.org/linux/man-pages/man3/getentropy.3.html
1304                // macOS: https://keith.github.io/xcode-man-pages/getentropy.2.html
1305                // Solaris/Illumos: https://illumos.org/man/3C/getentropy
1306                if bufsize > 256 {
1307                    this.set_errno_and_return_neg1(LibcError("EIO"), dest)?;
1308                } else {
1309                    this.gen_random(buf, bufsize)?;
1310                    this.write_int(0, dest)?;
1311                }
1312            }
1313            "getrandom" => {
1314                // This function is non-standard but exists with the same signature and behavior on
1315                // Linux, FreeBSD and Solaris/Illumos.
1316                this.check_target_os(
1317                    &[Os::Linux, Os::FreeBsd, Os::Illumos, Os::Solaris, Os::Android],
1318                    link_name,
1319                )?;
1320
1321                let [ptr, len, flags] = &this.check_shim_sig(
1322                    shim_sig!(extern "C" fn(*_, usize, u32) -> isize),
1323                    (link_name, abi, args),
1324                )?;
1325                let ptr = this.read_pointer(ptr)?;
1326                let len = this.read_target_usize(len)?;
1327                let _flags = this.read_scalar(flags)?.to_i32()?;
1328                // We ignore the flags, just always use the same PRNG / host RNG.
1329                this.gen_random(ptr, len)?;
1330                this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
1331            }
1332            "arc4random_buf" => {
1333                // This function is non-standard but exists with the same signature and
1334                // same behavior (eg never fails) on FreeBSD and Solaris/Illumos.
1335                this.check_target_os(&[Os::FreeBsd, Os::Illumos, Os::Solaris], link_name)?;
1336
1337                let [ptr, len] = &this.check_shim_sig(
1338                    shim_sig!(extern "C" fn(*_, usize) -> ()),
1339                    (link_name, abi, args),
1340                )?;
1341                let ptr = this.read_pointer(ptr)?;
1342                let len = this.read_target_usize(len)?;
1343                this.gen_random(ptr, len)?;
1344            }
1345            "_Unwind_RaiseException" => {
1346                // This is not formally part of POSIX, but it is very wide-spread on POSIX systems.
1347                // It was originally specified as part of the Itanium C++ ABI:
1348                // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw.
1349                // On Linux it is
1350                // documented as part of the LSB:
1351                // https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib--unwind-raiseexception.html
1352                // Basically every other UNIX uses the exact same api though. Arm also references
1353                // back to the Itanium C++ ABI for the definition of `_Unwind_RaiseException` for
1354                // arm64:
1355                // https://github.com/ARM-software/abi-aa/blob/main/cppabi64/cppabi64.rst#toc-entry-35
1356                // For arm32 they did something custom, but similar enough that the same
1357                // `_Unwind_RaiseException` impl in miri should work:
1358                // https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst
1359                this.check_target_os(
1360                    &[Os::Linux, Os::FreeBsd, Os::Illumos, Os::Solaris, Os::Android, Os::MacOs],
1361                    link_name,
1362                )?;
1363
1364                // This function looks and behaves exactly like miri_start_unwind.
1365                let [payload] = &this.check_shim_sig(
1366                    // Look up the return type via `panic_unwind::`, not via `unwind::`, as
1367                    // the latter it not always unique.
1368                    shim_sig!(extern "C" fn(*_) -> panic_unwind::imp::uw::_Unwind_Reason_Code),
1369                    (link_name, abi, args),
1370                )?;
1371                this.handle_miri_start_unwind(payload)?;
1372                return interp_ok(EmulateItemResult::NeedsUnwind);
1373            }
1374            "getuid" | "geteuid" => {
1375                let [] = &this.check_shim_sig(
1376                    shim_sig!(extern "C" fn() -> libc::uid_t),
1377                    (link_name, abi, args),
1378                )?;
1379                // For now, just pretend we always have this fixed UID.
1380                this.write_int(UID, dest)?;
1381            }
1382            "pause" => {
1383                let [] = &this
1384                    .check_shim_sig(shim_sig!(extern "C" fn() -> i32), (link_name, abi, args))?;
1385                // This blocks the current thread until a signal gets delivered. We don't support
1386                // signals, so we block forever.
1387                this.block_thread(
1388                    BlockReason::Pause,
1389                    None,
1390                    callback!(@capture<'tcx> {}
1391                    |_ecx, _unblock: UnblockKind| {
1392                        panic!("should never unblock")
1393                    }),
1394                );
1395            }
1396
1397            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
1398            // These shims are enabled only when the caller is in the standard library.
1399            "pthread_attr_getguardsize" if this.frame_in_std() => {
1400                let [_attr, guard_size] = &this.check_shim_sig(
1401                    shim_sig!(extern "C" fn(*_, *usize) -> i32),
1402                    (link_name, abi, args),
1403                )?;
1404                let guard_size = this.deref_pointer(guard_size)?;
1405                this.write_scalar(
1406                    Scalar::from_uint(this.machine.page_size, guard_size.layout.size),
1407                    &guard_size,
1408                )?;
1409
1410                // Return success (`0`).
1411                this.write_int(0, dest)?;
1412            }
1413
1414            "pthread_attr_init" | "pthread_attr_destroy" if this.frame_in_std() => {
1415                let [_] = &this
1416                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), (link_name, abi, args))?;
1417                this.write_int(0, dest)?;
1418            }
1419
1420            "pthread_attr_setstacksize" if this.frame_in_std() => {
1421                let [_, _] = &this.check_shim_sig(
1422                    shim_sig!(extern "C" fn(*_, usize) -> i32),
1423                    (link_name, abi, args),
1424                )?;
1425                this.write_int(0, dest)?;
1426            }
1427
1428            "pthread_attr_getstack" if this.frame_in_std() => {
1429                // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here.
1430                // Hence we can mostly ignore the input `attr_place`.
1431                let [attr_place, addr_place, size_place] = &this.check_shim_sig(
1432                    shim_sig!(extern "C" fn(*libc::pthread_attr_t, *usize, *usize) -> i32),
1433                    (link_name, abi, args),
1434                )?;
1435                let _attr_place = this.deref_pointer(attr_place)?;
1436                let addr_place = this.deref_pointer(addr_place)?;
1437                let size_place = this.deref_pointer(size_place)?;
1438
1439                this.write_scalar(
1440                    Scalar::from_uint(this.machine.stack_addr, this.pointer_size()),
1441                    &addr_place,
1442                )?;
1443                this.write_scalar(
1444                    Scalar::from_uint(this.machine.stack_size, this.pointer_size()),
1445                    &size_place,
1446                )?;
1447
1448                // Return success (`0`).
1449                this.write_int(0, dest)?;
1450            }
1451
1452            "signal" if this.frame_in_std() => {
1453                let [_, _] = &this.check_shim_sig(
1454                    shim_sig!(extern "C" fn(i32, libc::sighandler_t) -> libc::sighandler_t),
1455                    (link_name, abi, args),
1456                )?;
1457                this.write_int(0, dest)?;
1458            }
1459            "sigaltstack" if this.frame_in_std() => {
1460                let [_, _] = &this.check_shim_sig(
1461                    shim_sig!(extern "C" fn(*_, *_) -> i32),
1462                    (link_name, abi, args),
1463                )?;
1464                this.write_int(0, dest)?;
1465            }
1466            "sigaction" if this.frame_in_std() => {
1467                let [_, _, _] = &this.check_shim_sig(
1468                    shim_sig!(extern "C" fn(i32, *_, *_) -> i32),
1469                    (link_name, abi, args),
1470                )?;
1471                this.write_int(0, dest)?;
1472            }
1473
1474            "getpwuid_r" | "__posix_getpwuid_r" if this.frame_in_std() => {
1475                // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish
1476                let [uid, pwd, buf, buflen, result] = &this.check_shim_sig(
1477                    shim_sig!(extern "C" fn(libc::uid_t, *libc::passwd, *_, usize, **_) -> i32),
1478                    (link_name, abi, args),
1479                )?;
1480                this.check_no_isolation("`getpwuid_r`")?;
1481
1482                let uid = this.read_scalar(uid)?.to_u32()?;
1483                let pwd = this.deref_pointer(pwd)?;
1484                let buf = this.read_pointer(buf)?;
1485                let buflen = this.read_target_usize(buflen)?;
1486                let result = this.deref_pointer(result)?;
1487
1488                // Must be for "us".
1489                if uid != UID {
1490                    throw_unsup_format!("`getpwuid_r` on other users is not supported");
1491                }
1492
1493                // Reset all fields to `uninit` to make sure nobody reads them.
1494                // (This is a std-only shim so we are okay with such hacks.)
1495                this.write_uninit(&pwd)?;
1496
1497                // We only set the home_dir field.
1498                #[allow(deprecated)]
1499                let home_dir = std::env::home_dir().unwrap();
1500                let (written, _) = this.write_path_to_c_str(&home_dir, buf, buflen)?;
1501                let pw_dir = this.project_field_named(&pwd, "pw_dir")?;
1502                this.write_pointer(buf, &pw_dir)?;
1503
1504                if written {
1505                    this.write_pointer(pwd.ptr(), &result)?;
1506                    this.write_int(0, dest)?;
1507                } else {
1508                    this.write_int(0, &result)?;
1509                    this.write_scalar(this.eval_libc("ERANGE"), dest)?;
1510                }
1511            }
1512
1513            // Platform-specific shims
1514            _ => {
1515                let target_os = &this.tcx.sess.target.os;
1516                return match target_os {
1517                    Os::Android =>
1518                        android::EvalContextExt::emulate_foreign_item_inner(
1519                            this, link_name, abi, args, dest,
1520                        ),
1521                    Os::FreeBsd =>
1522                        freebsd::EvalContextExt::emulate_foreign_item_inner(
1523                            this, link_name, abi, args, dest,
1524                        ),
1525                    Os::Linux =>
1526                        linux::EvalContextExt::emulate_foreign_item_inner(
1527                            this, link_name, abi, args, dest,
1528                        ),
1529                    Os::MacOs =>
1530                        macos::EvalContextExt::emulate_foreign_item_inner(
1531                            this, link_name, abi, args, dest,
1532                        ),
1533                    Os::Solaris | Os::Illumos =>
1534                        solarish::EvalContextExt::emulate_foreign_item_inner(
1535                            this, link_name, abi, args, dest,
1536                        ),
1537                    Os::NetBsd =>
1538                        netbsd::EvalContextExt::emulate_foreign_item_inner(
1539                            this, link_name, abi, args, dest,
1540                        ),
1541                    _ => interp_ok(EmulateItemResult::NotSupported),
1542                };
1543            }
1544        };
1545
1546        interp_ok(EmulateItemResult::NeedsReturn)
1547    }
1548}