]> git.lizzy.rs Git - rust.git/blob - src/shims/posix/foreign_items.rs
09dd7d9c7b86987ca2b1ac0d142fd80cd77e039b
[rust.git] / src / shims / posix / foreign_items.rs
1 use log::trace;
2
3 use rustc_middle::mir;
4 use rustc_span::Symbol;
5 use rustc_target::abi::{Align, LayoutOf, Size};
6 use rustc_target::spec::abi::Abi;
7
8 use crate::*;
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 _;
13
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(
17         &mut self,
18         link_name: Symbol,
19         abi: Abi,
20         args: &[OpTy<'tcx, Tag>],
21         dest: &PlaceTy<'tcx, Tag>,
22         ret: mir::BasicBlock,
23     ) -> InterpResult<'tcx, EmulateByNameResult> {
24         let this = self.eval_context_mut();
25
26         match &*link_name.as_str() {
27             // Environment related shims
28             "getenv" => {
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)?;
32             }
33             "unsetenv" => {
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)?;
37             }
38             "setenv" => {
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)?;
43             }
44             "getcwd" => {
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)?;
48             }
49             "chdir" => {
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)?;
53             }
54
55             // File related shims
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)?;
60             }
61             "fcntl" => {
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)?;
67             }
68             "read" => {
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)?;
75             }
76             "write" => {
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)?;
85             }
86             "unlink" => {
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)?;
90             }
91             "symlink" => {
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)?;
95             }
96             "rename" => {
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)?;
100             }
101             "mkdir" => {
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)?;
105             }
106             "rmdir" => {
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)?;
110             }
111             "closedir" => {
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)?;
115             }
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)?;
121             }
122             "fsync" => {
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)?;
126             }
127             "fdatasync" => {
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)?;
131             }
132             "readlink" => {
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)?;
136             }
137
138             // Allocation
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);
147                 }
148                 if align < this.pointer_size().bytes() {
149                     throw_ub_format!(
150                         "posix_memalign: alignment must be at least the size of a pointer, but is {}",
151                         align,
152                     );
153                 }
154
155                 if size == 0 {
156                     this.write_null(&ret.into())?;
157                 } else {
158                     let ptr = this.memory.allocate(
159                         Size::from_bytes(size),
160                         Align::from_bytes(align).unwrap(),
161                         MiriMemoryKind::C.into(),
162                     )?;
163                     this.write_pointer(ptr, &ret.into())?;
164                 }
165                 this.write_null(dest)?;
166             }
167
168             // Dynamic symbol loading
169             "dlsym" => {
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)?;
177                 } else {
178                     this.write_null(dest)?;
179                 }
180             }
181
182             // Querying system information
183             "sysconf" => {
184                 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
185                 let name = this.read_scalar(name)?.to_i32()?;
186
187                 let sysconfs = &[
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())),
191                 ];
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);
197                         break;
198                     }
199                 }
200                 if let Some(result) = result {
201                     this.write_scalar(result, dest)?;
202                 } else {
203                     throw_unsup_format!("unimplemented sysconf name: {}", name)
204                 }
205             }
206
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)?;
212
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()?)
216                 } else {
217                     None
218                 };
219
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
223                     .builtin_deref(true)
224                     .ok_or_else(|| err_ub_format!(
225                         "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
226                     ))?
227                     .ty;
228                 let key_layout = this.layout_of(key_type)?;
229
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())?;
233
234                 // Return success (`0`).
235                 this.write_null(dest)?;
236             }
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)?;
243             }
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)?;
250             }
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)?;
257
258                 // Return success (`0`).
259                 this.write_null(dest)?;
260             }
261
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)?;
267             }
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)?;
272             }
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)?;
277             }
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)?;
282             }
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)?;
287             }
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)?;
292             }
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)?;
297             }
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)?;
302             }
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)?;
307             }
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)?;
312             }
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)?;
317             }
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)?;
322             }
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)?;
327             }
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)?;
332             }
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)?;
337             }
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)?;
342             }
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)?;
347             }
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)?;
352             }
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)?;
357             }
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)?;
362             }
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)?;
366             }
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)?;
371             }
372
373             // Threading
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)?;
378             }
379             "pthread_join" => {
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)?;
383             }
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)?;
388             }
389             "pthread_self" => {
390                 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
391                 this.pthread_self(dest)?;
392             }
393             "sched_yield" => {
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)?;
397             }
398             "nanosleep" => {
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)?;
402             }
403
404             // Miscellaneous
405             "isatty" => {
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)?;
413             }
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)?;
421             }
422
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())?;
431
432                 // Return success (`0`).
433                 this.write_null(dest)?;
434             }
435
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)?;
441             }
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)?;
446             }
447
448             | "signal"
449             | "sigaltstack"
450             if this.frame_in_std() => {
451                 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
452                 this.write_null(dest)?;
453             }
454             | "sigaction"
455             | "mprotect"
456             if this.frame_in_std() => {
457                 let &[_, _, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
458                 this.write_null(dest)?;
459             }
460
461             // Platform-specific shims
462             _ => {
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),
466                     _ => unreachable!(),
467                 }
468             }
469         };
470
471         Ok(EmulateByNameResult::NeedsJumping)
472     }
473 }