1 use log::trace; 2 3 use rustc_middle::mir; 4 use rustc_middle::ty::layout::LayoutOf; 5 use rustc_span::Symbol; 6 use rustc_target::abi::{Align, Size}; 7 use rustc_target::spec::abi::Abi; 8 9 use crate::*; 10 use shims::foreign_items::EmulateByNameResult; 11 use shims::posix::fs::EvalContextExt as _; 12 use shims::posix::sync::EvalContextExt as _; 13 use shims::posix::thread::EvalContextExt as _; 14 15 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} 16 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { emulate_foreign_item_by_name( &mut self, link_name: Symbol, abi: Abi, args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, ret: mir::BasicBlock, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>>17 fn emulate_foreign_item_by_name( 18 &mut self, 19 link_name: Symbol, 20 abi: Abi, 21 args: &[OpTy<'tcx, Tag>], 22 dest: &PlaceTy<'tcx, Tag>, 23 ret: mir::BasicBlock, 24 ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { 25 let this = self.eval_context_mut(); 26 27 match &*link_name.as_str() { 28 // Environment related shims 29 "getenv" => { 30 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 31 let result = this.getenv(name)?; 32 this.write_pointer(result, dest)?; 33 } 34 "unsetenv" => { 35 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 36 let result = this.unsetenv(name)?; 37 this.write_scalar(Scalar::from_i32(result), dest)?; 38 } 39 "setenv" => { 40 let &[ref name, ref value, ref overwrite] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 41 this.read_scalar(overwrite)?.to_i32()?; 42 let result = this.setenv(name, value)?; 43 this.write_scalar(Scalar::from_i32(result), dest)?; 44 } 45 "getcwd" => { 46 let &[ref buf, ref size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 47 let result = this.getcwd(buf, size)?; 48 this.write_pointer(result, dest)?; 49 } 50 "chdir" => { 51 let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 52 let result = this.chdir(path)?; 53 this.write_scalar(Scalar::from_i32(result), dest)?; 54 } 55 56 // File related shims 57 "open" | "open64" => { 58 let &[ref path, ref flag, ref mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 59 let result = this.open(path, flag, mode)?; 60 this.write_scalar(Scalar::from_i32(result), dest)?; 61 } 62 "fcntl" => { 63 // `fcntl` is variadic. The argument count is checked based on the first argument 64 // in `this.fcntl()`, so we do not use `check_shim` here. 65 this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?; 66 let result = this.fcntl(args)?; 67 this.write_scalar(Scalar::from_i32(result), dest)?; 68 } 69 "read" => { 70 let &[ref fd, ref buf, ref count] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 71 let fd = this.read_scalar(fd)?.to_i32()?; 72 let buf = this.read_pointer(buf)?; 73 let count = this.read_scalar(count)?.to_machine_usize(this)?; 74 let result = this.read(fd, buf, count)?; 75 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; 76 } 77 "write" => { 78 let &[ref fd, ref buf, ref n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 79 let fd = this.read_scalar(fd)?.to_i32()?; 80 let buf = this.read_pointer(buf)?; 81 let count = this.read_scalar(n)?.to_machine_usize(this)?; 82 trace!("Called write({:?}, {:?}, {:?})", fd, buf, count); 83 let result = this.write(fd, buf, count)?; 84 // Now, `result` is the value we return back to the program. 85 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; 86 } 87 "unlink" => { 88 let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 89 let result = this.unlink(path)?; 90 this.write_scalar(Scalar::from_i32(result), dest)?; 91 } 92 "symlink" => { 93 let &[ref target, ref linkpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 94 let result = this.symlink(target, linkpath)?; 95 this.write_scalar(Scalar::from_i32(result), dest)?; 96 } 97 "rename" => { 98 let &[ref oldpath, ref newpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 99 let result = this.rename(oldpath, newpath)?; 100 this.write_scalar(Scalar::from_i32(result), dest)?; 101 } 102 "mkdir" => { 103 let &[ref path, ref mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 104 let result = this.mkdir(path, mode)?; 105 this.write_scalar(Scalar::from_i32(result), dest)?; 106 } 107 "rmdir" => { 108 let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 109 let result = this.rmdir(path)?; 110 this.write_scalar(Scalar::from_i32(result), dest)?; 111 } 112 "closedir" => { 113 let &[ref dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 114 let result = this.closedir(dirp)?; 115 this.write_scalar(Scalar::from_i32(result), dest)?; 116 } 117 "lseek" | "lseek64" => { 118 let &[ref fd, ref offset, ref whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 119 let result = this.lseek64(fd, offset, whence)?; 120 // "lseek" is only used on macOS which is 64bit-only, so `i64` always works. 121 this.write_scalar(Scalar::from_i64(result), dest)?; 122 } 123 "fsync" => { 124 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 125 let result = this.fsync(fd)?; 126 this.write_scalar(Scalar::from_i32(result), dest)?; 127 } 128 "fdatasync" => { 129 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 130 let result = this.fdatasync(fd)?; 131 this.write_scalar(Scalar::from_i32(result), dest)?; 132 } 133 "readlink" => { 134 let &[ref pathname, ref buf, ref bufsize] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 135 let result = this.readlink(pathname, buf, bufsize)?; 136 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; 137 } 138 139 // Allocation 140 "posix_memalign" => { 141 let &[ref ret, ref align, ref size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 142 let ret = this.deref_operand(ret)?; 143 let align = this.read_scalar(align)?.to_machine_usize(this)?; 144 let size = this.read_scalar(size)?.to_machine_usize(this)?; 145 // Align must be power of 2, and also at least ptr-sized (POSIX rules). 146 if !align.is_power_of_two() { 147 throw_ub_format!("posix_memalign: alignment must be a power of two, but is {}", align); 148 } 149 if align < this.pointer_size().bytes() { 150 throw_ub_format!( 151 "posix_memalign: alignment must be at least the size of a pointer, but is {}", 152 align, 153 ); 154 } 155 156 if size == 0 { 157 this.write_null(&ret.into())?; 158 } else { 159 let ptr = this.memory.allocate( 160 Size::from_bytes(size), 161 Align::from_bytes(align).unwrap(), 162 MiriMemoryKind::C.into(), 163 )?; 164 this.write_pointer(ptr, &ret.into())?; 165 } 166 this.write_null(dest)?; 167 } 168 169 // Dynamic symbol loading 170 "dlsym" => { 171 let &[ref handle, ref symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 172 this.read_scalar(handle)?.to_machine_usize(this)?; 173 let symbol = this.read_pointer(symbol)?; 174 let symbol_name = this.read_c_str(symbol)?; 175 if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.os)? { 176 let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym)); 177 this.write_pointer(ptr, dest)?; 178 } else { 179 this.write_null(dest)?; 180 } 181 } 182 183 // Querying system information 184 "sysconf" => { 185 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 186 let name = this.read_scalar(name)?.to_i32()?; 187 188 let sysconfs = &[ 189 ("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, this.pointer_size())), 190 ("_SC_NPROCESSORS_CONF", Scalar::from_int(NUM_CPUS, this.pointer_size())), 191 ("_SC_NPROCESSORS_ONLN", Scalar::from_int(NUM_CPUS, this.pointer_size())), 192 ]; 193 let mut result = None; 194 for &(sysconf_name, value) in sysconfs { 195 let sysconf_name = this.eval_libc_i32(sysconf_name)?; 196 if sysconf_name == name { 197 result = Some(value); 198 break; 199 } 200 } 201 if let Some(result) = result { 202 this.write_scalar(result, dest)?; 203 } else { 204 throw_unsup_format!("unimplemented sysconf name: {}", name) 205 } 206 } 207 208 // Thread-local storage 209 "pthread_key_create" => { 210 let &[ref key, ref dtor] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 211 let key_place = this.deref_operand(key)?; 212 let dtor = this.read_pointer(dtor)?; 213 214 // Extract the function type out of the signature (that seems easier than constructing it ourselves). 215 let dtor = if !this.ptr_is_null(dtor)? { 216 Some(this.memory.get_fn(dtor)?.as_instance()?) 217 } else { 218 None 219 }; 220 221 // Figure out how large a pthread TLS key actually is. 222 // To this end, deref the argument type. This is `libc::pthread_key_t`. 223 let key_type = key.layout.ty 224 .builtin_deref(true) 225 .ok_or_else(|| err_ub_format!( 226 "wrong signature used for `pthread_key_create`: first argument must be a raw pointer." 227 ))? 228 .ty; 229 let key_layout = this.layout_of(key_type)?; 230 231 // Create key and write it into the memory where `key_ptr` wants it. 232 let key = this.machine.tls.create_tls_key(dtor, key_layout.size)?; 233 this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place.into())?; 234 235 // Return success (`0`). 236 this.write_null(dest)?; 237 } 238 "pthread_key_delete" => { 239 let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 240 let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?; 241 this.machine.tls.delete_tls_key(key)?; 242 // Return success (0) 243 this.write_null(dest)?; 244 } 245 "pthread_getspecific" => { 246 let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 247 let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?; 248 let active_thread = this.get_active_thread(); 249 let ptr = this.machine.tls.load_tls(key, active_thread, this)?; 250 this.write_scalar(ptr, dest)?; 251 } 252 "pthread_setspecific" => { 253 let &[ref key, ref new_ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 254 let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?; 255 let active_thread = this.get_active_thread(); 256 let new_data = this.read_scalar(new_ptr)?; 257 this.machine.tls.store_tls(key, active_thread, new_data.check_init()?, &*this.tcx)?; 258 259 // Return success (`0`). 260 this.write_null(dest)?; 261 } 262 263 // Synchronization primitives 264 "pthread_mutexattr_init" => { 265 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 266 let result = this.pthread_mutexattr_init(attr)?; 267 this.write_scalar(Scalar::from_i32(result), dest)?; 268 } 269 "pthread_mutexattr_settype" => { 270 let &[ref attr, ref kind] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 271 let result = this.pthread_mutexattr_settype(attr, kind)?; 272 this.write_scalar(Scalar::from_i32(result), dest)?; 273 } 274 "pthread_mutexattr_destroy" => { 275 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 276 let result = this.pthread_mutexattr_destroy(attr)?; 277 this.write_scalar(Scalar::from_i32(result), dest)?; 278 } 279 "pthread_mutex_init" => { 280 let &[ref mutex, ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 281 let result = this.pthread_mutex_init(mutex, attr)?; 282 this.write_scalar(Scalar::from_i32(result), dest)?; 283 } 284 "pthread_mutex_lock" => { 285 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 286 let result = this.pthread_mutex_lock(mutex)?; 287 this.write_scalar(Scalar::from_i32(result), dest)?; 288 } 289 "pthread_mutex_trylock" => { 290 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 291 let result = this.pthread_mutex_trylock(mutex)?; 292 this.write_scalar(Scalar::from_i32(result), dest)?; 293 } 294 "pthread_mutex_unlock" => { 295 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 296 let result = this.pthread_mutex_unlock(mutex)?; 297 this.write_scalar(Scalar::from_i32(result), dest)?; 298 } 299 "pthread_mutex_destroy" => { 300 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 301 let result = this.pthread_mutex_destroy(mutex)?; 302 this.write_scalar(Scalar::from_i32(result), dest)?; 303 } 304 "pthread_rwlock_rdlock" => { 305 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 306 let result = this.pthread_rwlock_rdlock(rwlock)?; 307 this.write_scalar(Scalar::from_i32(result), dest)?; 308 } 309 "pthread_rwlock_tryrdlock" => { 310 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 311 let result = this.pthread_rwlock_tryrdlock(rwlock)?; 312 this.write_scalar(Scalar::from_i32(result), dest)?; 313 } 314 "pthread_rwlock_wrlock" => { 315 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 316 let result = this.pthread_rwlock_wrlock(rwlock)?; 317 this.write_scalar(Scalar::from_i32(result), dest)?; 318 } 319 "pthread_rwlock_trywrlock" => { 320 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 321 let result = this.pthread_rwlock_trywrlock(rwlock)?; 322 this.write_scalar(Scalar::from_i32(result), dest)?; 323 } 324 "pthread_rwlock_unlock" => { 325 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 326 let result = this.pthread_rwlock_unlock(rwlock)?; 327 this.write_scalar(Scalar::from_i32(result), dest)?; 328 } 329 "pthread_rwlock_destroy" => { 330 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 331 let result = this.pthread_rwlock_destroy(rwlock)?; 332 this.write_scalar(Scalar::from_i32(result), dest)?; 333 } 334 "pthread_condattr_init" => { 335 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 336 let result = this.pthread_condattr_init(attr)?; 337 this.write_scalar(Scalar::from_i32(result), dest)?; 338 } 339 "pthread_condattr_destroy" => { 340 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 341 let result = this.pthread_condattr_destroy(attr)?; 342 this.write_scalar(Scalar::from_i32(result), dest)?; 343 } 344 "pthread_cond_init" => { 345 let &[ref cond, ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 346 let result = this.pthread_cond_init(cond, attr)?; 347 this.write_scalar(Scalar::from_i32(result), dest)?; 348 } 349 "pthread_cond_signal" => { 350 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 351 let result = this.pthread_cond_signal(cond)?; 352 this.write_scalar(Scalar::from_i32(result), dest)?; 353 } 354 "pthread_cond_broadcast" => { 355 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 356 let result = this.pthread_cond_broadcast(cond)?; 357 this.write_scalar(Scalar::from_i32(result), dest)?; 358 } 359 "pthread_cond_wait" => { 360 let &[ref cond, ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 361 let result = this.pthread_cond_wait(cond, mutex)?; 362 this.write_scalar(Scalar::from_i32(result), dest)?; 363 } 364 "pthread_cond_timedwait" => { 365 let &[ref cond, ref mutex, ref abstime] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 366 this.pthread_cond_timedwait(cond, mutex, abstime, dest)?; 367 } 368 "pthread_cond_destroy" => { 369 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 370 let result = this.pthread_cond_destroy(cond)?; 371 this.write_scalar(Scalar::from_i32(result), dest)?; 372 } 373 374 // Threading 375 "pthread_create" => { 376 let &[ref thread, ref attr, ref start, ref arg] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 377 let result = this.pthread_create(thread, attr, start, arg)?; 378 this.write_scalar(Scalar::from_i32(result), dest)?; 379 } 380 "pthread_join" => { 381 let &[ref thread, ref retval] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 382 let result = this.pthread_join(thread, retval)?; 383 this.write_scalar(Scalar::from_i32(result), dest)?; 384 } 385 "pthread_detach" => { 386 let &[ref thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 387 let result = this.pthread_detach(thread)?; 388 this.write_scalar(Scalar::from_i32(result), dest)?; 389 } 390 "pthread_self" => { 391 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 392 this.pthread_self(dest)?; 393 } 394 "sched_yield" => { 395 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 396 let result = this.sched_yield()?; 397 this.write_scalar(Scalar::from_i32(result), dest)?; 398 } 399 "nanosleep" => { 400 let &[ref req, ref rem] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 401 let result = this.nanosleep(req, rem)?; 402 this.write_scalar(Scalar::from_i32(result), dest)?; 403 } 404 405 // Miscellaneous 406 "isatty" => { 407 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 408 this.read_scalar(fd)?.to_i32()?; 409 // "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" 410 // FIXME: we just say nothing is a terminal. 411 let enotty = this.eval_libc("ENOTTY")?; 412 this.set_last_error(enotty)?; 413 this.write_null(dest)?; 414 } 415 "pthread_atfork" => { 416 let &[ref prepare, ref parent, ref child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 417 this.read_pointer(prepare)?; 418 this.read_pointer(parent)?; 419 this.read_pointer(child)?; 420 // We do not support forking, so there is nothing to do here. 421 this.write_null(dest)?; 422 } 423 424 // Incomplete shims that we "stub out" just to get pre-main initialization code to work. 425 // These shims are enabled only when the caller is in the standard library. 426 "pthread_attr_getguardsize" 427 if this.frame_in_std() => { 428 let &[ref _attr, ref guard_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 429 let guard_size = this.deref_operand(guard_size)?; 430 let guard_size_layout = this.libc_ty_layout("size_t")?; 431 this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), &guard_size.into())?; 432 433 // Return success (`0`). 434 this.write_null(dest)?; 435 } 436 437 | "pthread_attr_init" 438 | "pthread_attr_destroy" 439 if this.frame_in_std() => { 440 let &[_] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 441 this.write_null(dest)?; 442 } 443 | "pthread_attr_setstacksize" 444 if this.frame_in_std() => { 445 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 446 this.write_null(dest)?; 447 } 448 449 | "signal" 450 | "sigaltstack" 451 if this.frame_in_std() => { 452 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 453 this.write_null(dest)?; 454 } 455 | "sigaction" 456 | "mprotect" 457 if this.frame_in_std() => { 458 let &[_, _, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; 459 this.write_null(dest)?; 460 } 461 462 // Platform-specific shims 463 _ => { 464 match this.tcx.sess.target.os.as_str() { 465 "linux" => return shims::posix::linux::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret), 466 "macos" => return shims::posix::macos::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret), 467 _ => unreachable!(), 468 } 469 } 470 }; 471 472 Ok(EmulateByNameResult::NeedsJumping) 473 } 474 } 475