miri/shims/unix/
foreign_items.rs

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