]> git.lizzy.rs Git - rust.git/blob - src/shims/posix/foreign_items.rs
26c743b360e064844f11c8284780f99096333b81
[rust.git] / src / shims / posix / foreign_items.rs
1 use log::trace;
2
3 use rustc_middle::mir;
4 use rustc_target::abi::{Align, LayoutOf, Size};
5
6 use crate::*;
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 _;
11
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(
15         &mut self,
16         link_name: &str,
17         args: &[OpTy<'tcx, Tag>],
18         dest: PlaceTy<'tcx, Tag>,
19         ret: mir::BasicBlock,
20     ) -> InterpResult<'tcx, bool> {
21         let this = self.eval_context_mut();
22
23         match link_name {
24             // Environment related shims
25             "getenv" => {
26                 let &[name] = check_arg_count(args)?;
27                 let result = this.getenv(name)?;
28                 this.write_scalar(result, dest)?;
29             }
30             "unsetenv" => {
31                 let &[name] = check_arg_count(args)?;
32                 let result = this.unsetenv(name)?;
33                 this.write_scalar(Scalar::from_i32(result), dest)?;
34             }
35             "setenv" => {
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)?;
40             }
41             "getcwd" => {
42                 let &[buf, size] = check_arg_count(args)?;
43                 let result = this.getcwd(buf, size)?;
44                 this.write_scalar(result, dest)?;
45             }
46             "chdir" => {
47                 let &[path] = check_arg_count(args)?;
48                 let result = this.chdir(path)?;
49                 this.write_scalar(Scalar::from_i32(result), dest)?;
50             }
51
52             // File related shims
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)?;
57             }
58             "fcntl" => {
59                 let result = this.fcntl(args)?;
60                 this.write_scalar(Scalar::from_i32(result), dest)?;
61             }
62             "read" => {
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)?;
69             }
70             "write" => {
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)?;
79             }
80             "unlink" => {
81                 let &[path] = check_arg_count(args)?;
82                 let result = this.unlink(path)?;
83                 this.write_scalar(Scalar::from_i32(result), dest)?;
84             }
85             "symlink" => {
86                 let &[target, linkpath] = check_arg_count(args)?;
87                 let result = this.symlink(target, linkpath)?;
88                 this.write_scalar(Scalar::from_i32(result), dest)?;
89             }
90             "rename" => {
91                 let &[oldpath, newpath] = check_arg_count(args)?;
92                 let result = this.rename(oldpath, newpath)?;
93                 this.write_scalar(Scalar::from_i32(result), dest)?;
94             }
95             "mkdir" => {
96                 let &[path, mode] = check_arg_count(args)?;
97                 let result = this.mkdir(path, mode)?;
98                 this.write_scalar(Scalar::from_i32(result), dest)?;
99             }
100             "rmdir" => {
101                 let &[path] = check_arg_count(args)?;
102                 let result = this.rmdir(path)?;
103                 this.write_scalar(Scalar::from_i32(result), dest)?;
104             }
105             "closedir" => {
106                 let &[dirp] = check_arg_count(args)?;
107                 let result = this.closedir(dirp)?;
108                 this.write_scalar(Scalar::from_i32(result), dest)?;
109             }
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)?;
115             }
116             "fsync" => {
117                 let &[fd] = check_arg_count(args)?;
118                 let result = this.fsync(fd)?;
119                 this.write_scalar(Scalar::from_i32(result), dest)?;
120             }
121             "fdatasync" => {
122                 let &[fd] = check_arg_count(args)?;
123                 let result = this.fdatasync(fd)?;
124                 this.write_scalar(Scalar::from_i32(result), dest)?;
125             }
126
127             // Allocation
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);
136                 }
137                 if align < this.pointer_size().bytes() {
138                     throw_ub_format!(
139                         "posix_memalign: alignment must be at least the size of a pointer, but is {}",
140                         align,
141                     );
142                 }
143
144                 if size == 0 {
145                     this.write_null(ret.into())?;
146                 } else {
147                     let ptr = this.memory.allocate(
148                         Size::from_bytes(size),
149                         Align::from_bytes(align).unwrap(),
150                         MiriMemoryKind::C.into(),
151                     );
152                     this.write_scalar(ptr, ret.into())?;
153                 }
154                 this.write_null(dest)?;
155             }
156
157             // Dynamic symbol loading
158             "dlsym" => {
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)?;
166                 } else {
167                     this.write_null(dest)?;
168                 }
169             }
170
171             // Querying system information
172             "sysconf" => {
173                 let &[name] = check_arg_count(args)?;
174                 let name = this.read_scalar(name)?.to_i32()?;
175
176                 let sysconfs = &[
177                     ("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, this.pointer_size())),
178                     ("_SC_NPROCESSORS_ONLN", Scalar::from_int(NUM_CPUS, this.pointer_size())),
179                 ];
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);
185                         break;
186                     }
187                 }
188                 if let Some(result) = result {
189                     this.write_scalar(result, dest)?;
190                 } else {
191                     throw_unsup_format!("unimplemented sysconf name: {}", name)
192                 }
193             }
194
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()?;
200
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()?),
204                     None => None,
205                 };
206
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
210                     .builtin_deref(true)
211                     .ok_or_else(|| err_ub_format!(
212                         "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
213                     ))?
214                     .ty;
215                 let key_layout = this.layout_of(key_type)?;
216
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())?;
220
221                 // Return success (`0`).
222                 this.write_null(dest)?;
223             }
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)?;
230             }
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)?;
237             }
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)?)?;
244
245                 // Return success (`0`).
246                 this.write_null(dest)?;
247             }
248
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)?;
254             }
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)?;
259             }
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)?;
264             }
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)?;
269             }
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)?;
274             }
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)?;
279             }
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)?;
284             }
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)?;
289             }
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)?;
294             }
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)?;
299             }
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)?;
304             }
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)?;
309             }
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)?;
314             }
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)?;
319             }
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)?;
324             }
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)?;
329             }
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)?;
334             }
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)?;
339             }
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)?;
344             }
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)?;
349             }
350             "pthread_cond_timedwait" => {
351                 let &[cond, mutex, abstime] = check_arg_count(args)?;
352                 this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
353             }
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)?;
358             }
359
360             // Threading
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)?;
365             }
366             "pthread_join" => {
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)?;
370             }
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)?;
375             }
376             "pthread_self" => {
377                 let &[] = check_arg_count(args)?;
378                 this.pthread_self(dest)?;
379             }
380             "sched_yield" => {
381                 let &[] = check_arg_count(args)?;
382                 let result = this.sched_yield()?;
383                 this.write_scalar(Scalar::from_i32(result), dest)?;
384             }
385             "nanosleep" => {
386                 let &[req, rem] = check_arg_count(args)?;
387                 let result = this.nanosleep(req, rem)?;
388                 this.write_scalar(Scalar::from_i32(result), dest)?;
389             }
390
391             // Miscellaneous
392             "isatty" => {
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)?;
400             }
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)?;
408             }
409
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())?;
418
419                 // Return success (`0`).
420                 this.write_null(dest)?;
421             }
422
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)?;
428             }
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)?;
433             }
434
435             | "signal"
436             | "sigaltstack"
437             if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
438                 let &[_, _] = check_arg_count(args)?;
439                 this.write_null(dest)?;
440             }
441             | "sigaction"
442             | "mprotect"
443             if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
444                 let &[_, _, _] = check_arg_count(args)?;
445                 this.write_null(dest)?;
446             }
447
448             // Platform-specific shims
449             _ => {
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),
453                     _ => unreachable!(),
454                 }
455             }
456         };
457
458         Ok(true)
459     }
460 }