1use rustc_middle::ty::Ty;
2use rustc_span::Symbol;
3use rustc_target::callconv::{Conv, 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.check_shim(abi, Conv::C, link_name, args)?;
38 let errno_place = this.last_error_place()?;
39 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
40 }
41
42 "close$NOCANCEL" => {
44 let [result] = this.check_shim(abi, Conv::C, link_name, args)?;
45 let result = this.close(result)?;
46 this.write_scalar(result, dest)?;
47 }
48 "stat" | "stat64" | "stat$INODE64" => {
49 let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
50 let result = this.macos_fbsd_solarish_stat(path, buf)?;
51 this.write_scalar(result, dest)?;
52 }
53 "lstat" | "lstat64" | "lstat$INODE64" => {
54 let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
55 let result = this.macos_fbsd_solarish_lstat(path, buf)?;
56 this.write_scalar(result, dest)?;
57 }
58 "fstat" | "fstat64" | "fstat$INODE64" => {
59 let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
60 let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
61 this.write_scalar(result, dest)?;
62 }
63 "opendir$INODE64" => {
64 let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
65 let result = this.opendir(name)?;
66 this.write_scalar(result, dest)?;
67 }
68 "readdir_r" | "readdir_r$INODE64" => {
69 let [dirp, entry, result] = this.check_shim(abi, Conv::C, link_name, args)?;
70 let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
71 this.write_scalar(result, dest)?;
72 }
73 "realpath$DARWIN_EXTSN" => {
74 let [path, resolved_path] = this.check_shim(abi, Conv::C, link_name, args)?;
75 let result = this.realpath(path, resolved_path)?;
76 this.write_scalar(result, dest)?;
77 }
78 "ioctl" => {
79 let ([fd_num, cmd], varargs) =
80 this.check_shim_variadic(abi, Conv::C, link_name, args)?;
81 let result = this.ioctl(fd_num, cmd, varargs)?;
82 this.write_scalar(result, dest)?;
83 }
84
85 "_NSGetEnviron" => {
87 let [] = this.check_shim(abi, Conv::C, link_name, args)?;
88 let environ = this.machine.env_vars.unix().environ();
89 this.write_pointer(environ, dest)?;
90 }
91
92 "CCRandomGenerateBytes" => {
94 let [bytes, count] = this.check_shim(abi, Conv::C, link_name, args)?;
95 let bytes = this.read_pointer(bytes)?;
96 let count = this.read_target_usize(count)?;
97 let success = this.eval_libc_i32("kCCSuccess");
98 this.gen_random(bytes, count)?;
99 this.write_int(success, dest)?;
100 }
101
102 "mach_absolute_time" => {
104 let [] = this.check_shim(abi, Conv::C, link_name, args)?;
105 let result = this.mach_absolute_time()?;
106 this.write_scalar(result, dest)?;
107 }
108
109 "mach_timebase_info" => {
110 let [info] = this.check_shim(abi, Conv::C, link_name, args)?;
111 let result = this.mach_timebase_info(info)?;
112 this.write_scalar(result, dest)?;
113 }
114
115 "_NSGetArgc" => {
117 let [] = this.check_shim(abi, Conv::C, link_name, args)?;
118 this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?;
119 }
120 "_NSGetArgv" => {
121 let [] = this.check_shim(abi, Conv::C, link_name, args)?;
122 this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
123 }
124 "_NSGetExecutablePath" => {
125 let [buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
126 this.check_no_isolation("`_NSGetExecutablePath`")?;
127
128 let buf_ptr = this.read_pointer(buf)?;
129 let bufsize = this.deref_pointer_as(bufsize, this.machine.layouts.u32)?;
130
131 let path = std::env::current_exe().unwrap();
134 let (written, size_needed) = this.write_path_to_c_str(
135 &path,
136 buf_ptr,
137 this.read_scalar(&bufsize)?.to_u32()?.into(),
138 )?;
139
140 if written {
141 this.write_null(dest)?;
142 } else {
143 this.write_scalar(Scalar::from_u32(size_needed.try_into().unwrap()), &bufsize)?;
144 this.write_int(-1, dest)?;
145 }
146 }
147
148 "_tlv_atexit" => {
150 let [dtor, data] = this.check_shim(abi, Conv::C, link_name, args)?;
151 let dtor = this.read_pointer(dtor)?;
152 let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
153 let data = this.read_scalar(data)?;
154 let active_thread = this.active_thread();
155 this.machine.tls.add_macos_thread_dtor(active_thread, dtor, data)?;
156 }
157
158 "pthread_get_stackaddr_np" => {
160 let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
161 this.read_target_usize(thread)?;
162 let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
163 this.write_scalar(stack_addr, dest)?;
164 }
165 "pthread_get_stacksize_np" => {
166 let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
167 this.read_target_usize(thread)?;
168 let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
169 this.write_scalar(stack_size, dest)?;
170 }
171
172 "pthread_setname_np" => {
174 let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
175
176 let thread = this.pthread_self()?;
186 let res = match this.pthread_setname_np(
187 thread,
188 this.read_scalar(name)?,
189 this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
190 false,
191 )? {
192 ThreadNameResult::Ok => Scalar::from_u32(0),
193 ThreadNameResult::NameTooLong => this.eval_libc("ENAMETOOLONG"),
194 ThreadNameResult::ThreadNotFound => unreachable!(),
195 };
196 this.write_scalar(res, dest)?;
199 }
200 "pthread_getname_np" => {
201 let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
202
203 let res = match this.pthread_getname_np(
212 this.read_scalar(thread)?,
213 this.read_scalar(name)?,
214 this.read_scalar(len)?,
215 true,
216 )? {
217 ThreadNameResult::Ok => Scalar::from_u32(0),
218 ThreadNameResult::NameTooLong => Scalar::from_u32(0),
220 ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
221 };
222 this.write_scalar(res, dest)?;
223 }
224
225 "os_sync_wait_on_address" => {
227 let [addr_op, value_op, size_op, flags_op] =
228 this.check_shim(abi, Conv::C, link_name, args)?;
229 this.os_sync_wait_on_address(
230 addr_op,
231 value_op,
232 size_op,
233 flags_op,
234 MacOsFutexTimeout::None,
235 dest,
236 )?;
237 }
238 "os_sync_wait_on_address_with_deadline" => {
239 let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
240 this.check_shim(abi, Conv::C, link_name, args)?;
241 this.os_sync_wait_on_address(
242 addr_op,
243 value_op,
244 size_op,
245 flags_op,
246 MacOsFutexTimeout::Absolute { clock_op, timeout_op },
247 dest,
248 )?;
249 }
250 "os_sync_wait_on_address_with_timeout" => {
251 let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
252 this.check_shim(abi, Conv::C, link_name, args)?;
253 this.os_sync_wait_on_address(
254 addr_op,
255 value_op,
256 size_op,
257 flags_op,
258 MacOsFutexTimeout::Relative { clock_op, timeout_op },
259 dest,
260 )?;
261 }
262 "os_sync_wake_by_address_any" => {
263 let [addr_op, size_op, flags_op] =
264 this.check_shim(abi, Conv::C, link_name, args)?;
265 this.os_sync_wake_by_address(
266 addr_op, size_op, flags_op, false, dest,
267 )?;
268 }
269 "os_sync_wake_by_address_all" => {
270 let [addr_op, size_op, flags_op] =
271 this.check_shim(abi, Conv::C, link_name, args)?;
272 this.os_sync_wake_by_address(
273 addr_op, size_op, flags_op, true, dest,
274 )?;
275 }
276
277 "os_unfair_lock_lock" => {
278 let [lock_op] = this.check_shim(abi, Conv::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, Conv::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, Conv::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, Conv::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, Conv::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}