Skip to main content

miri/shims/unix/
foreign_items.rs

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