miri/shims/unix/
foreign_items.rs

1use std::ffi::OsStr;
2use std::str;
3
4use rustc_abi::{ExternAbi, Size};
5use rustc_middle::ty::Ty;
6use rustc_middle::ty::layout::LayoutOf;
7use rustc_span::Symbol;
8use rustc_target::callconv::{Conv, FnAbi};
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::*;
19
20pub fn is_dyn_sym(name: &str, target_os: &str) -> bool {
21    match name {
22        // Used for tests.
23        "isatty" => 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                "android" => android::is_dyn_sym(name),
33                "freebsd" => freebsd::is_dyn_sym(name),
34                "linux" => linux::is_dyn_sym(name),
35                "macos" => macos::is_dyn_sym(name),
36                "solaris" | "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_abi(
116                    link_name,
117                    abi,
118                    ExternAbi::C { unwind: false },
119                    [this.machine.layouts.const_raw_ptr.ty],
120                    this.machine.layouts.mut_raw_ptr.ty,
121                    args,
122                )?;
123                let result = this.getenv(name)?;
124                this.write_pointer(result, dest)?;
125            }
126            "unsetenv" => {
127                let [name] = this.check_shim_abi(
128                    link_name,
129                    abi,
130                    ExternAbi::C { unwind: false },
131                    [this.machine.layouts.const_raw_ptr.ty],
132                    this.tcx.types.i32,
133                    args,
134                )?;
135                let result = this.unsetenv(name)?;
136                this.write_scalar(result, dest)?;
137            }
138            "setenv" => {
139                let [name, value, overwrite] = this.check_shim_abi(
140                    link_name,
141                    abi,
142                    ExternAbi::C { unwind: false },
143                    [
144                        this.machine.layouts.const_raw_ptr.ty,
145                        this.machine.layouts.const_raw_ptr.ty,
146                        this.tcx.types.i32,
147                    ],
148                    this.tcx.types.i32,
149                    args,
150                )?;
151                this.read_scalar(overwrite)?.to_i32()?;
152                let result = this.setenv(name, value)?;
153                this.write_scalar(result, dest)?;
154            }
155            "getcwd" => {
156                let [buf, size] = this.check_shim_abi(
157                    link_name,
158                    abi,
159                    ExternAbi::C { unwind: false },
160                    [this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.usize],
161                    this.machine.layouts.mut_raw_ptr.ty,
162                    args,
163                )?;
164                let result = this.getcwd(buf, size)?;
165                this.write_pointer(result, dest)?;
166            }
167            "chdir" => {
168                let [path] = this.check_shim_abi(
169                    link_name,
170                    abi,
171                    ExternAbi::C { unwind: false },
172                    [this.machine.layouts.const_raw_ptr.ty],
173                    this.tcx.types.i32,
174                    args,
175                )?;
176                let result = this.chdir(path)?;
177                this.write_scalar(result, dest)?;
178            }
179            "getpid" => {
180                let [] = this.check_shim_abi(
181                    link_name,
182                    abi,
183                    ExternAbi::C { unwind: false },
184                    [],
185                    this.libc_ty_layout("pid_t").ty,
186                    args,
187                )?;
188                let result = this.getpid()?;
189                this.write_scalar(result, dest)?;
190            }
191            "sysconf" => {
192                let [val] = this.check_shim_abi(
193                    link_name,
194                    abi,
195                    ExternAbi::C { unwind: false },
196                    [this.tcx.types.i32],
197                    this.tcx.types.isize,
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_abi(
206                    link_name,
207                    abi,
208                    ExternAbi::C { unwind: false },
209                    [this.tcx.types.i32, this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.usize],
210                    this.tcx.types.isize,
211                    args,
212                )?;
213                let fd = this.read_scalar(fd)?.to_i32()?;
214                let buf = this.read_pointer(buf)?;
215                let count = this.read_target_usize(count)?;
216                this.read(fd, buf, count, None, dest)?;
217            }
218            "write" => {
219                let [fd, buf, n] = this.check_shim_abi(
220                    link_name,
221                    abi,
222                    ExternAbi::C { unwind: false },
223                    [
224                        this.tcx.types.i32,
225                        this.machine.layouts.const_raw_ptr.ty,
226                        this.tcx.types.usize,
227                    ],
228                    this.tcx.types.isize,
229                    args,
230                )?;
231                let fd = this.read_scalar(fd)?.to_i32()?;
232                let buf = this.read_pointer(buf)?;
233                let count = this.read_target_usize(n)?;
234                trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
235                this.write(fd, buf, count, None, dest)?;
236            }
237            "pread" => {
238                let off_t = this.libc_ty_layout("off_t");
239                let [fd, buf, count, offset] = this.check_shim_abi(
240                    link_name,
241                    abi,
242                    ExternAbi::C { unwind: false },
243                    [
244                        this.tcx.types.i32,
245                        this.machine.layouts.mut_raw_ptr.ty,
246                        this.tcx.types.usize,
247                        off_t.ty,
248                    ],
249                    this.tcx.types.isize,
250                    args,
251                )?;
252                let fd = this.read_scalar(fd)?.to_i32()?;
253                let buf = this.read_pointer(buf)?;
254                let count = this.read_target_usize(count)?;
255                let offset = this.read_scalar(offset)?.to_int(off_t.size)?;
256                this.read(fd, buf, count, Some(offset), dest)?;
257            }
258            "pwrite" => {
259                let off_t = this.libc_ty_layout("off_t");
260                let [fd, buf, n, offset] = this.check_shim_abi(
261                    link_name,
262                    abi,
263                    ExternAbi::C { unwind: false },
264                    [
265                        this.tcx.types.i32,
266                        this.machine.layouts.const_raw_ptr.ty,
267                        this.tcx.types.usize,
268                        off_t.ty,
269                    ],
270                    this.tcx.types.isize,
271                    args,
272                )?;
273                let fd = this.read_scalar(fd)?.to_i32()?;
274                let buf = this.read_pointer(buf)?;
275                let count = this.read_target_usize(n)?;
276                let offset = this.read_scalar(offset)?.to_int(off_t.size)?;
277                trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
278                this.write(fd, buf, count, Some(offset), dest)?;
279            }
280            "pread64" => {
281                let off64_t = this.libc_ty_layout("off64_t");
282                let [fd, buf, count, offset] = this.check_shim_abi(
283                    link_name,
284                    abi,
285                    ExternAbi::C { unwind: false },
286                    [
287                        this.tcx.types.i32,
288                        this.machine.layouts.mut_raw_ptr.ty,
289                        this.tcx.types.usize,
290                        off64_t.ty,
291                    ],
292                    this.tcx.types.isize,
293                    args,
294                )?;
295                let fd = this.read_scalar(fd)?.to_i32()?;
296                let buf = this.read_pointer(buf)?;
297                let count = this.read_target_usize(count)?;
298                let offset = this.read_scalar(offset)?.to_int(off64_t.size)?;
299                this.read(fd, buf, count, Some(offset), dest)?;
300            }
301            "pwrite64" => {
302                let off64_t = this.libc_ty_layout("off64_t");
303                let [fd, buf, n, offset] = this.check_shim_abi(
304                    link_name,
305                    abi,
306                    ExternAbi::C { unwind: false },
307                    [
308                        this.tcx.types.i32,
309                        this.machine.layouts.const_raw_ptr.ty,
310                        this.tcx.types.usize,
311                        off64_t.ty,
312                    ],
313                    this.tcx.types.isize,
314                    args,
315                )?;
316                let fd = this.read_scalar(fd)?.to_i32()?;
317                let buf = this.read_pointer(buf)?;
318                let count = this.read_target_usize(n)?;
319                let offset = this.read_scalar(offset)?.to_int(off64_t.size)?;
320                trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
321                this.write(fd, buf, count, Some(offset), dest)?;
322            }
323            "close" => {
324                let [fd] = this.check_shim_abi(
325                    link_name,
326                    abi,
327                    ExternAbi::C { unwind: false },
328                    [this.tcx.types.i32],
329                    this.tcx.types.i32,
330                    args,
331                )?;
332                let result = this.close(fd)?;
333                this.write_scalar(result, dest)?;
334            }
335            "fcntl" => {
336                let ([fd_num, cmd], varargs) =
337                    this.check_shim_variadic(abi, Conv::C, link_name, args)?;
338                let result = this.fcntl(fd_num, cmd, varargs)?;
339                this.write_scalar(result, dest)?;
340            }
341            "dup" => {
342                let [old_fd] = this.check_shim_abi(
343                    link_name,
344                    abi,
345                    ExternAbi::C { unwind: false },
346                    [this.tcx.types.i32],
347                    this.tcx.types.i32,
348                    args,
349                )?;
350                let old_fd = this.read_scalar(old_fd)?.to_i32()?;
351                let new_fd = this.dup(old_fd)?;
352                this.write_scalar(new_fd, dest)?;
353            }
354            "dup2" => {
355                let [old_fd, new_fd] = this.check_shim_abi(
356                    link_name,
357                    abi,
358                    ExternAbi::C { unwind: false },
359                    [this.tcx.types.i32, this.tcx.types.i32],
360                    this.tcx.types.i32,
361                    args,
362                )?;
363                let old_fd = this.read_scalar(old_fd)?.to_i32()?;
364                let new_fd = this.read_scalar(new_fd)?.to_i32()?;
365                let result = this.dup2(old_fd, new_fd)?;
366                this.write_scalar(result, dest)?;
367            }
368            "flock" => {
369                // Currently this function does not exist on all Unixes, e.g. on Solaris.
370                this.check_target_os(&["linux", "freebsd", "macos", "illumos"], link_name)?;
371                let [fd, op] = this.check_shim_abi(
372                    link_name,
373                    abi,
374                    ExternAbi::C { unwind: false },
375                    [this.tcx.types.i32, this.tcx.types.i32],
376                    this.tcx.types.i32,
377                    args,
378                )?;
379                let fd = this.read_scalar(fd)?.to_i32()?;
380                let op = this.read_scalar(op)?.to_i32()?;
381                let result = this.flock(fd, op)?;
382                this.write_scalar(result, dest)?;
383            }
384
385            // File and file system access
386            "open" | "open64" => {
387                // `open` is variadic, the third argument is only present when the second argument
388                // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
389                let ([path_raw, flag], varargs) =
390                    this.check_shim_variadic(abi, Conv::C, link_name, args)?;
391                let result = this.open(path_raw, flag, varargs)?;
392                this.write_scalar(result, dest)?;
393            }
394            "unlink" => {
395                let [path] = this.check_shim_abi(
396                    link_name,
397                    abi,
398                    ExternAbi::C { unwind: false },
399                    [this.machine.layouts.const_raw_ptr.ty],
400                    this.tcx.types.i32,
401                    args,
402                )?;
403                let result = this.unlink(path)?;
404                this.write_scalar(result, dest)?;
405            }
406            "symlink" => {
407                let [target, linkpath] = this.check_shim_abi(
408                    link_name,
409                    abi,
410                    ExternAbi::C { unwind: false },
411                    [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.const_raw_ptr.ty],
412                    this.tcx.types.i32,
413                    args,
414                )?;
415                let result = this.symlink(target, linkpath)?;
416                this.write_scalar(result, dest)?;
417            }
418            "rename" => {
419                let [oldpath, newpath] = this.check_shim_abi(
420                    link_name,
421                    abi,
422                    ExternAbi::C { unwind: false },
423                    [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.const_raw_ptr.ty],
424                    this.tcx.types.i32,
425                    args,
426                )?;
427                let result = this.rename(oldpath, newpath)?;
428                this.write_scalar(result, dest)?;
429            }
430            "mkdir" => {
431                let [path, mode] = this.check_shim_abi(
432                    link_name,
433                    abi,
434                    ExternAbi::C { unwind: false },
435                    [this.machine.layouts.const_raw_ptr.ty, this.libc_ty_layout("mode_t").ty],
436                    this.tcx.types.i32,
437                    args,
438                )?;
439                let result = this.mkdir(path, mode)?;
440                this.write_scalar(result, dest)?;
441            }
442            "rmdir" => {
443                let [path] = this.check_shim_abi(
444                    link_name,
445                    abi,
446                    ExternAbi::C { unwind: false },
447                    [this.machine.layouts.const_raw_ptr.ty],
448                    this.tcx.types.i32,
449                    args,
450                )?;
451                let result = this.rmdir(path)?;
452                this.write_scalar(result, dest)?;
453            }
454            "opendir" => {
455                let [name] = this.check_shim_abi(
456                    link_name,
457                    abi,
458                    ExternAbi::C { unwind: false },
459                    [this.machine.layouts.const_raw_ptr.ty],
460                    this.machine.layouts.mut_raw_ptr.ty,
461                    args,
462                )?;
463                let result = this.opendir(name)?;
464                this.write_scalar(result, dest)?;
465            }
466            "closedir" => {
467                let [dirp] = this.check_shim_abi(
468                    link_name,
469                    abi,
470                    ExternAbi::C { unwind: false },
471                    [this.machine.layouts.mut_raw_ptr.ty],
472                    this.tcx.types.i32,
473                    args,
474                )?;
475                let result = this.closedir(dirp)?;
476                this.write_scalar(result, dest)?;
477            }
478            "lseek64" => {
479                let off64_t = this.libc_ty_layout("off64_t");
480                let [fd, offset, whence] = this.check_shim_abi(
481                    link_name,
482                    abi,
483                    ExternAbi::C { unwind: false },
484                    [this.tcx.types.i32, off64_t.ty, this.tcx.types.i32],
485                    off64_t.ty,
486                    args,
487                )?;
488                let fd = this.read_scalar(fd)?.to_i32()?;
489                let offset = this.read_scalar(offset)?.to_int(off64_t.size)?;
490                let whence = this.read_scalar(whence)?.to_i32()?;
491                this.lseek64(fd, offset, whence, dest)?;
492            }
493            "lseek" => {
494                let off_t = this.libc_ty_layout("off_t");
495                let [fd, offset, whence] = this.check_shim_abi(
496                    link_name,
497                    abi,
498                    ExternAbi::C { unwind: false },
499                    [this.tcx.types.i32, off_t.ty, this.tcx.types.i32],
500                    off_t.ty,
501                    args,
502                )?;
503                let fd = this.read_scalar(fd)?.to_i32()?;
504                let offset = this.read_scalar(offset)?.to_int(off_t.size)?;
505                let whence = this.read_scalar(whence)?.to_i32()?;
506                this.lseek64(fd, offset, whence, dest)?;
507            }
508            "ftruncate64" => {
509                let off64_t = this.libc_ty_layout("off64_t");
510                let [fd, length] = this.check_shim_abi(
511                    link_name,
512                    abi,
513                    ExternAbi::C { unwind: false },
514                    [this.tcx.types.i32, off64_t.ty],
515                    this.tcx.types.i32,
516                    args,
517                )?;
518                let fd = this.read_scalar(fd)?.to_i32()?;
519                let length = this.read_scalar(length)?.to_int(off64_t.size)?;
520                let result = this.ftruncate64(fd, length)?;
521                this.write_scalar(result, dest)?;
522            }
523            "ftruncate" => {
524                let off_t = this.libc_ty_layout("off_t");
525                let [fd, length] = this.check_shim_abi(
526                    link_name,
527                    abi,
528                    ExternAbi::C { unwind: false },
529                    [this.tcx.types.i32, off_t.ty],
530                    this.tcx.types.i32,
531                    args,
532                )?;
533                let fd = this.read_scalar(fd)?.to_i32()?;
534                let length = this.read_scalar(length)?.to_int(off_t.size)?;
535                let result = this.ftruncate64(fd, length)?;
536                this.write_scalar(result, dest)?;
537            }
538            "fsync" => {
539                let [fd] = this.check_shim_abi(
540                    link_name,
541                    abi,
542                    ExternAbi::C { unwind: false },
543                    [this.tcx.types.i32],
544                    this.tcx.types.i32,
545                    args,
546                )?;
547                let result = this.fsync(fd)?;
548                this.write_scalar(result, dest)?;
549            }
550            "fdatasync" => {
551                let [fd] = this.check_shim_abi(
552                    link_name,
553                    abi,
554                    ExternAbi::C { unwind: false },
555                    [this.tcx.types.i32],
556                    this.tcx.types.i32,
557                    args,
558                )?;
559                let result = this.fdatasync(fd)?;
560                this.write_scalar(result, dest)?;
561            }
562            "readlink" => {
563                let [pathname, buf, bufsize] = this.check_shim_abi(
564                    link_name,
565                    abi,
566                    ExternAbi::C { unwind: false },
567                    [
568                        this.machine.layouts.const_raw_ptr.ty,
569                        this.machine.layouts.mut_raw_ptr.ty,
570                        this.tcx.types.usize,
571                    ],
572                    this.tcx.types.isize,
573                    args,
574                )?;
575                let result = this.readlink(pathname, buf, bufsize)?;
576                this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
577            }
578            "posix_fadvise" => {
579                let off_t = this.libc_ty_layout("off_t");
580                let [fd, offset, len, advice] = this.check_shim_abi(
581                    link_name,
582                    abi,
583                    ExternAbi::C { unwind: false },
584                    [this.tcx.types.i32, off_t.ty, off_t.ty, this.tcx.types.i32],
585                    this.tcx.types.i32,
586                    args,
587                )?;
588                this.read_scalar(fd)?.to_i32()?;
589                this.read_scalar(offset)?.to_int(off_t.size)?;
590                this.read_scalar(len)?.to_int(off_t.size)?;
591                this.read_scalar(advice)?.to_i32()?;
592                // fadvise is only informational, we can ignore it.
593                this.write_null(dest)?;
594            }
595            "realpath" => {
596                let [path, resolved_path] = this.check_shim_abi(
597                    link_name,
598                    abi,
599                    ExternAbi::C { unwind: false },
600                    [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty],
601                    this.machine.layouts.mut_raw_ptr.ty,
602                    args,
603                )?;
604                let result = this.realpath(path, resolved_path)?;
605                this.write_scalar(result, dest)?;
606            }
607            "mkstemp" => {
608                let [template] = this.check_shim_abi(
609                    link_name,
610                    abi,
611                    ExternAbi::C { unwind: false },
612                    [this.machine.layouts.mut_raw_ptr.ty],
613                    this.tcx.types.i32,
614                    args,
615                )?;
616                let result = this.mkstemp(template)?;
617                this.write_scalar(result, dest)?;
618            }
619
620            // Unnamed sockets and pipes
621            "socketpair" => {
622                let [domain, type_, protocol, sv] = this.check_shim_abi(
623                    link_name,
624                    abi,
625                    ExternAbi::C { unwind: false },
626                    [
627                        this.tcx.types.i32,
628                        this.tcx.types.i32,
629                        this.tcx.types.i32,
630                        this.machine.layouts.mut_raw_ptr.ty,
631                    ],
632                    this.tcx.types.i32,
633                    args,
634                )?;
635                let result = this.socketpair(domain, type_, protocol, sv)?;
636                this.write_scalar(result, dest)?;
637            }
638            "pipe" => {
639                let [pipefd] = this.check_shim_abi(
640                    link_name,
641                    abi,
642                    ExternAbi::C { unwind: false },
643                    [this.machine.layouts.mut_raw_ptr.ty],
644                    this.tcx.types.i32,
645                    args,
646                )?;
647                let result = this.pipe2(pipefd, /*flags*/ None)?;
648                this.write_scalar(result, dest)?;
649            }
650            "pipe2" => {
651                // Currently this function does not exist on all Unixes, e.g. on macOS.
652                this.check_target_os(&["linux", "freebsd", "solaris", "illumos"], link_name)?;
653                let [pipefd, flags] = this.check_shim_abi(
654                    link_name,
655                    abi,
656                    ExternAbi::C { unwind: false },
657                    [this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.i32],
658                    this.tcx.types.i32,
659                    args,
660                )?;
661                let result = this.pipe2(pipefd, Some(flags))?;
662                this.write_scalar(result, dest)?;
663            }
664
665            // Time
666            "gettimeofday" => {
667                let [tv, tz] = this.check_shim_abi(
668                    link_name,
669                    abi,
670                    ExternAbi::C { unwind: false },
671                    [this.machine.layouts.mut_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty],
672                    this.tcx.types.i32,
673                    args,
674                )?;
675                let result = this.gettimeofday(tv, tz)?;
676                this.write_scalar(result, dest)?;
677            }
678            "localtime_r" => {
679                let [timep, result_op] = this.check_shim_abi(
680                    link_name,
681                    abi,
682                    ExternAbi::C { unwind: false },
683                    [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty],
684                    this.machine.layouts.mut_raw_ptr.ty,
685                    args,
686                )?;
687                let result = this.localtime_r(timep, result_op)?;
688                this.write_pointer(result, dest)?;
689            }
690            "clock_gettime" => {
691                let [clk_id, tp] = this.check_shim_abi(
692                    link_name,
693                    abi,
694                    ExternAbi::C { unwind: false },
695                    [this.libc_ty_layout("clockid_t").ty, this.machine.layouts.mut_raw_ptr.ty],
696                    this.tcx.types.i32,
697                    args,
698                )?;
699                this.clock_gettime(clk_id, tp, dest)?;
700            }
701
702            // Allocation
703            "posix_memalign" => {
704                let [memptr, align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
705                let result = this.posix_memalign(memptr, align, size)?;
706                this.write_scalar(result, dest)?;
707            }
708
709            "mmap" => {
710                let [addr, length, prot, flags, fd, offset] =
711                    this.check_shim(abi, Conv::C, link_name, args)?;
712                let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
713                let ptr = this.mmap(addr, length, prot, flags, fd, offset)?;
714                this.write_scalar(ptr, dest)?;
715            }
716            "munmap" => {
717                let [addr, length] = this.check_shim(abi, Conv::C, link_name, args)?;
718                let result = this.munmap(addr, length)?;
719                this.write_scalar(result, dest)?;
720            }
721
722            "reallocarray" => {
723                // Currently this function does not exist on all Unixes, e.g. on macOS.
724                this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
725                let [ptr, nmemb, size] = this.check_shim(abi, Conv::C, link_name, args)?;
726                let ptr = this.read_pointer(ptr)?;
727                let nmemb = this.read_target_usize(nmemb)?;
728                let size = this.read_target_usize(size)?;
729                // reallocarray checks a possible overflow and returns ENOMEM
730                // if that happens.
731                //
732                // Linux: https://www.unix.com/man-page/linux/3/reallocarray/
733                // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray
734                match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) {
735                    None => {
736                        this.set_last_error(LibcError("ENOMEM"))?;
737                        this.write_null(dest)?;
738                    }
739                    Some(len) => {
740                        let res = this.realloc(ptr, len.bytes())?;
741                        this.write_pointer(res, dest)?;
742                    }
743                }
744            }
745            "aligned_alloc" => {
746                // This is a C11 function, we assume all Unixes have it.
747                // (MSVC explicitly does not support this.)
748                let [align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
749                let res = this.aligned_alloc(align, size)?;
750                this.write_pointer(res, dest)?;
751            }
752
753            // Dynamic symbol loading
754            "dlsym" => {
755                let [handle, symbol] = this.check_shim(abi, Conv::C, link_name, args)?;
756                this.read_target_usize(handle)?;
757                let symbol = this.read_pointer(symbol)?;
758                let name = this.read_c_str(symbol)?;
759                if let Ok(name) = str::from_utf8(name)
760                    && is_dyn_sym(name, &this.tcx.sess.target.os)
761                {
762                    let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name)));
763                    this.write_pointer(ptr, dest)?;
764                } else {
765                    this.write_null(dest)?;
766                }
767            }
768
769            // Thread-local storage
770            "pthread_key_create" => {
771                let [key, dtor] = this.check_shim(abi, Conv::C, link_name, args)?;
772                let key_place = this.deref_pointer_as(key, this.libc_ty_layout("pthread_key_t"))?;
773                let dtor = this.read_pointer(dtor)?;
774
775                // Extract the function type out of the signature (that seems easier than constructing it ourselves).
776                let dtor = if !this.ptr_is_null(dtor)? {
777                    Some(this.get_ptr_fn(dtor)?.as_instance()?)
778                } else {
779                    None
780                };
781
782                // Figure out how large a pthread TLS key actually is.
783                // To this end, deref the argument type. This is `libc::pthread_key_t`.
784                let key_type = key.layout.ty
785                    .builtin_deref(true)
786                    .ok_or_else(|| err_ub_format!(
787                        "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
788                    ))?;
789                let key_layout = this.layout_of(key_type)?;
790
791                // Create key and write it into the memory where `key_ptr` wants it.
792                let key = this.machine.tls.create_tls_key(dtor, key_layout.size)?;
793                this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place)?;
794
795                // Return success (`0`).
796                this.write_null(dest)?;
797            }
798            "pthread_key_delete" => {
799                let [key] = this.check_shim(abi, Conv::C, link_name, args)?;
800                let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
801                this.machine.tls.delete_tls_key(key)?;
802                // Return success (0)
803                this.write_null(dest)?;
804            }
805            "pthread_getspecific" => {
806                let [key] = this.check_shim(abi, Conv::C, link_name, args)?;
807                let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
808                let active_thread = this.active_thread();
809                let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
810                this.write_scalar(ptr, dest)?;
811            }
812            "pthread_setspecific" => {
813                let [key, new_ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
814                let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
815                let active_thread = this.active_thread();
816                let new_data = this.read_scalar(new_ptr)?;
817                this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
818
819                // Return success (`0`).
820                this.write_null(dest)?;
821            }
822
823            // Synchronization primitives
824            "pthread_mutexattr_init" => {
825                let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
826                this.pthread_mutexattr_init(attr)?;
827                this.write_null(dest)?;
828            }
829            "pthread_mutexattr_settype" => {
830                let [attr, kind] = this.check_shim(abi, Conv::C, link_name, args)?;
831                let result = this.pthread_mutexattr_settype(attr, kind)?;
832                this.write_scalar(result, dest)?;
833            }
834            "pthread_mutexattr_destroy" => {
835                let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
836                this.pthread_mutexattr_destroy(attr)?;
837                this.write_null(dest)?;
838            }
839            "pthread_mutex_init" => {
840                let [mutex, attr] = this.check_shim(abi, Conv::C, link_name, args)?;
841                this.pthread_mutex_init(mutex, attr)?;
842                this.write_null(dest)?;
843            }
844            "pthread_mutex_lock" => {
845                let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
846                this.pthread_mutex_lock(mutex, dest)?;
847            }
848            "pthread_mutex_trylock" => {
849                let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
850                let result = this.pthread_mutex_trylock(mutex)?;
851                this.write_scalar(result, dest)?;
852            }
853            "pthread_mutex_unlock" => {
854                let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
855                let result = this.pthread_mutex_unlock(mutex)?;
856                this.write_scalar(result, dest)?;
857            }
858            "pthread_mutex_destroy" => {
859                let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
860                this.pthread_mutex_destroy(mutex)?;
861                this.write_int(0, dest)?;
862            }
863            "pthread_rwlock_rdlock" => {
864                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
865                this.pthread_rwlock_rdlock(rwlock, dest)?;
866            }
867            "pthread_rwlock_tryrdlock" => {
868                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
869                let result = this.pthread_rwlock_tryrdlock(rwlock)?;
870                this.write_scalar(result, dest)?;
871            }
872            "pthread_rwlock_wrlock" => {
873                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
874                this.pthread_rwlock_wrlock(rwlock, dest)?;
875            }
876            "pthread_rwlock_trywrlock" => {
877                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
878                let result = this.pthread_rwlock_trywrlock(rwlock)?;
879                this.write_scalar(result, dest)?;
880            }
881            "pthread_rwlock_unlock" => {
882                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
883                this.pthread_rwlock_unlock(rwlock)?;
884                this.write_null(dest)?;
885            }
886            "pthread_rwlock_destroy" => {
887                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
888                this.pthread_rwlock_destroy(rwlock)?;
889                this.write_null(dest)?;
890            }
891            "pthread_condattr_init" => {
892                let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
893                this.pthread_condattr_init(attr)?;
894                this.write_null(dest)?;
895            }
896            "pthread_condattr_setclock" => {
897                let [attr, clock_id] = this.check_shim(abi, Conv::C, link_name, args)?;
898                let result = this.pthread_condattr_setclock(attr, clock_id)?;
899                this.write_scalar(result, dest)?;
900            }
901            "pthread_condattr_getclock" => {
902                let [attr, clock_id] = this.check_shim(abi, Conv::C, link_name, args)?;
903                this.pthread_condattr_getclock(attr, clock_id)?;
904                this.write_null(dest)?;
905            }
906            "pthread_condattr_destroy" => {
907                let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
908                this.pthread_condattr_destroy(attr)?;
909                this.write_null(dest)?;
910            }
911            "pthread_cond_init" => {
912                let [cond, attr] = this.check_shim(abi, Conv::C, link_name, args)?;
913                this.pthread_cond_init(cond, attr)?;
914                this.write_null(dest)?;
915            }
916            "pthread_cond_signal" => {
917                let [cond] = this.check_shim(abi, Conv::C, link_name, args)?;
918                this.pthread_cond_signal(cond)?;
919                this.write_null(dest)?;
920            }
921            "pthread_cond_broadcast" => {
922                let [cond] = this.check_shim(abi, Conv::C, link_name, args)?;
923                this.pthread_cond_broadcast(cond)?;
924                this.write_null(dest)?;
925            }
926            "pthread_cond_wait" => {
927                let [cond, mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
928                this.pthread_cond_wait(cond, mutex, dest)?;
929            }
930            "pthread_cond_timedwait" => {
931                let [cond, mutex, abstime] = this.check_shim(abi, Conv::C, link_name, args)?;
932                this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
933            }
934            "pthread_cond_destroy" => {
935                let [cond] = this.check_shim(abi, Conv::C, link_name, args)?;
936                this.pthread_cond_destroy(cond)?;
937                this.write_null(dest)?;
938            }
939
940            // Threading
941            "pthread_create" => {
942                let [thread, attr, start, arg] = this.check_shim(abi, Conv::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, Conv::C, link_name, args)?;
948                let res = this.pthread_join(thread, retval)?;
949                this.write_scalar(res, dest)?;
950            }
951            "pthread_detach" => {
952                let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
953                let res = this.pthread_detach(thread)?;
954                this.write_scalar(res, dest)?;
955            }
956            "pthread_self" => {
957                let [] = this.check_shim(abi, Conv::C, link_name, args)?;
958                let res = this.pthread_self()?;
959                this.write_scalar(res, dest)?;
960            }
961            "sched_yield" => {
962                let [] = this.check_shim(abi, Conv::C, link_name, args)?;
963                this.sched_yield()?;
964                this.write_null(dest)?;
965            }
966            "nanosleep" => {
967                let [req, rem] = this.check_shim(abi, Conv::C, link_name, args)?;
968                let result = this.nanosleep(req, rem)?;
969                this.write_scalar(result, dest)?;
970            }
971            "sched_getaffinity" => {
972                // Currently this function does not exist on all Unixes, e.g. on macOS.
973                this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
974                let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
975                let pid = this.read_scalar(pid)?.to_u32()?;
976                let cpusetsize = this.read_target_usize(cpusetsize)?;
977                let mask = this.read_pointer(mask)?;
978
979                // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
980                let thread_id = match pid {
981                    0 => this.active_thread(),
982                    _ =>
983                        throw_unsup_format!(
984                            "`sched_getaffinity` is only supported with a pid of 0 (indicating the current thread)"
985                        ),
986                };
987
988                // The mask is stored in chunks, and the size must be a whole number of chunks.
989                let chunk_size = CpuAffinityMask::chunk_size(this);
990
991                if this.ptr_is_null(mask)? {
992                    this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
993                } else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 {
994                    // we only copy whole chunks of size_of::<c_ulong>()
995                    this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
996                } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) {
997                    let cpuset = cpuset.clone();
998                    // we only copy whole chunks of size_of::<c_ulong>()
999                    let byte_count =
1000                        Ord::min(cpuset.as_slice().len(), cpusetsize.try_into().unwrap());
1001                    this.write_bytes_ptr(mask, cpuset.as_slice()[..byte_count].iter().copied())?;
1002                    this.write_null(dest)?;
1003                } else {
1004                    // The thread whose ID is pid could not be found
1005                    this.set_last_error_and_return(LibcError("ESRCH"), dest)?;
1006                }
1007            }
1008            "sched_setaffinity" => {
1009                // Currently this function does not exist on all Unixes, e.g. on macOS.
1010                this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
1011                let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
1012                let pid = this.read_scalar(pid)?.to_u32()?;
1013                let cpusetsize = this.read_target_usize(cpusetsize)?;
1014                let mask = this.read_pointer(mask)?;
1015
1016                // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
1017                let thread_id = match pid {
1018                    0 => this.active_thread(),
1019                    _ =>
1020                        throw_unsup_format!(
1021                            "`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)"
1022                        ),
1023                };
1024
1025                if this.ptr_is_null(mask)? {
1026                    this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
1027                } else {
1028                    // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
1029                    // Any unspecified bytes are treated as zero here (none of the CPUs are configured).
1030                    // This is not exactly documented, so we assume that this is the behavior in practice.
1031                    let bits_slice =
1032                        this.read_bytes_ptr_strip_provenance(mask, Size::from_bytes(cpusetsize))?;
1033                    // This ignores the bytes beyond `CpuAffinityMask::CPU_MASK_BYTES`
1034                    let bits_array: [u8; CpuAffinityMask::CPU_MASK_BYTES] =
1035                        std::array::from_fn(|i| bits_slice.get(i).copied().unwrap_or(0));
1036                    match CpuAffinityMask::from_array(this, this.machine.num_cpus, bits_array) {
1037                        Some(cpuset) => {
1038                            this.machine.thread_cpu_affinity.insert(thread_id, cpuset);
1039                            this.write_null(dest)?;
1040                        }
1041                        None => {
1042                            // The intersection between the mask and the available CPUs was empty.
1043                            this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
1044                        }
1045                    }
1046                }
1047            }
1048
1049            // Miscellaneous
1050            "isatty" => {
1051                let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
1052                let result = this.isatty(fd)?;
1053                this.write_scalar(result, dest)?;
1054            }
1055            "pthread_atfork" => {
1056                let [prepare, parent, child] = this.check_shim(abi, Conv::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, Conv::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, Conv::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, Conv::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, Conv::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, Conv::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, Conv::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, Conv::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, Conv::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, Conv::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, Conv::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, Conv::C, link_name, args)?;
1195                this.write_null(dest)?;
1196            }
1197            "sigaction" | "mprotect" if this.frame_in_std() => {
1198                let [_, _, _] = this.check_shim(abi, Conv::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, Conv::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}