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