4 use rustc_span::Symbol;
5 use rustc_target::abi::{Align, LayoutOf, Size};
6 use rustc_target::spec::abi::Abi;
9 use shims::foreign_items::EmulateByNameResult;
10 use shims::posix::fs::EvalContextExt as _;
11 use shims::posix::sync::EvalContextExt as _;
12 use shims::posix::thread::EvalContextExt as _;
14 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
15 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
16 fn emulate_foreign_item_by_name(
20 args: &[OpTy<'tcx, Tag>],
21 dest: &PlaceTy<'tcx, Tag>,
23 ) -> InterpResult<'tcx, EmulateByNameResult> {
24 let this = self.eval_context_mut();
26 match &*link_name.as_str() {
27 // Environment related shims
29 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
30 let result = this.getenv(name)?;
31 this.write_scalar(result, dest)?;
34 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
35 let result = this.unsetenv(name)?;
36 this.write_scalar(Scalar::from_i32(result), dest)?;
39 let &[ref name, ref value, ref overwrite] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
40 this.read_scalar(overwrite)?.to_i32()?;
41 let result = this.setenv(name, value)?;
42 this.write_scalar(Scalar::from_i32(result), dest)?;
45 let &[ref buf, ref size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
46 let result = this.getcwd(buf, size)?;
47 this.write_scalar(result, dest)?;
50 let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
51 let result = this.chdir(path)?;
52 this.write_scalar(Scalar::from_i32(result), dest)?;
56 "open" | "open64" => {
57 let &[ref path, ref flag, ref mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
58 let result = this.open(path, flag, mode)?;
59 this.write_scalar(Scalar::from_i32(result), dest)?;
62 // `fcntl` is variadic. The argument count is checked based on the first argument
63 // in `this.fcntl()`, so we do not use `check_shim` here.
64 this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
65 let result = this.fcntl(args)?;
66 this.write_scalar(Scalar::from_i32(result), dest)?;
69 let &[ref fd, ref buf, ref count] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
70 let fd = this.read_scalar(fd)?.to_i32()?;
71 let buf = this.read_scalar(buf)?.check_init()?;
72 let count = this.read_scalar(count)?.to_machine_usize(this)?;
73 let result = this.read(fd, buf, count)?;
74 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
77 let &[ref fd, ref buf, ref n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
78 let fd = this.read_scalar(fd)?.to_i32()?;
79 let buf = this.read_scalar(buf)?.check_init()?;
80 let count = this.read_scalar(n)?.to_machine_usize(this)?;
81 trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
82 let result = this.write(fd, buf, count)?;
83 // Now, `result` is the value we return back to the program.
84 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
87 let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
88 let result = this.unlink(path)?;
89 this.write_scalar(Scalar::from_i32(result), dest)?;
92 let &[ref target, ref linkpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
93 let result = this.symlink(target, linkpath)?;
94 this.write_scalar(Scalar::from_i32(result), dest)?;
97 let &[ref oldpath, ref newpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
98 let result = this.rename(oldpath, newpath)?;
99 this.write_scalar(Scalar::from_i32(result), dest)?;
102 let &[ref path, ref mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
103 let result = this.mkdir(path, mode)?;
104 this.write_scalar(Scalar::from_i32(result), dest)?;
107 let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
108 let result = this.rmdir(path)?;
109 this.write_scalar(Scalar::from_i32(result), dest)?;
112 let &[ref dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
113 let result = this.closedir(dirp)?;
114 this.write_scalar(Scalar::from_i32(result), dest)?;
116 "lseek" | "lseek64" => {
117 let &[ref fd, ref offset, ref whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
118 let result = this.lseek64(fd, offset, whence)?;
119 // "lseek" is only used on macOS which is 64bit-only, so `i64` always works.
120 this.write_scalar(Scalar::from_i64(result), dest)?;
123 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
124 let result = this.fsync(fd)?;
125 this.write_scalar(Scalar::from_i32(result), dest)?;
128 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
129 let result = this.fdatasync(fd)?;
130 this.write_scalar(Scalar::from_i32(result), dest)?;
133 let &[ref pathname, ref buf, ref bufsize] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
134 let result = this.readlink(pathname, buf, bufsize)?;
135 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
139 "posix_memalign" => {
140 let &[ref ret, ref align, ref size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
141 let ret = this.deref_operand(ret)?;
142 let align = this.read_scalar(align)?.to_machine_usize(this)?;
143 let size = this.read_scalar(size)?.to_machine_usize(this)?;
144 // Align must be power of 2, and also at least ptr-sized (POSIX rules).
145 if !align.is_power_of_two() {
146 throw_ub_format!("posix_memalign: alignment must be a power of two, but is {}", align);
148 if align < this.pointer_size().bytes() {
150 "posix_memalign: alignment must be at least the size of a pointer, but is {}",
156 this.write_null(&ret.into())?;
158 let ptr = this.memory.allocate(
159 Size::from_bytes(size),
160 Align::from_bytes(align).unwrap(),
161 MiriMemoryKind::C.into(),
163 this.write_scalar(ptr, &ret.into())?;
165 this.write_null(dest)?;
168 // Dynamic symbol loading
170 let &[ref handle, ref symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
171 this.read_scalar(handle)?.to_machine_usize(this)?;
172 let symbol = this.read_scalar(symbol)?.check_init()?;
173 let symbol_name = this.read_c_str(symbol)?;
174 if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.os)? {
175 let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym));
176 this.write_scalar(Scalar::from(ptr), dest)?;
178 this.write_null(dest)?;
182 // Querying system information
184 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
185 let name = this.read_scalar(name)?.to_i32()?;
188 ("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, this.pointer_size())),
189 ("_SC_NPROCESSORS_CONF", Scalar::from_int(NUM_CPUS, this.pointer_size())),
190 ("_SC_NPROCESSORS_ONLN", Scalar::from_int(NUM_CPUS, this.pointer_size())),
192 let mut result = None;
193 for &(sysconf_name, value) in sysconfs {
194 let sysconf_name = this.eval_libc_i32(sysconf_name)?;
195 if sysconf_name == name {
196 result = Some(value);
200 if let Some(result) = result {
201 this.write_scalar(result, dest)?;
203 throw_unsup_format!("unimplemented sysconf name: {}", name)
207 // Thread-local storage
208 "pthread_key_create" => {
209 let &[ref key, ref dtor] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
210 let key_place = this.deref_operand(key)?;
211 let dtor = this.read_scalar(dtor)?.check_init()?;
213 // Extract the function type out of the signature (that seems easier than constructing it ourselves).
214 let dtor = match this.test_null(dtor)? {
215 Some(dtor_ptr) => Some(this.memory.get_fn(dtor_ptr)?.as_instance()?),
219 // Figure out how large a pthread TLS key actually is.
220 // To this end, deref the argument type. This is `libc::pthread_key_t`.
221 let key_type = key.layout.ty
223 .ok_or_else(|| err_ub_format!(
224 "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
227 let key_layout = this.layout_of(key_type)?;
229 // Create key and write it into the memory where `key_ptr` wants it.
230 let key = this.machine.tls.create_tls_key(dtor, key_layout.size)?;
231 this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place.into())?;
233 // Return success (`0`).
234 this.write_null(dest)?;
236 "pthread_key_delete" => {
237 let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
238 let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?;
239 this.machine.tls.delete_tls_key(key)?;
240 // Return success (0)
241 this.write_null(dest)?;
243 "pthread_getspecific" => {
244 let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
245 let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?;
246 let active_thread = this.get_active_thread();
247 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
248 this.write_scalar(ptr, dest)?;
250 "pthread_setspecific" => {
251 let &[ref key, ref new_ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
252 let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?;
253 let active_thread = this.get_active_thread();
254 let new_ptr = this.read_scalar(new_ptr)?.check_init()?;
255 this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
257 // Return success (`0`).
258 this.write_null(dest)?;
261 // Synchronization primitives
262 "pthread_mutexattr_init" => {
263 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
264 let result = this.pthread_mutexattr_init(attr)?;
265 this.write_scalar(Scalar::from_i32(result), dest)?;
267 "pthread_mutexattr_settype" => {
268 let &[ref attr, ref kind] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
269 let result = this.pthread_mutexattr_settype(attr, kind)?;
270 this.write_scalar(Scalar::from_i32(result), dest)?;
272 "pthread_mutexattr_destroy" => {
273 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
274 let result = this.pthread_mutexattr_destroy(attr)?;
275 this.write_scalar(Scalar::from_i32(result), dest)?;
277 "pthread_mutex_init" => {
278 let &[ref mutex, ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
279 let result = this.pthread_mutex_init(mutex, attr)?;
280 this.write_scalar(Scalar::from_i32(result), dest)?;
282 "pthread_mutex_lock" => {
283 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
284 let result = this.pthread_mutex_lock(mutex)?;
285 this.write_scalar(Scalar::from_i32(result), dest)?;
287 "pthread_mutex_trylock" => {
288 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
289 let result = this.pthread_mutex_trylock(mutex)?;
290 this.write_scalar(Scalar::from_i32(result), dest)?;
292 "pthread_mutex_unlock" => {
293 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
294 let result = this.pthread_mutex_unlock(mutex)?;
295 this.write_scalar(Scalar::from_i32(result), dest)?;
297 "pthread_mutex_destroy" => {
298 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
299 let result = this.pthread_mutex_destroy(mutex)?;
300 this.write_scalar(Scalar::from_i32(result), dest)?;
302 "pthread_rwlock_rdlock" => {
303 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
304 let result = this.pthread_rwlock_rdlock(rwlock)?;
305 this.write_scalar(Scalar::from_i32(result), dest)?;
307 "pthread_rwlock_tryrdlock" => {
308 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
309 let result = this.pthread_rwlock_tryrdlock(rwlock)?;
310 this.write_scalar(Scalar::from_i32(result), dest)?;
312 "pthread_rwlock_wrlock" => {
313 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
314 let result = this.pthread_rwlock_wrlock(rwlock)?;
315 this.write_scalar(Scalar::from_i32(result), dest)?;
317 "pthread_rwlock_trywrlock" => {
318 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
319 let result = this.pthread_rwlock_trywrlock(rwlock)?;
320 this.write_scalar(Scalar::from_i32(result), dest)?;
322 "pthread_rwlock_unlock" => {
323 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
324 let result = this.pthread_rwlock_unlock(rwlock)?;
325 this.write_scalar(Scalar::from_i32(result), dest)?;
327 "pthread_rwlock_destroy" => {
328 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
329 let result = this.pthread_rwlock_destroy(rwlock)?;
330 this.write_scalar(Scalar::from_i32(result), dest)?;
332 "pthread_condattr_init" => {
333 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
334 let result = this.pthread_condattr_init(attr)?;
335 this.write_scalar(Scalar::from_i32(result), dest)?;
337 "pthread_condattr_destroy" => {
338 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
339 let result = this.pthread_condattr_destroy(attr)?;
340 this.write_scalar(Scalar::from_i32(result), dest)?;
342 "pthread_cond_init" => {
343 let &[ref cond, ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
344 let result = this.pthread_cond_init(cond, attr)?;
345 this.write_scalar(Scalar::from_i32(result), dest)?;
347 "pthread_cond_signal" => {
348 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
349 let result = this.pthread_cond_signal(cond)?;
350 this.write_scalar(Scalar::from_i32(result), dest)?;
352 "pthread_cond_broadcast" => {
353 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
354 let result = this.pthread_cond_broadcast(cond)?;
355 this.write_scalar(Scalar::from_i32(result), dest)?;
357 "pthread_cond_wait" => {
358 let &[ref cond, ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
359 let result = this.pthread_cond_wait(cond, mutex)?;
360 this.write_scalar(Scalar::from_i32(result), dest)?;
362 "pthread_cond_timedwait" => {
363 let &[ref cond, ref mutex, ref abstime] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
364 this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
366 "pthread_cond_destroy" => {
367 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
368 let result = this.pthread_cond_destroy(cond)?;
369 this.write_scalar(Scalar::from_i32(result), dest)?;
373 "pthread_create" => {
374 let &[ref thread, ref attr, ref start, ref arg] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
375 let result = this.pthread_create(thread, attr, start, arg)?;
376 this.write_scalar(Scalar::from_i32(result), dest)?;
379 let &[ref thread, ref retval] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
380 let result = this.pthread_join(thread, retval)?;
381 this.write_scalar(Scalar::from_i32(result), dest)?;
383 "pthread_detach" => {
384 let &[ref thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
385 let result = this.pthread_detach(thread)?;
386 this.write_scalar(Scalar::from_i32(result), dest)?;
389 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
390 this.pthread_self(dest)?;
393 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
394 let result = this.sched_yield()?;
395 this.write_scalar(Scalar::from_i32(result), dest)?;
398 let &[ref req, ref rem] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
399 let result = this.nanosleep(req, rem)?;
400 this.write_scalar(Scalar::from_i32(result), dest)?;
405 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
406 this.read_scalar(fd)?.to_i32()?;
407 // "returns 1 if fd is an open file descriptor referring to a terminal; otherwise 0 is returned, and errno is set to indicate the error"
408 // FIXME: we just say nothing is a terminal.
409 let enotty = this.eval_libc("ENOTTY")?;
410 this.set_last_error(enotty)?;
411 this.write_null(dest)?;
413 "pthread_atfork" => {
414 let &[ref prepare, ref parent, ref child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
415 this.force_bits(this.read_scalar(prepare)?.check_init()?, this.memory.pointer_size())?;
416 this.force_bits(this.read_scalar(parent)?.check_init()?, this.memory.pointer_size())?;
417 this.force_bits(this.read_scalar(child)?.check_init()?, this.memory.pointer_size())?;
418 // We do not support forking, so there is nothing to do here.
419 this.write_null(dest)?;
422 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
423 // These shims are enabled only when the caller is in the standard library.
424 "pthread_attr_getguardsize"
425 if this.frame_in_std() => {
426 let &[ref _attr, ref guard_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
427 let guard_size = this.deref_operand(guard_size)?;
428 let guard_size_layout = this.libc_ty_layout("size_t")?;
429 this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), &guard_size.into())?;
431 // Return success (`0`).
432 this.write_null(dest)?;
435 | "pthread_attr_init"
436 | "pthread_attr_destroy"
437 if this.frame_in_std() => {
438 let &[_] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
439 this.write_null(dest)?;
441 | "pthread_attr_setstacksize"
442 if this.frame_in_std() => {
443 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
444 this.write_null(dest)?;
449 if this.frame_in_std() => {
450 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
451 this.write_null(dest)?;
455 if this.frame_in_std() => {
456 let &[_, _, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
457 this.write_null(dest)?;
460 // Platform-specific shims
462 match this.tcx.sess.target.os.as_str() {
463 "linux" => return shims::posix::linux::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
464 "macos" => return shims::posix::macos::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
470 Ok(EmulateByNameResult::NeedsJumping)