]> git.lizzy.rs Git - rust.git/blob - src/shims/posix/foreign_items.rs
Remove `strip_linker_suffix`
[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_scalar(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_scalar(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_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)?;
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_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)?;
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_scalar(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_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)?;
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_scalar(dtor)?.check_init()?;
212
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()?),
216                     None => None,
217                 };
218
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
222                     .builtin_deref(true)
223                     .ok_or_else(|| err_ub_format!(
224                         "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
225                     ))?
226                     .ty;
227                 let key_layout = this.layout_of(key_type)?;
228
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())?;
232
233                 // Return success (`0`).
234                 this.write_null(dest)?;
235             }
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)?;
242             }
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)?;
249             }
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)?)?;
256
257                 // Return success (`0`).
258                 this.write_null(dest)?;
259             }
260
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)?;
266             }
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)?;
271             }
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)?;
276             }
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)?;
281             }
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)?;
286             }
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)?;
291             }
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)?;
296             }
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)?;
301             }
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)?;
306             }
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)?;
311             }
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)?;
316             }
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)?;
321             }
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)?;
326             }
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)?;
331             }
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)?;
336             }
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)?;
341             }
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)?;
346             }
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)?;
351             }
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)?;
356             }
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)?;
361             }
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)?;
365             }
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)?;
370             }
371
372             // Threading
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)?;
377             }
378             "pthread_join" => {
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)?;
382             }
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)?;
387             }
388             "pthread_self" => {
389                 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
390                 this.pthread_self(dest)?;
391             }
392             "sched_yield" => {
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)?;
396             }
397             "nanosleep" => {
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)?;
401             }
402
403             // Miscellaneous
404             "isatty" => {
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)?;
412             }
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)?;
420             }
421
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())?;
430
431                 // Return success (`0`).
432                 this.write_null(dest)?;
433             }
434
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)?;
440             }
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)?;
445             }
446
447             | "signal"
448             | "sigaltstack"
449             if this.frame_in_std() => {
450                 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
451                 this.write_null(dest)?;
452             }
453             | "sigaction"
454             | "mprotect"
455             if this.frame_in_std() => {
456                 let &[_, _, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
457                 this.write_null(dest)?;
458             }
459
460             // Platform-specific shims
461             _ => {
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),
465                     _ => unreachable!(),
466                 }
467             }
468         };
469
470         Ok(EmulateByNameResult::NeedsJumping)
471     }
472 }