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