Skip to main content

miri/shims/unix/
foreign_items.rs

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