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