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