1use rustc_middle::ty::Ty;
2use rustc_span::Symbol;
3use rustc_target::callconv::FnAbi;
4
5use super::sync::{EvalContextExt as _, MacOsFutexTimeout};
6use crate::shims::unix::*;
7use crate::*;
8
9pub fn is_dyn_sym(name: &str) -> bool {
10 match name {
11 "os_sync_wait_on_address"
13 | "os_sync_wait_on_address_with_deadline"
14 | "os_sync_wait_on_address_with_timeout"
15 | "os_sync_wake_by_address_any"
16 | "os_sync_wake_by_address_all" => true,
17 _ => false,
18 }
19}
20
21impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
22pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
23 fn emulate_foreign_item_inner(
24 &mut self,
25 link_name: Symbol,
26 abi: &FnAbi<'tcx, Ty<'tcx>>,
27 args: &[OpTy<'tcx>],
28 dest: &MPlaceTy<'tcx>,
29 ) -> InterpResult<'tcx, EmulateItemResult> {
30 let this = self.eval_context_mut();
31
32 match link_name.as_str() {
35 "__error" => {
37 let [] = &this
38 .check_shim_sig(shim_sig!(extern "C" fn() -> *_), (link_name, abi, 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 [fd] = &this
46 .check_shim_sig(shim_sig!(extern "C" fn(i32) -> i32), (link_name, abi, args))?;
47 let fd = this.read_scalar(fd)?.to_i32()?;
48 let result = this.close(fd)?;
49 this.write_scalar(result, dest)?;
50 }
51 "stat$INODE64" => {
52 let [path, buf] = &this.check_shim_sig(
53 shim_sig!(extern "C" fn(*_, *libc::stat) -> i32),
54 (link_name, abi, args),
55 )?;
56 let result = this.stat(path, buf)?;
57 this.write_scalar(result, dest)?;
58 }
59 "lstat$INODE64" => {
60 let [path, buf] = &this.check_shim_sig(
61 shim_sig!(extern "C" fn(*_, *libc::stat) -> i32),
62 (link_name, abi, args),
63 )?;
64 let result = this.lstat(path, buf)?;
65 this.write_scalar(result, dest)?;
66 }
67 "fstat$INODE64" => {
68 let [fd, buf] = &this.check_shim_sig(
69 shim_sig!(extern "C" fn(i32, *libc::stat) -> i32),
70 (link_name, abi, args),
71 )?;
72 let result = this.fstat(fd, buf)?;
73 this.write_scalar(result, dest)?;
74 }
75 "fstatat$INODE64" => {
76 let [dirfd, path, buf, flags] = &this.check_shim_sig(
77 shim_sig!(extern "C" fn(i32, *_, *libc::stat, i32) -> i32),
78 (link_name, abi, args),
79 )?;
80 let result = this.fstatat(dirfd, path, buf, flags)?;
81 this.write_scalar(result, dest)?;
82 }
83 "opendir$INODE64" => {
84 let [name] = &this
85 .check_shim_sig(shim_sig!(extern "C" fn(*_) -> *_), (link_name, abi, args))?;
86 let result = this.opendir(name)?;
87 this.write_scalar(result, dest)?;
88 }
89 "readdir$INODE64" => {
90 let [dirp] = &this
91 .check_shim_sig(shim_sig!(extern "C" fn(*_) -> *_), (link_name, abi, args))?;
92 this.readdir(dirp, dest, "dirent")?;
93 }
94 "realpath$DARWIN_EXTSN" => {
95 let [path, resolved_path] = &this.check_shim_sig(
96 shim_sig!(extern "C" fn(*_, *_) -> *_),
97 (link_name, abi, args),
98 )?;
99 let result = this.realpath(path, resolved_path)?;
100 this.write_scalar(result, dest)?;
101 }
102
103 "_NSGetEnviron" => {
105 let [] = &this
107 .check_shim_sig(shim_sig!(extern "C" fn() -> *_), (link_name, abi, args))?;
108 let environ = this.machine.env_vars.unix().environ();
109 this.write_pointer(environ, dest)?;
110 }
111
112 "CCRandomGenerateBytes" => {
114 let [bytes, count] = &this.check_shim_sig(
115 shim_sig!(extern "C" fn(*_, usize) -> i32),
116 (link_name, abi, args),
117 )?;
118 let bytes = this.read_pointer(bytes)?;
119 let count = this.read_target_usize(count)?;
120 let success = this.eval_libc_i32("kCCSuccess");
121 this.gen_random(bytes, count)?;
122 this.write_int(success, dest)?;
123 }
124
125 "mach_absolute_time" => {
127 let [] = &this
128 .check_shim_sig(shim_sig!(extern "C" fn() -> u64), (link_name, abi, args))?;
129 let result = this.mach_absolute_time()?;
130 this.write_scalar(result, dest)?;
131 }
132
133 "mach_timebase_info" => {
134 let [info] = &this.check_shim_sig(
135 shim_sig!(extern "C" fn(*libc::mach_timebase_info) -> i32),
136 (link_name, abi, args),
137 )?;
138 let result = this.mach_timebase_info(info)?;
139 this.write_scalar(result, dest)?;
140 }
141
142 "mach_wait_until" => {
143 let [deadline] = &this
145 .check_shim_sig(shim_sig!(extern "C" fn(u64) -> i32), (link_name, abi, args))?;
146 let result = this.mach_wait_until(deadline)?;
147 this.write_scalar(result, dest)?;
148 }
149
150 "_NSGetArgc" => {
152 let [] = &this
154 .check_shim_sig(shim_sig!(extern "C" fn() -> *_), (link_name, abi, args))?;
155 this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?;
156 }
157 "_NSGetArgv" => {
158 let [] = &this
160 .check_shim_sig(shim_sig!(extern "C" fn() -> *_), (link_name, abi, args))?;
161 this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
162 }
163 "_NSGetExecutablePath" => {
164 let [buf, bufsize] = &this.check_shim_sig(
166 shim_sig!(extern "C" fn(*_, *u32) -> i32),
167 (link_name, abi, args),
168 )?;
169 this.check_no_isolation("`_NSGetExecutablePath`")?;
170
171 let buf_ptr = this.read_pointer(buf)?;
172 let bufsize = this.deref_pointer(bufsize)?;
173
174 let Some(path) = this.machine.current_exe.clone() else {
175 throw_unsup_format!(
176 "_NSGetExecutablePath only works when Miri is run on a file"
177 )
178 };
179 let (written, size_needed) = this.write_path_to_c_str(
180 &path,
181 buf_ptr,
182 this.read_scalar(&bufsize)?.to_u32()?.into(),
183 )?;
184
185 if written {
186 this.write_int(0, dest)?;
187 } else {
188 this.write_scalar(Scalar::from_u32(size_needed.try_into().unwrap()), &bufsize)?;
189 this.write_int(-1, dest)?;
190 }
191 }
192
193 "_tlv_atexit" => {
195 let [dtor, data] = &this.check_shim_sig(
196 shim_sig!(extern "C" fn(fn(..) -> _, *_) -> ()),
197 (link_name, abi, args),
198 )?;
199 let dtor = this.read_pointer(dtor)?;
200 let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
201 let data = this.read_scalar(data)?;
202 this.machine.tls.add_thread_atexit(
203 this.active_thread(),
204 dtor,
205 data,
206 this.machine.current_user_relevant_span(),
207 )?;
208 }
209
210 "pthread_get_stackaddr_np" => {
212 let [thread] = &this.check_shim_sig(
214 shim_sig!(extern "C" fn(libc::pthread_t) -> *_),
215 (link_name, abi, args),
216 )?;
217 this.read_target_usize(thread)?;
218 let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
219 this.write_scalar(stack_addr, dest)?;
220 }
221 "pthread_get_stacksize_np" => {
222 let [thread] = &this.check_shim_sig(
224 shim_sig!(extern "C" fn(libc::pthread_t) -> usize),
225 (link_name, abi, args),
226 )?;
227 this.read_target_usize(thread)?;
228 let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
229 this.write_scalar(stack_size, dest)?;
230 }
231
232 "pthread_setname_np" => {
234 let [name] = &this
235 .check_shim_sig(shim_sig!(extern "C" fn(*_) -> i32), (link_name, abi, args))?;
236
237 let thread = this.pthread_self()?;
247 let res = match this.pthread_setname_np(
248 thread,
249 this.read_scalar(name)?,
250 this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?,
251 false,
252 )? {
253 ThreadNameResult::Ok => Scalar::from_i32(0),
254 ThreadNameResult::NameTooLong => {
255 this.set_errno_and_return_neg1_i32(LibcError("ENAMETOOLONG"))?
257 }
258 };
259 this.write_scalar(res, dest)?;
262 }
263 "pthread_getname_np" => {
264 let [thread, name, len] = &this.check_shim_sig(
265 shim_sig!(extern "C" fn(libc::pthread_t, *_, usize) -> i32),
266 (link_name, abi, args),
267 )?;
268
269 let res = match this.pthread_getname_np(
278 this.read_scalar(thread)?,
279 this.read_scalar(name)?,
280 this.read_scalar(len)?,
281 true,
282 )? {
283 ThreadNameResult::Ok => Scalar::from_i32(0),
284 ThreadNameResult::NameTooLong => Scalar::from_i32(0),
286 };
287 this.write_scalar(res, dest)?;
288 }
289 "pthread_threadid_np" => {
290 let [thread, tid_ptr] = &this.check_shim_sig(
291 shim_sig!(extern "C" fn(libc::pthread_t, *u64) -> i32),
292 (link_name, abi, args),
293 )?;
294 let res = this.apple_pthread_threadid_np(thread, tid_ptr)?;
295 this.write_scalar(res, dest)?;
296 }
297
298 "os_sync_wait_on_address" => {
300 let [addr_op, value_op, size_op, flags_op] = &this.check_shim_sig(
301 shim_sig!(extern "C" fn(*_, u64, usize, u32) -> i32),
302 (link_name, abi, args),
303 )?;
304 this.os_sync_wait_on_address(
305 addr_op,
306 value_op,
307 size_op,
308 flags_op,
309 MacOsFutexTimeout::None,
310 dest,
311 )?;
312 }
313 "os_sync_wait_on_address_with_deadline" => {
314 let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = &this
315 .check_shim_sig(
316 shim_sig!(extern "C" fn(*_, u64, usize, u32, u32, u64) -> i32),
317 (link_name, abi, args),
318 )?;
319 this.os_sync_wait_on_address(
320 addr_op,
321 value_op,
322 size_op,
323 flags_op,
324 MacOsFutexTimeout::Absolute { clock_op, timeout_op },
325 dest,
326 )?;
327 }
328 "os_sync_wait_on_address_with_timeout" => {
329 let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = &this
330 .check_shim_sig(
331 shim_sig!(extern "C" fn(*_, u64, usize, u32, u32, u64) -> i32),
332 (link_name, abi, args),
333 )?;
334 this.os_sync_wait_on_address(
335 addr_op,
336 value_op,
337 size_op,
338 flags_op,
339 MacOsFutexTimeout::Relative { clock_op, timeout_op },
340 dest,
341 )?;
342 }
343 "os_sync_wake_by_address_any" => {
344 let [addr_op, size_op, flags_op] = &this.check_shim_sig(
345 shim_sig!(extern "C" fn(*_, usize, u32) -> i32),
346 (link_name, abi, args),
347 )?;
348 this.os_sync_wake_by_address(
349 addr_op, size_op, flags_op, false, dest,
350 )?;
351 }
352 "os_sync_wake_by_address_all" => {
353 let [addr_op, size_op, flags_op] = &this.check_shim_sig(
354 shim_sig!(extern "C" fn(*_, usize, u32) -> i32),
355 (link_name, abi, args),
356 )?;
357 this.os_sync_wake_by_address(
358 addr_op, size_op, flags_op, true, dest,
359 )?;
360 }
361 "os_unfair_lock_lock" => {
362 let [lock_op] = &this.check_shim_sig(
363 shim_sig!(extern "C" fn(*libc::os_unfair_lock_s) -> ()),
364 (link_name, abi, args),
365 )?;
366 this.os_unfair_lock_lock(lock_op)?;
367 }
368 "os_unfair_lock_trylock" => {
369 let [lock_op] = &this.check_shim_sig(
370 shim_sig!(extern "C" fn(*libc::os_unfair_lock_s) -> bool),
371 (link_name, abi, args),
372 )?;
373 this.os_unfair_lock_trylock(lock_op, dest)?;
374 }
375 "os_unfair_lock_unlock" => {
376 let [lock_op] = &this.check_shim_sig(
377 shim_sig!(extern "C" fn(*libc::os_unfair_lock_s) -> ()),
378 (link_name, abi, args),
379 )?;
380 this.os_unfair_lock_unlock(lock_op)?;
381 }
382 "os_unfair_lock_assert_owner" => {
383 let [lock_op] = &this.check_shim_sig(
384 shim_sig!(extern "C" fn(*libc::os_unfair_lock_s) -> ()),
385 (link_name, abi, args),
386 )?;
387 this.os_unfair_lock_assert_owner(lock_op)?;
388 }
389 "os_unfair_lock_assert_not_owner" => {
390 let [lock_op] = &this.check_shim_sig(
391 shim_sig!(extern "C" fn(*libc::os_unfair_lock_s) -> ()),
392 (link_name, abi, args),
393 )?;
394 this.os_unfair_lock_assert_not_owner(lock_op)?;
395 }
396 "dispatch_semaphore_create" => {
397 let [value] = &this.check_shim_sig(
398 shim_sig!(extern "C" fn(isize) -> *_),
399 (link_name, abi, args),
400 )?;
401 this.dispatch_semaphore_create(value, dest)?;
402 }
403 "dispatch_semaphore_wait" => {
404 let [sem, timeout] = &this.check_shim_sig(
405 shim_sig!(extern "C" fn(*_, u64) -> isize),
406 (link_name, abi, args),
407 )?;
408 this.dispatch_semaphore_wait(sem, timeout, dest)?;
409 }
410 "dispatch_semaphore_signal" => {
411 let [sem] = &this.check_shim_sig(
412 shim_sig!(extern "C" fn(*_) -> isize),
413 (link_name, abi, args),
414 )?;
415 this.dispatch_semaphore_signal(sem, dest)?;
416 }
417 "dispatch_release" => {
418 let [sem] = &this
419 .check_shim_sig(shim_sig!(extern "C" fn(*_) -> ()), (link_name, abi, args))?;
420 this.dispatch_release(sem)?;
421 }
422 "dispatch_time" => {
423 let [when, delta] = &this.check_shim_sig(
424 shim_sig!(extern "C" fn(u64, i64) -> u64),
425 (link_name, abi, args),
426 )?;
427 this.dispatch_time(when, delta, dest)?;
428 }
429
430 "pthread_cond_timedwait_relative_np" => {
431 let [cond, mutex, reltime] = &this.check_shim_sig(
432 shim_sig!(extern "C" fn(*libc::pthread_cond_t, *libc::pthread_mutex_t, *libc::timespec) -> i32),
433 (link_name, abi, args),
434 )?;
435 this.pthread_cond_timedwait(
436 cond, mutex, reltime, dest, true,
437 )?;
438 }
439
440 "confstr" => {
443 let [_key, _buf, _buflen] = &this.check_shim_sig(
445 shim_sig!(extern "C" fn(i32, *_, usize) -> usize),
446 (link_name, abi, args),
447 )?;
448 this.set_last_error(LibcError("EINVAL"))?;
450 this.write_int(0, dest)?;
451 }
452
453 _ => return interp_ok(EmulateItemResult::NotSupported),
454 };
455
456 interp_ok(EmulateItemResult::NeedsReturn)
457 }
458}