Skip to main content

miri/shims/unix/
foreign_items.rs

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