4 use rustc_target::abi::{Align, LayoutOf, Size};
7 use helpers::check_arg_count;
8 use shims::posix::fs::EvalContextExt as _;
9 use shims::posix::sync::EvalContextExt as _;
10 use shims::posix::thread::EvalContextExt as _;
12 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
13 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
14 fn emulate_foreign_item_by_name(
17 args: &[OpTy<'tcx, Tag>],
18 dest: PlaceTy<'tcx, Tag>,
20 ) -> InterpResult<'tcx, bool> {
21 let this = self.eval_context_mut();
24 // Environment related shims
26 let &[name] = check_arg_count(args)?;
27 let result = this.getenv(name)?;
28 this.write_scalar(result, dest)?;
31 let &[name] = check_arg_count(args)?;
32 let result = this.unsetenv(name)?;
33 this.write_scalar(Scalar::from_i32(result), dest)?;
36 let &[name, value, overwrite] = check_arg_count(args)?;
37 this.read_scalar(overwrite)?.to_i32()?;
38 let result = this.setenv(name, value)?;
39 this.write_scalar(Scalar::from_i32(result), dest)?;
42 let &[buf, size] = check_arg_count(args)?;
43 let result = this.getcwd(buf, size)?;
44 this.write_scalar(result, dest)?;
47 let &[path] = check_arg_count(args)?;
48 let result = this.chdir(path)?;
49 this.write_scalar(Scalar::from_i32(result), dest)?;
53 "open" | "open64" => {
54 let &[path, flag, mode] = check_arg_count(args)?;
55 let result = this.open(path, flag, mode)?;
56 this.write_scalar(Scalar::from_i32(result), dest)?;
59 let result = this.fcntl(args)?;
60 this.write_scalar(Scalar::from_i32(result), dest)?;
63 let &[fd, buf, count] = check_arg_count(args)?;
64 let fd = this.read_scalar(fd)?.to_i32()?;
65 let buf = this.read_scalar(buf)?.check_init()?;
66 let count = this.read_scalar(count)?.to_machine_usize(this)?;
67 let result = this.read(fd, buf, count)?;
68 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
71 let &[fd, buf, n] = check_arg_count(args)?;
72 let fd = this.read_scalar(fd)?.to_i32()?;
73 let buf = this.read_scalar(buf)?.check_init()?;
74 let count = this.read_scalar(n)?.to_machine_usize(this)?;
75 trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
76 let result = this.write(fd, buf, count)?;
77 // Now, `result` is the value we return back to the program.
78 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
81 let &[path] = check_arg_count(args)?;
82 let result = this.unlink(path)?;
83 this.write_scalar(Scalar::from_i32(result), dest)?;
86 let &[target, linkpath] = check_arg_count(args)?;
87 let result = this.symlink(target, linkpath)?;
88 this.write_scalar(Scalar::from_i32(result), dest)?;
91 let &[oldpath, newpath] = check_arg_count(args)?;
92 let result = this.rename(oldpath, newpath)?;
93 this.write_scalar(Scalar::from_i32(result), dest)?;
96 let &[path, mode] = check_arg_count(args)?;
97 let result = this.mkdir(path, mode)?;
98 this.write_scalar(Scalar::from_i32(result), dest)?;
101 let &[path] = check_arg_count(args)?;
102 let result = this.rmdir(path)?;
103 this.write_scalar(Scalar::from_i32(result), dest)?;
106 let &[dirp] = check_arg_count(args)?;
107 let result = this.closedir(dirp)?;
108 this.write_scalar(Scalar::from_i32(result), dest)?;
110 "lseek" | "lseek64" => {
111 let &[fd, offset, whence] = check_arg_count(args)?;
112 let result = this.lseek64(fd, offset, whence)?;
113 // "lseek" is only used on macOS which is 64bit-only, so `i64` always works.
114 this.write_scalar(Scalar::from_i64(result), dest)?;
117 let &[fd] = check_arg_count(args)?;
118 let result = this.fsync(fd)?;
119 this.write_scalar(Scalar::from_i32(result), dest)?;
122 let &[fd] = check_arg_count(args)?;
123 let result = this.fdatasync(fd)?;
124 this.write_scalar(Scalar::from_i32(result), dest)?;
128 "posix_memalign" => {
129 let &[ret, align, size] = check_arg_count(args)?;
130 let ret = this.deref_operand(ret)?;
131 let align = this.read_scalar(align)?.to_machine_usize(this)?;
132 let size = this.read_scalar(size)?.to_machine_usize(this)?;
133 // Align must be power of 2, and also at least ptr-sized (POSIX rules).
134 if !align.is_power_of_two() {
135 throw_ub_format!("posix_memalign: alignment must be a power of two, but is {}", align);
137 if align < this.pointer_size().bytes() {
139 "posix_memalign: alignment must be at least the size of a pointer, but is {}",
145 this.write_null(ret.into())?;
147 let ptr = this.memory.allocate(
148 Size::from_bytes(size),
149 Align::from_bytes(align).unwrap(),
150 MiriMemoryKind::C.into(),
152 this.write_scalar(ptr, ret.into())?;
154 this.write_null(dest)?;
157 // Dynamic symbol loading
159 let &[handle, symbol] = check_arg_count(args)?;
160 this.read_scalar(handle)?.to_machine_usize(this)?;
161 let symbol = this.read_scalar(symbol)?.check_init()?;
162 let symbol_name = this.memory.read_c_str(symbol)?;
163 if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.target.target_os)? {
164 let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym));
165 this.write_scalar(Scalar::from(ptr), dest)?;
167 this.write_null(dest)?;
171 // Querying system information
173 let &[name] = check_arg_count(args)?;
174 let name = this.read_scalar(name)?.to_i32()?;
177 ("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, this.pointer_size())),
178 ("_SC_NPROCESSORS_ONLN", Scalar::from_int(NUM_CPUS, this.pointer_size())),
180 let mut result = None;
181 for &(sysconf_name, value) in sysconfs {
182 let sysconf_name = this.eval_libc_i32(sysconf_name)?;
183 if sysconf_name == name {
184 result = Some(value);
188 if let Some(result) = result {
189 this.write_scalar(result, dest)?;
191 throw_unsup_format!("unimplemented sysconf name: {}", name)
195 // Thread-local storage
196 "pthread_key_create" => {
197 let &[key, dtor] = check_arg_count(args)?;
198 let key_place = this.deref_operand(key)?;
199 let dtor = this.read_scalar(dtor)?.check_init()?;
201 // Extract the function type out of the signature (that seems easier than constructing it ourselves).
202 let dtor = match this.test_null(dtor)? {
203 Some(dtor_ptr) => Some(this.memory.get_fn(dtor_ptr)?.as_instance()?),
207 // Figure out how large a pthread TLS key actually is.
208 // To this end, deref the argument type. This is `libc::pthread_key_t`.
209 let key_type = key.layout.ty
211 .ok_or_else(|| err_ub_format!(
212 "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
215 let key_layout = this.layout_of(key_type)?;
217 // Create key and write it into the memory where `key_ptr` wants it.
218 let key = this.machine.tls.create_tls_key(dtor, key_layout.size)?;
219 this.write_scalar(Scalar::from_uint(key, key_layout.size), key_place.into())?;
221 // Return success (`0`).
222 this.write_null(dest)?;
224 "pthread_key_delete" => {
225 let &[key] = check_arg_count(args)?;
226 let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?;
227 this.machine.tls.delete_tls_key(key)?;
228 // Return success (0)
229 this.write_null(dest)?;
231 "pthread_getspecific" => {
232 let &[key] = check_arg_count(args)?;
233 let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?;
234 let active_thread = this.get_active_thread();
235 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
236 this.write_scalar(ptr, dest)?;
238 "pthread_setspecific" => {
239 let &[key, new_ptr] = check_arg_count(args)?;
240 let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?;
241 let active_thread = this.get_active_thread();
242 let new_ptr = this.read_scalar(new_ptr)?.check_init()?;
243 this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
245 // Return success (`0`).
246 this.write_null(dest)?;
249 // Synchronization primitives
250 "pthread_mutexattr_init" => {
251 let &[attr] = check_arg_count(args)?;
252 let result = this.pthread_mutexattr_init(attr)?;
253 this.write_scalar(Scalar::from_i32(result), dest)?;
255 "pthread_mutexattr_settype" => {
256 let &[attr, kind] = check_arg_count(args)?;
257 let result = this.pthread_mutexattr_settype(attr, kind)?;
258 this.write_scalar(Scalar::from_i32(result), dest)?;
260 "pthread_mutexattr_destroy" => {
261 let &[attr] = check_arg_count(args)?;
262 let result = this.pthread_mutexattr_destroy(attr)?;
263 this.write_scalar(Scalar::from_i32(result), dest)?;
265 "pthread_mutex_init" => {
266 let &[mutex, attr] = check_arg_count(args)?;
267 let result = this.pthread_mutex_init(mutex, attr)?;
268 this.write_scalar(Scalar::from_i32(result), dest)?;
270 "pthread_mutex_lock" => {
271 let &[mutex] = check_arg_count(args)?;
272 let result = this.pthread_mutex_lock(mutex)?;
273 this.write_scalar(Scalar::from_i32(result), dest)?;
275 "pthread_mutex_trylock" => {
276 let &[mutex] = check_arg_count(args)?;
277 let result = this.pthread_mutex_trylock(mutex)?;
278 this.write_scalar(Scalar::from_i32(result), dest)?;
280 "pthread_mutex_unlock" => {
281 let &[mutex] = check_arg_count(args)?;
282 let result = this.pthread_mutex_unlock(mutex)?;
283 this.write_scalar(Scalar::from_i32(result), dest)?;
285 "pthread_mutex_destroy" => {
286 let &[mutex] = check_arg_count(args)?;
287 let result = this.pthread_mutex_destroy(mutex)?;
288 this.write_scalar(Scalar::from_i32(result), dest)?;
290 "pthread_rwlock_rdlock" => {
291 let &[rwlock] = check_arg_count(args)?;
292 let result = this.pthread_rwlock_rdlock(rwlock)?;
293 this.write_scalar(Scalar::from_i32(result), dest)?;
295 "pthread_rwlock_tryrdlock" => {
296 let &[rwlock] = check_arg_count(args)?;
297 let result = this.pthread_rwlock_tryrdlock(rwlock)?;
298 this.write_scalar(Scalar::from_i32(result), dest)?;
300 "pthread_rwlock_wrlock" => {
301 let &[rwlock] = check_arg_count(args)?;
302 let result = this.pthread_rwlock_wrlock(rwlock)?;
303 this.write_scalar(Scalar::from_i32(result), dest)?;
305 "pthread_rwlock_trywrlock" => {
306 let &[rwlock] = check_arg_count(args)?;
307 let result = this.pthread_rwlock_trywrlock(rwlock)?;
308 this.write_scalar(Scalar::from_i32(result), dest)?;
310 "pthread_rwlock_unlock" => {
311 let &[rwlock] = check_arg_count(args)?;
312 let result = this.pthread_rwlock_unlock(rwlock)?;
313 this.write_scalar(Scalar::from_i32(result), dest)?;
315 "pthread_rwlock_destroy" => {
316 let &[rwlock] = check_arg_count(args)?;
317 let result = this.pthread_rwlock_destroy(rwlock)?;
318 this.write_scalar(Scalar::from_i32(result), dest)?;
320 "pthread_condattr_init" => {
321 let &[attr] = check_arg_count(args)?;
322 let result = this.pthread_condattr_init(attr)?;
323 this.write_scalar(Scalar::from_i32(result), dest)?;
325 "pthread_condattr_destroy" => {
326 let &[attr] = check_arg_count(args)?;
327 let result = this.pthread_condattr_destroy(attr)?;
328 this.write_scalar(Scalar::from_i32(result), dest)?;
330 "pthread_cond_init" => {
331 let &[cond, attr] = check_arg_count(args)?;
332 let result = this.pthread_cond_init(cond, attr)?;
333 this.write_scalar(Scalar::from_i32(result), dest)?;
335 "pthread_cond_signal" => {
336 let &[cond] = check_arg_count(args)?;
337 let result = this.pthread_cond_signal(cond)?;
338 this.write_scalar(Scalar::from_i32(result), dest)?;
340 "pthread_cond_broadcast" => {
341 let &[cond] = check_arg_count(args)?;
342 let result = this.pthread_cond_broadcast(cond)?;
343 this.write_scalar(Scalar::from_i32(result), dest)?;
345 "pthread_cond_wait" => {
346 let &[cond, mutex] = check_arg_count(args)?;
347 let result = this.pthread_cond_wait(cond, mutex)?;
348 this.write_scalar(Scalar::from_i32(result), dest)?;
350 "pthread_cond_timedwait" => {
351 let &[cond, mutex, abstime] = check_arg_count(args)?;
352 this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
354 "pthread_cond_destroy" => {
355 let &[cond] = check_arg_count(args)?;
356 let result = this.pthread_cond_destroy(cond)?;
357 this.write_scalar(Scalar::from_i32(result), dest)?;
361 "pthread_create" => {
362 let &[thread, attr, start, arg] = check_arg_count(args)?;
363 let result = this.pthread_create(thread, attr, start, arg)?;
364 this.write_scalar(Scalar::from_i32(result), dest)?;
367 let &[thread, retval] = check_arg_count(args)?;
368 let result = this.pthread_join(thread, retval)?;
369 this.write_scalar(Scalar::from_i32(result), dest)?;
371 "pthread_detach" => {
372 let &[thread] = check_arg_count(args)?;
373 let result = this.pthread_detach(thread)?;
374 this.write_scalar(Scalar::from_i32(result), dest)?;
377 let &[] = check_arg_count(args)?;
378 this.pthread_self(dest)?;
381 let &[] = check_arg_count(args)?;
382 let result = this.sched_yield()?;
383 this.write_scalar(Scalar::from_i32(result), dest)?;
386 let &[req, rem] = check_arg_count(args)?;
387 let result = this.nanosleep(req, rem)?;
388 this.write_scalar(Scalar::from_i32(result), dest)?;
393 let &[fd] = check_arg_count(args)?;
394 this.read_scalar(fd)?.to_i32()?;
395 // "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"
396 // FIXME: we just say nothing is a terminal.
397 let enotty = this.eval_libc("ENOTTY")?;
398 this.set_last_error(enotty)?;
399 this.write_null(dest)?;
401 "pthread_atfork" => {
402 let &[prepare, parent, child] = check_arg_count(args)?;
403 this.force_bits(this.read_scalar(prepare)?.check_init()?, this.memory.pointer_size())?;
404 this.force_bits(this.read_scalar(parent)?.check_init()?, this.memory.pointer_size())?;
405 this.force_bits(this.read_scalar(child)?.check_init()?, this.memory.pointer_size())?;
406 // We do not support forking, so there is nothing to do here.
407 this.write_null(dest)?;
410 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
411 // These shims are enabled only when the caller is in the standard library.
412 "pthread_attr_getguardsize"
413 if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
414 let &[_attr, guard_size] = check_arg_count(args)?;
415 let guard_size = this.deref_operand(guard_size)?;
416 let guard_size_layout = this.libc_ty_layout("size_t")?;
417 this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), guard_size.into())?;
419 // Return success (`0`).
420 this.write_null(dest)?;
423 | "pthread_attr_init"
424 | "pthread_attr_destroy"
425 if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
426 let &[_] = check_arg_count(args)?;
427 this.write_null(dest)?;
429 | "pthread_attr_setstacksize"
430 if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
431 let &[_, _] = check_arg_count(args)?;
432 this.write_null(dest)?;
437 if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
438 let &[_, _] = check_arg_count(args)?;
439 this.write_null(dest)?;
443 if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
444 let &[_, _, _] = check_arg_count(args)?;
445 this.write_null(dest)?;
448 // Platform-specific shims
450 match this.tcx.sess.target.target.target_os.as_str() {
451 "linux" => return shims::posix::linux::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),
452 "macos" => return shims::posix::macos::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),