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