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_pointer(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_pointer(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_pointer(buf)?;
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_pointer(buf)?;
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_pointer(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_pointer(symbol)?;
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_pointer(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_pointer(dtor)?;
213 // Extract the function type out of the signature (that seems easier than constructing it ourselves).
214 let dtor = if !this.ptr_is_null(dtor)? {
215 Some(this.memory.get_fn(dtor)?.as_instance()?)
220 // Figure out how large a pthread TLS key actually is.
221 // To this end, deref the argument type. This is `libc::pthread_key_t`.
222 let key_type = key.layout.ty
224 .ok_or_else(|| err_ub_format!(
225 "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
228 let key_layout = this.layout_of(key_type)?;
230 // Create key and write it into the memory where `key_ptr` wants it.
231 let key = this.machine.tls.create_tls_key(dtor, key_layout.size)?;
232 this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place.into())?;
234 // Return success (`0`).
235 this.write_null(dest)?;
237 "pthread_key_delete" => {
238 let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
239 let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?;
240 this.machine.tls.delete_tls_key(key)?;
241 // Return success (0)
242 this.write_null(dest)?;
244 "pthread_getspecific" => {
245 let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
246 let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?;
247 let active_thread = this.get_active_thread();
248 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
249 this.write_scalar(ptr, dest)?;
251 "pthread_setspecific" => {
252 let &[ref key, ref new_ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
253 let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?;
254 let active_thread = this.get_active_thread();
255 let new_data = this.read_scalar(new_ptr)?;
256 this.machine.tls.store_tls(key, active_thread, new_data.check_init()?, &*this.tcx)?;
258 // Return success (`0`).
259 this.write_null(dest)?;
262 // Synchronization primitives
263 "pthread_mutexattr_init" => {
264 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
265 let result = this.pthread_mutexattr_init(attr)?;
266 this.write_scalar(Scalar::from_i32(result), dest)?;
268 "pthread_mutexattr_settype" => {
269 let &[ref attr, ref kind] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
270 let result = this.pthread_mutexattr_settype(attr, kind)?;
271 this.write_scalar(Scalar::from_i32(result), dest)?;
273 "pthread_mutexattr_destroy" => {
274 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
275 let result = this.pthread_mutexattr_destroy(attr)?;
276 this.write_scalar(Scalar::from_i32(result), dest)?;
278 "pthread_mutex_init" => {
279 let &[ref mutex, ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
280 let result = this.pthread_mutex_init(mutex, attr)?;
281 this.write_scalar(Scalar::from_i32(result), dest)?;
283 "pthread_mutex_lock" => {
284 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
285 let result = this.pthread_mutex_lock(mutex)?;
286 this.write_scalar(Scalar::from_i32(result), dest)?;
288 "pthread_mutex_trylock" => {
289 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
290 let result = this.pthread_mutex_trylock(mutex)?;
291 this.write_scalar(Scalar::from_i32(result), dest)?;
293 "pthread_mutex_unlock" => {
294 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
295 let result = this.pthread_mutex_unlock(mutex)?;
296 this.write_scalar(Scalar::from_i32(result), dest)?;
298 "pthread_mutex_destroy" => {
299 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
300 let result = this.pthread_mutex_destroy(mutex)?;
301 this.write_scalar(Scalar::from_i32(result), dest)?;
303 "pthread_rwlock_rdlock" => {
304 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
305 let result = this.pthread_rwlock_rdlock(rwlock)?;
306 this.write_scalar(Scalar::from_i32(result), dest)?;
308 "pthread_rwlock_tryrdlock" => {
309 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
310 let result = this.pthread_rwlock_tryrdlock(rwlock)?;
311 this.write_scalar(Scalar::from_i32(result), dest)?;
313 "pthread_rwlock_wrlock" => {
314 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
315 let result = this.pthread_rwlock_wrlock(rwlock)?;
316 this.write_scalar(Scalar::from_i32(result), dest)?;
318 "pthread_rwlock_trywrlock" => {
319 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
320 let result = this.pthread_rwlock_trywrlock(rwlock)?;
321 this.write_scalar(Scalar::from_i32(result), dest)?;
323 "pthread_rwlock_unlock" => {
324 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
325 let result = this.pthread_rwlock_unlock(rwlock)?;
326 this.write_scalar(Scalar::from_i32(result), dest)?;
328 "pthread_rwlock_destroy" => {
329 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
330 let result = this.pthread_rwlock_destroy(rwlock)?;
331 this.write_scalar(Scalar::from_i32(result), dest)?;
333 "pthread_condattr_init" => {
334 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
335 let result = this.pthread_condattr_init(attr)?;
336 this.write_scalar(Scalar::from_i32(result), dest)?;
338 "pthread_condattr_destroy" => {
339 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
340 let result = this.pthread_condattr_destroy(attr)?;
341 this.write_scalar(Scalar::from_i32(result), dest)?;
343 "pthread_cond_init" => {
344 let &[ref cond, ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
345 let result = this.pthread_cond_init(cond, attr)?;
346 this.write_scalar(Scalar::from_i32(result), dest)?;
348 "pthread_cond_signal" => {
349 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
350 let result = this.pthread_cond_signal(cond)?;
351 this.write_scalar(Scalar::from_i32(result), dest)?;
353 "pthread_cond_broadcast" => {
354 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
355 let result = this.pthread_cond_broadcast(cond)?;
356 this.write_scalar(Scalar::from_i32(result), dest)?;
358 "pthread_cond_wait" => {
359 let &[ref cond, ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
360 let result = this.pthread_cond_wait(cond, mutex)?;
361 this.write_scalar(Scalar::from_i32(result), dest)?;
363 "pthread_cond_timedwait" => {
364 let &[ref cond, ref mutex, ref abstime] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
365 this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
367 "pthread_cond_destroy" => {
368 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
369 let result = this.pthread_cond_destroy(cond)?;
370 this.write_scalar(Scalar::from_i32(result), dest)?;
374 "pthread_create" => {
375 let &[ref thread, ref attr, ref start, ref arg] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
376 let result = this.pthread_create(thread, attr, start, arg)?;
377 this.write_scalar(Scalar::from_i32(result), dest)?;
380 let &[ref thread, ref retval] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
381 let result = this.pthread_join(thread, retval)?;
382 this.write_scalar(Scalar::from_i32(result), dest)?;
384 "pthread_detach" => {
385 let &[ref thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
386 let result = this.pthread_detach(thread)?;
387 this.write_scalar(Scalar::from_i32(result), dest)?;
390 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
391 this.pthread_self(dest)?;
394 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
395 let result = this.sched_yield()?;
396 this.write_scalar(Scalar::from_i32(result), dest)?;
399 let &[ref req, ref rem] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
400 let result = this.nanosleep(req, rem)?;
401 this.write_scalar(Scalar::from_i32(result), dest)?;
406 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
407 this.read_scalar(fd)?.to_i32()?;
408 // "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"
409 // FIXME: we just say nothing is a terminal.
410 let enotty = this.eval_libc("ENOTTY")?;
411 this.set_last_error(enotty)?;
412 this.write_null(dest)?;
414 "pthread_atfork" => {
415 let &[ref prepare, ref parent, ref child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
416 this.read_pointer(prepare)?;
417 this.read_pointer(parent)?;
418 this.read_pointer(child)?;
419 // We do not support forking, so there is nothing to do here.
420 this.write_null(dest)?;
423 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
424 // These shims are enabled only when the caller is in the standard library.
425 "pthread_attr_getguardsize"
426 if this.frame_in_std() => {
427 let &[ref _attr, ref guard_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
428 let guard_size = this.deref_operand(guard_size)?;
429 let guard_size_layout = this.libc_ty_layout("size_t")?;
430 this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), &guard_size.into())?;
432 // Return success (`0`).
433 this.write_null(dest)?;
436 | "pthread_attr_init"
437 | "pthread_attr_destroy"
438 if this.frame_in_std() => {
439 let &[_] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
440 this.write_null(dest)?;
442 | "pthread_attr_setstacksize"
443 if this.frame_in_std() => {
444 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
445 this.write_null(dest)?;
450 if this.frame_in_std() => {
451 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
452 this.write_null(dest)?;
456 if this.frame_in_std() => {
457 let &[_, _, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
458 this.write_null(dest)?;
461 // Platform-specific shims
463 match this.tcx.sess.target.os.as_str() {
464 "linux" => return shims::posix::linux::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
465 "macos" => return shims::posix::macos::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
471 Ok(EmulateByNameResult::NeedsJumping)