1use rustc_middle::ty::Ty;
2use rustc_span::Symbol;
3use rustc_target::callconv::FnAbi;
4use rustc_target::spec::{Arch, Target};
5
6use self::shims::unix::linux::mem::EvalContextExt as _;
7use self::shims::unix::linux_like::eventfd::EvalContextExt as _;
8use self::shims::unix::linux_like::syscall::syscall;
9use crate::machine::{SIGRTMAX, SIGRTMIN};
10use crate::shims::unix::foreign_items::EvalContextExt as _;
11use crate::shims::unix::linux_like::epoll::EvalContextExt as _;
12use crate::shims::unix::linux_like::thread::prctl;
13use crate::shims::unix::*;
14use crate::*;
15
16const TASK_COMM_LEN: u64 = 16;
20
21pub fn is_dyn_sym(name: &str, target: &Target) -> bool {
22 match name {
23 "copy_file_range" | "gettid" | "statx" => true,
24 "__cxa_thread_atexit_impl" => target.env == rustc_target::spec::Env::Gnu,
25 "__futimens64" =>
27 target.env == rustc_target::spec::Env::Gnu
28 && matches!(target.arch, Arch::X86 | Arch::Arm | Arch::Mips),
29 _ => false,
30 }
31}
32
33impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
34pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
35 fn emulate_foreign_item_inner(
36 &mut self,
37 link_name: Symbol,
38 abi: &FnAbi<'tcx, Ty<'tcx>>,
39 args: &[OpTy<'tcx>],
40 dest: &MPlaceTy<'tcx>,
41 ) -> InterpResult<'tcx, EmulateItemResult> {
42 let ecx = self.eval_context_mut();
43
44 match link_name.as_str() {
47 "open64" => {
49 let ([path_raw, flag], varargs) = ecx.check_shim_sig_variadic(
51 shim_sig!(extern "C" fn(*_, i32, ...) -> i32),
52 (link_name, abi, args),
53 )?;
54 let result = ecx.open(None, &path_raw, &flag, varargs)?;
55 ecx.write_scalar(result, dest)?;
56 }
57 "openat64" => {
58 let ([dirfd, path_raw, flag], varargs) = ecx.check_shim_sig_variadic(
60 shim_sig!(extern "C" fn(i32, *_, i32, ...) -> i32),
61 (link_name, abi, args),
62 )?;
63 let result = ecx.open(Some(&dirfd), &path_raw, &flag, varargs)?;
64 ecx.write_scalar(result, dest)?;
65 }
66 "pread64" => {
67 let [fd, buf, count, offset] = &ecx.check_shim_sig(
68 shim_sig!(extern "C" fn(i32, *_, usize, libc::off64_t) -> isize),
69 (link_name, abi, args),
70 )?;
71 let fd = ecx.read_scalar(fd)?.to_i32()?;
72 let buf = ecx.read_pointer(buf)?;
73 let count = ecx.read_target_usize(count)?;
74 let offset = ecx.read_scalar(offset)?.to_int(offset.layout.size)?;
75 ecx.read(fd, buf, count, Some(offset), dest)?;
76 }
77 "pwrite64" => {
78 let [fd, buf, n, offset] = &ecx.check_shim_sig(
79 shim_sig!(extern "C" fn(i32, *_, usize, libc::off64_t) -> isize),
80 (link_name, abi, args),
81 )?;
82 let fd = ecx.read_scalar(fd)?.to_i32()?;
83 let buf = ecx.read_pointer(buf)?;
84 let count = ecx.read_target_usize(n)?;
85 let offset = ecx.read_scalar(offset)?.to_int(offset.layout.size)?;
86 trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
87 ecx.write(fd, buf, count, Some(offset), dest)?;
88 }
89 "lseek64" => {
90 let [fd, offset, whence] = &ecx.check_shim_sig(
91 shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t),
92 (link_name, abi, args),
93 )?;
94 let fd = ecx.read_scalar(fd)?.to_i32()?;
95 let offset = ecx.read_scalar(offset)?.to_int(offset.layout.size)?;
96 let whence = ecx.read_scalar(whence)?.to_i32()?;
97 ecx.lseek(fd, offset, whence, dest)?;
98 }
99 "posix_fallocate64" => {
100 let [fd, offset, len] = &ecx.check_shim_sig(
101 shim_sig!(extern "C" fn(i32, libc::off64_t, libc::off64_t) -> i32),
102 (link_name, abi, args),
103 )?;
104
105 let fd = ecx.read_scalar(fd)?.to_i32()?;
106 let offset = ecx.read_scalar(offset)?.to_i64()?;
107 let len = ecx.read_scalar(len)?.to_i64()?;
108
109 let result = ecx.posix_fallocate(fd, offset, len)?;
110 ecx.write_scalar(result, dest)?;
111 }
112 "fallocate" => {
113 let [fd, mode, offset, len] = &ecx.check_shim_sig(
114 shim_sig!(extern "C" fn(i32, i32, libc::off_t, libc::off_t) -> i32),
115 (link_name, abi, args),
116 )?;
117
118 let fd = ecx.read_scalar(fd)?.to_i32()?;
119 let mode = ecx.read_scalar(mode)?.to_i32()?;
120 let offset =
122 i64::try_from(ecx.read_scalar(offset)?.to_int(offset.layout.size)?).unwrap();
123 let len = i64::try_from(ecx.read_scalar(len)?.to_int(len.layout.size)?).unwrap();
124
125 let result = ecx.linux_fallocate(fd, mode, offset, len)?;
126 ecx.write_scalar(result, dest)?;
127 }
128 "fallocate64" => {
129 let [fd, mode, offset, len] = &ecx.check_shim_sig(
130 shim_sig!(extern "C" fn(i32, i32, libc::off64_t, libc::off64_t) -> i32),
131 (link_name, abi, args),
132 )?;
133
134 let fd = ecx.read_scalar(fd)?.to_i32()?;
135 let mode = ecx.read_scalar(mode)?.to_i32()?;
136 let offset = ecx.read_scalar(offset)?.to_i64()?;
137 let len = ecx.read_scalar(len)?.to_i64()?;
138
139 let result = ecx.linux_fallocate(fd, mode, offset, len)?;
140 ecx.write_scalar(result, dest)?;
141 }
142 "ftruncate64" => {
143 let [fd, length] = &ecx.check_shim_sig(
144 shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32),
145 (link_name, abi, args),
146 )?;
147 let fd = ecx.read_scalar(fd)?.to_i32()?;
148 let length = ecx.read_scalar(length)?.to_int(length.layout.size)?;
149 let result = ecx.ftruncate64(fd, length)?;
150 ecx.write_scalar(result, dest)?;
151 }
152 "sync_file_range" => {
153 let [fd, offset, nbytes, flags] = &ecx.check_shim_sig(
154 shim_sig!(extern "C" fn(i32, libc::off64_t, libc::off64_t, u32) -> i32),
155 (link_name, abi, args),
156 )?;
157 let result = ecx.linux_sync_file_range(fd, offset, nbytes, flags)?;
158 ecx.write_scalar(result, dest)?;
159 }
160 "copy_file_range" => {
161 let [fd_in, off_in, fd_out, off_out, len, flags] = &ecx.check_shim_sig(
162 shim_sig!(extern "C" fn(i32, *libc::off_t, i32, *libc::off_t, libc::size_t, u32) -> libc::ssize_t),
163 (link_name, abi, args),
164 )?;
165 ecx.linux_copy_file_range(fd_in, off_in, fd_out, off_out, len, flags, dest)?;
166 }
167 "statx" => {
168 let [dirfd, pathname, flags, mask, statxbuf] = &ecx.check_shim_sig(
169 shim_sig!(extern "C" fn(i32, *_, i32, u32, *libc::statx) -> i32),
170 (link_name, abi, args),
171 )?;
172 let result = ecx.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
173 ecx.write_scalar(result, dest)?;
174 }
175 "fstat64" => {
176 let [fd, buf] = &ecx.check_shim_sig(
177 shim_sig!(extern "C" fn(i32, *libc::stat64) -> i32),
178 (link_name, abi, args),
179 )?;
180 let result = ecx.fstat(fd, buf)?;
181 ecx.write_scalar(result, dest)?;
182 }
183 "readdir64" => {
184 let [dirp] = &ecx
185 .check_shim_sig(shim_sig!(extern "C" fn(*_) -> *_), (link_name, abi, args))?;
186 ecx.readdir(dirp, dest, "dirent64")?;
187 }
188 "__futimens64" if ecx.is_dyn_sym("__futimens64") => {
189 let [fd, times] = &ecx.check_shim_sig(
192 shim_sig!(extern "C" fn(i32, *std::sys::pal::time::__timespec64) -> i32),
193 (link_name, abi, args),
194 )?;
195 let result = ecx.futimens(fd, times)?;
196 ecx.write_scalar(result, dest)?;
197 }
198
199 "epoll_create1" => {
201 let [flag] = &ecx
202 .check_shim_sig(shim_sig!(extern "C" fn(i32) -> i32), (link_name, abi, args))?;
203 let result = ecx.epoll_create1(flag)?;
204 ecx.write_scalar(result, dest)?;
205 }
206 "epoll_ctl" => {
207 let [epfd, op, fd, event] = &ecx.check_shim_sig(
208 shim_sig!(extern "C" fn(i32, i32, i32, *libc::epoll_event) -> i32),
209 (link_name, abi, args),
210 )?;
211 let result = ecx.epoll_ctl(epfd, op, fd, event)?;
212 ecx.write_scalar(result, dest)?;
213 }
214 "epoll_wait" => {
215 let [epfd, events, maxevents, timeout] = &ecx.check_shim_sig(
216 shim_sig!(extern "C" fn(i32, *libc::epoll_event, i32, i32) -> i32),
217 (link_name, abi, args),
218 )?;
219 ecx.epoll_wait(epfd, events, maxevents, timeout, dest)?;
220 }
221 "eventfd" => {
222 let [val, flag] = &ecx.check_shim_sig(
223 shim_sig!(extern "C" fn(u32, i32) -> i32),
224 (link_name, abi, args),
225 )?;
226 let result = ecx.eventfd(val, flag)?;
227 ecx.write_scalar(result, dest)?;
228 }
229
230 "pthread_setname_np" => {
232 let [thread, name] = &ecx.check_shim_sig(
233 shim_sig!(extern "C" fn(libc::pthread_t, *_) -> i32),
234 (link_name, abi, args),
235 )?;
236 let res = match ecx.pthread_setname_np(
237 ecx.read_scalar(thread)?,
238 ecx.read_scalar(name)?,
239 TASK_COMM_LEN,
240 false,
241 )? {
242 ThreadNameResult::Ok => Scalar::from_u32(0),
243 ThreadNameResult::NameTooLong => ecx.eval_libc("ERANGE"),
244 };
245 ecx.write_scalar(res, dest)?;
246 }
247 "pthread_getname_np" => {
248 let [thread, name, len] = &ecx.check_shim_sig(
249 shim_sig!(extern "C" fn(libc::pthread_t, *_, usize) -> i32),
250 (link_name, abi, args),
251 )?;
252 let len = ecx.read_scalar(len)?;
256 let res = if len.to_target_usize(ecx)? >= TASK_COMM_LEN {
257 match ecx.pthread_getname_np(
258 ecx.read_scalar(thread)?,
259 ecx.read_scalar(name)?,
260 len,
261 false,
262 )? {
263 ThreadNameResult::Ok => Scalar::from_u32(0),
264 ThreadNameResult::NameTooLong => unreachable!(),
265 }
266 } else {
267 ecx.eval_libc("ERANGE")
268 };
269 ecx.write_scalar(res, dest)?;
270 }
271 "gettid" => {
272 let [] = &ecx.check_shim_sig(
273 shim_sig!(extern "C" fn() -> libc::pid_t),
274 (link_name, abi, args),
275 )?;
276 let result = ecx.unix_gettid(link_name.as_str())?;
277 ecx.write_scalar(result, dest)?;
278 }
279 "prctl" => prctl(ecx, link_name, abi, args, dest)?,
280 "__cxa_thread_atexit_impl"
281 if ecx.tcx.sess.target.env == rustc_target::spec::Env::Gnu =>
282 {
283 let [dtor, arg, dso] = &ecx.check_shim_sig(
284 shim_sig!(extern "C" fn(*_, *_, *_) -> i32),
285 (link_name, abi, args),
286 )?;
287 let dtor = ecx.read_pointer(dtor)?;
288 let arg = ecx.read_scalar(arg)?;
289 let dso = ecx.read_pointer(dso)?;
290
291 let dso_handle = Symbol::intern("__dso_handle");
292 if dso.addr() != Pointer::from(ecx.machine.extern_statics[&dso_handle]).addr() {
293 throw_unsup_format!(
294 "__cxa_thread_atexit_impl: a DSO value other than the address of `__dso_handle` is not supported"
295 );
296 }
297
298 let dtor = ecx.get_ptr_fn(dtor)?.as_instance()?;
299 ecx.machine.tls.add_thread_atexit(
300 ecx.active_thread(),
301 dtor,
302 arg,
303 ecx.machine.current_user_relevant_span(),
304 )?;
305
306 ecx.write_int(0, dest)?;
307 }
308
309 "syscall" => {
311 syscall(ecx, link_name, abi, args, dest)?;
312 }
313
314 "mmap64" => {
316 let [addr, length, prot, flags, fd, offset] = &ecx.check_shim_sig(
317 shim_sig!(extern "C" fn(*_, usize, i32, i32, i32, libc::off64_t) -> *_),
318 (link_name, abi, args),
319 )?;
320 let offset = ecx.read_scalar(offset)?.to_i64()?;
321 let ptr = ecx.mmap(addr, length, prot, flags, fd, offset.into())?;
322 ecx.write_scalar(ptr, dest)?;
323 }
324 "mremap" => {
325 let ([old_address, old_size, new_size, flags], _) = ecx.check_shim_sig_variadic(
326 shim_sig!(extern "C" fn(*_, usize, usize, i32, ...) -> *_),
327 (link_name, abi, args),
328 )?;
329 let ptr = ecx.mremap(&old_address, &old_size, &new_size, &flags)?;
330 ecx.write_scalar(ptr, dest)?;
331 }
332 "__xpg_strerror_r" => {
333 let [errnum, buf, buflen] = &ecx.check_shim_sig(
334 shim_sig!(extern "C" fn(i32, *_, usize) -> i32),
335 (link_name, abi, args),
336 )?;
337 let result = ecx.strerror_r(errnum, buf, buflen)?;
338 ecx.write_scalar(result, dest)?;
339 }
340 "__errno_location" => {
341 let [] =
342 &ecx.check_shim_sig(shim_sig!(extern "C" fn() -> *_), (link_name, abi, args))?;
343 let errno_place = ecx.last_error_place()?;
344 ecx.write_scalar(errno_place.to_ref(ecx).to_scalar(), dest)?;
345 }
346 "__libc_current_sigrtmin" => {
347 let [] =
348 &ecx.check_shim_sig(shim_sig!(extern "C" fn() -> i32), (link_name, abi, args))?;
349
350 ecx.write_int(SIGRTMIN, dest)?;
351 }
352 "__libc_current_sigrtmax" => {
353 let [] =
354 &ecx.check_shim_sig(shim_sig!(extern "C" fn() -> i32), (link_name, abi, args))?;
355
356 ecx.write_int(SIGRTMAX, dest)?;
357 }
358
359 "pthread_getattr_np" if ecx.frame_in_std() => {
362 let [_thread, _attr] = &ecx.check_shim_sig(
363 shim_sig!(extern "C" fn(libc::pthread_t, *_) -> i32),
364 (link_name, abi, args),
365 )?;
366 ecx.write_int(0, dest)?;
367 }
368 "gnu_get_libc_version"
369 if ecx.frame_in_std()
370 && ecx.tcx.sess.target.env == rustc_target::spec::Env::Gnu =>
371 {
372 let [] =
373 &ecx.check_shim_sig(shim_sig!(extern "C" fn() -> *_), (link_name, abi, args))?;
374 let version = "2.26\0";
377 let version = ecx.allocate_str_dedup(version)?;
378 ecx.write_pointer(version.ptr(), dest)?;
379 }
380 "sendfile64" if ecx.frame_in_std() => {
381 let [_out_fd, _in_fd, _offset, _count] = &ecx.check_shim_sig(
382 shim_sig!(extern "C" fn(i32, i32, *libc::off64_t, usize) -> isize),
383 (link_name, abi, args),
384 )?;
385 ecx.set_errno_and_return_neg1(LibcError("ENOSYS"), dest)?;
387 }
388 "splice" if ecx.frame_in_std() => {
389 let [_src_fd, _src_offset, _dst_fd, _dst_offset, _len, _flags] = &ecx.check_shim_sig(
390 shim_sig!(extern "C" fn(i32, *libc::off_t, i32, *libc::off_t, usize, u32) -> isize),
391 (link_name, abi, args),
392 )?;
393 ecx.set_errno_and_return_neg1(LibcError("ENOSYS"), dest)?;
395 }
396
397 _ => return interp_ok(EmulateItemResult::NotSupported),
398 };
399
400 interp_ok(EmulateItemResult::NeedsReturn)
401 }
402}