1use rustc_abi::CanonAbi;
2use rustc_middle::ty::Ty;
3use rustc_span::Symbol;
4use rustc_target::callconv::FnAbi;
5
6use super::sync::{EvalContextExt as _, MacOsFutexTimeout};
7use crate::shims::unix::*;
8use crate::*;
9
10pub fn is_dyn_sym(name: &str) -> bool {
11 match name {
12 "os_sync_wait_on_address"
14 | "os_sync_wait_on_address_with_deadline"
15 | "os_sync_wait_on_address_with_timeout"
16 | "os_sync_wake_by_address_any"
17 | "os_sync_wake_by_address_all" => true,
18 _ => false,
19 }
20}
21
22impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
23pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
24 fn emulate_foreign_item_inner(
25 &mut self,
26 link_name: Symbol,
27 abi: &FnAbi<'tcx, Ty<'tcx>>,
28 args: &[OpTy<'tcx>],
29 dest: &MPlaceTy<'tcx>,
30 ) -> InterpResult<'tcx, EmulateItemResult> {
31 let this = self.eval_context_mut();
32
33 match link_name.as_str() {
36 "__error" => {
38 let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
39 let errno_place = this.last_error_place()?;
40 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
41 }
42
43 "close$NOCANCEL" => {
45 let [result] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
46 let result = this.close(result)?;
47 this.write_scalar(result, dest)?;
48 }
49 "stat" | "stat64" | "stat$INODE64" => {
50 let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
51 let result = this.macos_fbsd_solarish_stat(path, buf)?;
52 this.write_scalar(result, dest)?;
53 }
54 "lstat" | "lstat64" | "lstat$INODE64" => {
55 let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
56 let result = this.macos_fbsd_solarish_lstat(path, buf)?;
57 this.write_scalar(result, dest)?;
58 }
59 "fstat" | "fstat64" | "fstat$INODE64" => {
60 let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
61 let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
62 this.write_scalar(result, dest)?;
63 }
64 "opendir$INODE64" => {
65 let [name] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
66 let result = this.opendir(name)?;
67 this.write_scalar(result, dest)?;
68 }
69 "readdir_r" | "readdir_r$INODE64" => {
70 let [dirp, entry, result] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
71 let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
72 this.write_scalar(result, dest)?;
73 }
74 "realpath$DARWIN_EXTSN" => {
75 let [path, resolved_path] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
76 let result = this.realpath(path, resolved_path)?;
77 this.write_scalar(result, dest)?;
78 }
79 "ioctl" => {
80 let ([fd_num, cmd], varargs) =
81 this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?;
82 let result = this.ioctl(fd_num, cmd, varargs)?;
83 this.write_scalar(result, dest)?;
84 }
85
86 "_NSGetEnviron" => {
88 let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
89 let environ = this.machine.env_vars.unix().environ();
90 this.write_pointer(environ, dest)?;
91 }
92
93 "CCRandomGenerateBytes" => {
95 let [bytes, count] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
96 let bytes = this.read_pointer(bytes)?;
97 let count = this.read_target_usize(count)?;
98 let success = this.eval_libc_i32("kCCSuccess");
99 this.gen_random(bytes, count)?;
100 this.write_int(success, dest)?;
101 }
102
103 "mach_absolute_time" => {
105 let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
106 let result = this.mach_absolute_time()?;
107 this.write_scalar(result, dest)?;
108 }
109
110 "mach_timebase_info" => {
111 let [info] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
112 let result = this.mach_timebase_info(info)?;
113 this.write_scalar(result, dest)?;
114 }
115
116 "_NSGetArgc" => {
118 let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
119 this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?;
120 }
121 "_NSGetArgv" => {
122 let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
123 this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
124 }
125 "_NSGetExecutablePath" => {
126 let [buf, bufsize] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
127 this.check_no_isolation("`_NSGetExecutablePath`")?;
128
129 let buf_ptr = this.read_pointer(buf)?;
130 let bufsize = this.deref_pointer_as(bufsize, this.machine.layouts.u32)?;
131
132 let path = std::env::current_exe().unwrap();
135 let (written, size_needed) = this.write_path_to_c_str(
136 &path,
137 buf_ptr,
138 this.read_scalar(&bufsize)?.to_u32()?.into(),
139 )?;
140
141 if written {
142 this.write_null(dest)?;
143 } else {
144 this.write_scalar(Scalar::from_u32(size_needed.try_into().unwrap()), &bufsize)?;
145 this.write_int(-1, dest)?;
146 }
147 }
148
149 "_tlv_atexit" => {
151 let [dtor, data] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
152 let dtor = this.read_pointer(dtor)?;
153 let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
154 let data = this.read_scalar(data)?;
155 let active_thread = this.active_thread();
156 this.machine.tls.add_macos_thread_dtor(active_thread, dtor, data)?;
157 }
158
159 "pthread_get_stackaddr_np" => {
161 let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
162 this.read_target_usize(thread)?;
163 let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
164 this.write_scalar(stack_addr, dest)?;
165 }
166 "pthread_get_stacksize_np" => {
167 let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
168 this.read_target_usize(thread)?;
169 let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
170 this.write_scalar(stack_size, dest)?;
171 }
172
173 "pthread_setname_np" => {
175 let [name] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
176
177 let thread = this.pthread_self()?;
187 let res = match this.pthread_setname_np(
188 thread,
189 this.read_scalar(name)?,
190 this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?,
191 false,
192 )? {
193 ThreadNameResult::Ok => Scalar::from_u32(0),
194 ThreadNameResult::NameTooLong => this.eval_libc("ENAMETOOLONG"),
195 ThreadNameResult::ThreadNotFound => unreachable!(),
196 };
197 this.write_scalar(res, dest)?;
200 }
201 "pthread_getname_np" => {
202 let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
203
204 let res = match this.pthread_getname_np(
213 this.read_scalar(thread)?,
214 this.read_scalar(name)?,
215 this.read_scalar(len)?,
216 true,
217 )? {
218 ThreadNameResult::Ok => Scalar::from_u32(0),
219 ThreadNameResult::NameTooLong => Scalar::from_u32(0),
221 ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
222 };
223 this.write_scalar(res, dest)?;
224 }
225
226 "os_sync_wait_on_address" => {
228 let [addr_op, value_op, size_op, flags_op] =
229 this.check_shim(abi, CanonAbi::C, link_name, args)?;
230 this.os_sync_wait_on_address(
231 addr_op,
232 value_op,
233 size_op,
234 flags_op,
235 MacOsFutexTimeout::None,
236 dest,
237 )?;
238 }
239 "os_sync_wait_on_address_with_deadline" => {
240 let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
241 this.check_shim(abi, CanonAbi::C, link_name, args)?;
242 this.os_sync_wait_on_address(
243 addr_op,
244 value_op,
245 size_op,
246 flags_op,
247 MacOsFutexTimeout::Absolute { clock_op, timeout_op },
248 dest,
249 )?;
250 }
251 "os_sync_wait_on_address_with_timeout" => {
252 let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
253 this.check_shim(abi, CanonAbi::C, link_name, args)?;
254 this.os_sync_wait_on_address(
255 addr_op,
256 value_op,
257 size_op,
258 flags_op,
259 MacOsFutexTimeout::Relative { clock_op, timeout_op },
260 dest,
261 )?;
262 }
263 "os_sync_wake_by_address_any" => {
264 let [addr_op, size_op, flags_op] =
265 this.check_shim(abi, CanonAbi::C, link_name, args)?;
266 this.os_sync_wake_by_address(
267 addr_op, size_op, flags_op, false, dest,
268 )?;
269 }
270 "os_sync_wake_by_address_all" => {
271 let [addr_op, size_op, flags_op] =
272 this.check_shim(abi, CanonAbi::C, link_name, args)?;
273 this.os_sync_wake_by_address(
274 addr_op, size_op, flags_op, true, dest,
275 )?;
276 }
277 "os_unfair_lock_lock" => {
278 let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
279 this.os_unfair_lock_lock(lock_op)?;
280 }
281 "os_unfair_lock_trylock" => {
282 let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
283 this.os_unfair_lock_trylock(lock_op, dest)?;
284 }
285 "os_unfair_lock_unlock" => {
286 let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
287 this.os_unfair_lock_unlock(lock_op)?;
288 }
289 "os_unfair_lock_assert_owner" => {
290 let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
291 this.os_unfair_lock_assert_owner(lock_op)?;
292 }
293 "os_unfair_lock_assert_not_owner" => {
294 let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
295 this.os_unfair_lock_assert_not_owner(lock_op)?;
296 }
297
298 _ => return interp_ok(EmulateItemResult::NotSupported),
299 };
300
301 interp_ok(EmulateItemResult::NeedsReturn)
302 }
303
304 fn ioctl(
305 &mut self,
306 fd_num: &OpTy<'tcx>,
307 cmd: &OpTy<'tcx>,
308 _varargs: &[OpTy<'tcx>],
309 ) -> InterpResult<'tcx, Scalar> {
310 let this = self.eval_context_mut();
311
312 let fioclex = this.eval_libc_u64("FIOCLEX");
313
314 let fd_num = this.read_scalar(fd_num)?.to_i32()?;
315 let cmd = this.read_scalar(cmd)?.to_u64()?;
316
317 if cmd == fioclex {
318 if this.machine.fds.is_fd_num(fd_num) {
321 interp_ok(Scalar::from_i32(0))
322 } else {
323 this.set_last_error_and_return_i32(LibcError("EBADF"))
324 }
325 } else {
326 throw_unsup_format!("ioctl: unsupported command {cmd:#x}");
327 }
328 }
329}