Merge pull request #4135 from RalfJung/unsup-targets

Add FreeBSD maintainer; test all of Solarish
This commit is contained in:
Ralf Jung 2025-01-12 16:12:36 +00:00 committed by GitHub
commit 8db1e9a2ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 185 additions and 166 deletions

View file

@ -217,8 +217,8 @@ degree documented below):
- For every other target with OS `linux`, `macos`, or `windows`, Miri should generally work, but we
make no promises and we don't run tests for such targets.
- We have unofficial support (not maintained by the Miri team itself) for some further operating systems.
- `solaris` / `illumos`: maintained by @devnexen. Supports `std::{env, thread, sync}`, but not `std::fs`.
- `freebsd`: **maintainer wanted**. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`.
- `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite.
- `freebsd`: maintained by @YohDeadfall. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`.
- `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works.
- `wasi`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works.
- For targets on other operating systems, Miri might fail before even reaching the `main` function.

View file

@ -147,13 +147,14 @@ case $HOST_TARGET in
# Extra tier 2
TEST_TARGET=arm-unknown-linux-gnueabi run_tests
TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice
# Not officially supported tier 2
TEST_TARGET=x86_64-unknown-illumos run_tests
TEST_TARGET=x86_64-pc-solaris run_tests
# Partially supported targets (tier 2)
BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator
UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe fs
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe fs
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random sync threadname pthread epoll eventfd
TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std

View file

@ -109,56 +109,54 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
// See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern.
#[rustfmt::skip]
match link_name.as_str() {
// Environment related shims
"getenv" => {
let [name] = this.check_shim(abi, Conv::C , link_name, args)?;
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.getenv(name)?;
this.write_pointer(result, dest)?;
}
"unsetenv" => {
let [name] = this.check_shim(abi, Conv::C , link_name, args)?;
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.unsetenv(name)?;
this.write_scalar(result, dest)?;
}
"setenv" => {
let [name, value, overwrite] = this.check_shim(abi, Conv::C , link_name, args)?;
let [name, value, overwrite] = this.check_shim(abi, Conv::C, link_name, args)?;
this.read_scalar(overwrite)?.to_i32()?;
let result = this.setenv(name, value)?;
this.write_scalar(result, dest)?;
}
"getcwd" => {
let [buf, size] = this.check_shim(abi, Conv::C , link_name, args)?;
let [buf, size] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.getcwd(buf, size)?;
this.write_pointer(result, dest)?;
}
"chdir" => {
let [path] = this.check_shim(abi, Conv::C , link_name, args)?;
let [path] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.chdir(path)?;
this.write_scalar(result, dest)?;
}
"getpid" => {
let [] = this.check_shim(abi, Conv::C , link_name, args)?;
let [] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.getpid()?;
this.write_scalar(result, dest)?;
}
"sysconf" => {
let [val] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [val] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.sysconf(val)?;
this.write_scalar(result, dest)?;
}
// File descriptors
"read" => {
let [fd, buf, count] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, buf, count] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
this.read(fd, buf, count, None, dest)?;
}
"write" => {
let [fd, buf, n] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, buf, n] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
@ -166,7 +164,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write(fd, buf, count, None, dest)?;
}
"pread" => {
let [fd, buf, count, offset] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, buf, count, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
@ -174,7 +172,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.read(fd, buf, count, Some(offset), dest)?;
}
"pwrite" => {
let [fd, buf, n, offset] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, buf, n, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
@ -183,49 +181,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write(fd, buf, count, Some(offset), dest)?;
}
"pread64" => {
let [fd, buf, count, offset] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, buf, count, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
let offset =
this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
this.read(fd, buf, count, Some(offset), dest)?;
}
"pwrite64" => {
let [fd, buf, n, offset] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, buf, n, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
let offset =
this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
this.write(fd, buf, count, Some(offset), dest)?;
}
"close" => {
let [fd] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.close(fd)?;
this.write_scalar(result, dest)?;
}
"fcntl" => {
// `fcntl` is variadic. The argument count is checked based on the first argument
// in `this.fcntl()`, so we do not use `check_shim` here.
this.check_abi_and_shim_symbol_clash(abi, Conv::C , link_name)?;
this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?;
let result = this.fcntl(args)?;
this.write_scalar(result, dest)?;
}
"dup" => {
let [old_fd] = this.check_shim(abi, Conv::C , link_name, args)?;
let [old_fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let old_fd = this.read_scalar(old_fd)?.to_i32()?;
let new_fd = this.dup(old_fd)?;
this.write_scalar(new_fd, dest)?;
}
"dup2" => {
let [old_fd, new_fd] = this.check_shim(abi, Conv::C , link_name, args)?;
let [old_fd, new_fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let old_fd = this.read_scalar(old_fd)?.to_i32()?;
let new_fd = this.read_scalar(new_fd)?.to_i32()?;
let result = this.dup2(old_fd, new_fd)?;
this.write_scalar(result, dest)?;
}
"flock" => {
let [fd, op] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, op] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let op = this.read_scalar(op)?.to_i32()?;
let result = this.flock(fd, op)?;
@ -234,48 +234,49 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// File and file system access
"open" | "open64" => {
// `open` is variadic, the third argument is only present when the second argument has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
this.check_abi_and_shim_symbol_clash(abi, Conv::C , link_name)?;
// `open` is variadic, the third argument is only present when the second argument
// has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?;
let result = this.open(args)?;
this.write_scalar(result, dest)?;
}
"unlink" => {
let [path] = this.check_shim(abi, Conv::C , link_name, args)?;
let [path] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.unlink(path)?;
this.write_scalar(result, dest)?;
}
"symlink" => {
let [target, linkpath] = this.check_shim(abi, Conv::C , link_name, args)?;
let [target, linkpath] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.symlink(target, linkpath)?;
this.write_scalar(result, dest)?;
}
"rename" => {
let [oldpath, newpath] = this.check_shim(abi, Conv::C , link_name, args)?;
let [oldpath, newpath] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.rename(oldpath, newpath)?;
this.write_scalar(result, dest)?;
}
"mkdir" => {
let [path, mode] = this.check_shim(abi, Conv::C , link_name, args)?;
let [path, mode] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.mkdir(path, mode)?;
this.write_scalar(result, dest)?;
}
"rmdir" => {
let [path] = this.check_shim(abi, Conv::C , link_name, args)?;
let [path] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.rmdir(path)?;
this.write_scalar(result, dest)?;
}
"opendir" => {
let [name] = this.check_shim(abi, Conv::C , link_name, args)?;
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.opendir(name)?;
this.write_scalar(result, dest)?;
}
"closedir" => {
let [dirp] = this.check_shim(abi, Conv::C , link_name, args)?;
let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.closedir(dirp)?;
this.write_scalar(result, dest)?;
}
"lseek64" => {
let [fd, offset, whence] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, offset, whence] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let offset = this.read_scalar(offset)?.to_i64()?;
let whence = this.read_scalar(whence)?.to_i32()?;
@ -283,7 +284,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"lseek" => {
let [fd, offset, whence] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, offset, whence] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let whence = this.read_scalar(whence)?.to_i32()?;
@ -291,39 +292,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"ftruncate64" => {
let [fd, length] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, length] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let length = this.read_scalar(length)?.to_i64()?;
let result = this.ftruncate64(fd, length.into())?;
this.write_scalar(result, dest)?;
}
"ftruncate" => {
let [fd, length] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, length] = this.check_shim(abi, Conv::C, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let length = this.read_scalar(length)?.to_int(this.libc_ty_layout("off_t").size)?;
let result = this.ftruncate64(fd, length)?;
this.write_scalar(result, dest)?;
}
"fsync" => {
let [fd] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.fsync(fd)?;
this.write_scalar(result, dest)?;
}
"fdatasync" => {
let [fd] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.fdatasync(fd)?;
this.write_scalar(result, dest)?;
}
"readlink" => {
let [pathname, buf, bufsize] = this.check_shim(abi, Conv::C , link_name, args)?;
let [pathname, buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.readlink(pathname, buf, bufsize)?;
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"posix_fadvise" => {
let [fd, offset, len, advice] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [fd, offset, len, advice] = this.check_shim(abi, Conv::C, link_name, args)?;
this.read_scalar(fd)?.to_i32()?;
this.read_target_isize(offset)?;
this.read_target_isize(len)?;
@ -332,12 +330,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_null(dest)?;
}
"realpath" => {
let [path, resolved_path] = this.check_shim(abi, Conv::C , link_name, args)?;
let [path, resolved_path] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.realpath(path, resolved_path)?;
this.write_scalar(result, dest)?;
}
"mkstemp" => {
let [template] = this.check_shim(abi, Conv::C , link_name, args)?;
let [template] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.mkstemp(template)?;
this.write_scalar(result, dest)?;
}
@ -345,63 +343,59 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Unnamed sockets and pipes
"socketpair" => {
let [domain, type_, protocol, sv] =
this.check_shim(abi, Conv::C , link_name, args)?;
this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.socketpair(domain, type_, protocol, sv)?;
this.write_scalar(result, dest)?;
}
"pipe" => {
let [pipefd] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [pipefd] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.pipe2(pipefd, /*flags*/ None)?;
this.write_scalar(result, dest)?;
}
"pipe2" => {
// Currently this function does not exist on all Unixes, e.g. on macOS.
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "solaris" | "illumos") {
throw_unsup_format!(
"`pipe2` is not supported on {}",
this.tcx.sess.target.os
);
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "solaris" | "illumos")
{
throw_unsup_format!("`pipe2` is not supported on {}", this.tcx.sess.target.os);
}
let [pipefd, flags] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [pipefd, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.pipe2(pipefd, Some(flags))?;
this.write_scalar(result, dest)?;
}
// Time
"gettimeofday" => {
let [tv, tz] = this.check_shim(abi, Conv::C , link_name, args)?;
let [tv, tz] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.gettimeofday(tv, tz)?;
this.write_scalar(result, dest)?;
}
"localtime_r" => {
let [timep, result_op] = this.check_shim(abi, Conv::C , link_name, args)?;
let [timep, result_op] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.localtime_r(timep, result_op)?;
this.write_pointer(result, dest)?;
}
"clock_gettime" => {
let [clk_id, tp] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [clk_id, tp] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.clock_gettime(clk_id, tp)?;
this.write_scalar(result, dest)?;
}
// Allocation
"posix_memalign" => {
let [memptr, align, size] = this.check_shim(abi, Conv::C , link_name, args)?;
let [memptr, align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.posix_memalign(memptr, align, size)?;
this.write_scalar(result, dest)?;
}
"mmap" => {
let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, Conv::C , link_name, args)?;
let [addr, length, prot, flags, fd, offset] =
this.check_shim(abi, Conv::C, link_name, args)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let ptr = this.mmap(addr, length, prot, flags, fd, offset)?;
this.write_scalar(ptr, dest)?;
}
"munmap" => {
let [addr, length] = this.check_shim(abi, Conv::C , link_name, args)?;
let [addr, length] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.munmap(addr, length)?;
this.write_scalar(result, dest)?;
}
@ -414,8 +408,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.tcx.sess.target.os
);
}
let [ptr, nmemb, size] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr, nmemb, size] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let nmemb = this.read_target_usize(nmemb)?;
let size = this.read_target_usize(size)?;
@ -438,19 +431,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"aligned_alloc" => {
// This is a C11 function, we assume all Unixes have it.
// (MSVC explicitly does not support this.)
let [align, size] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
let res = this.aligned_alloc(align, size)?;
this.write_pointer(res, dest)?;
}
// Dynamic symbol loading
"dlsym" => {
let [handle, symbol] = this.check_shim(abi, Conv::C , link_name, args)?;
let [handle, symbol] = this.check_shim(abi, Conv::C, link_name, args)?;
this.read_target_usize(handle)?;
let symbol = this.read_pointer(symbol)?;
let name = this.read_c_str(symbol)?;
if let Ok(name) = str::from_utf8(name) && is_dyn_sym(name, &this.tcx.sess.target.os) {
if let Ok(name) = str::from_utf8(name)
&& is_dyn_sym(name, &this.tcx.sess.target.os)
{
let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name)));
this.write_pointer(ptr, dest)?;
} else {
@ -460,7 +454,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Thread-local storage
"pthread_key_create" => {
let [key, dtor] = this.check_shim(abi, Conv::C , link_name, args)?;
let [key, dtor] = this.check_shim(abi, Conv::C, link_name, args)?;
let key_place = this.deref_pointer_as(key, this.libc_ty_layout("pthread_key_t"))?;
let dtor = this.read_pointer(dtor)?;
@ -488,21 +482,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_null(dest)?;
}
"pthread_key_delete" => {
let [key] = this.check_shim(abi, Conv::C , link_name, args)?;
let [key] = this.check_shim(abi, Conv::C, link_name, args)?;
let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
this.machine.tls.delete_tls_key(key)?;
// Return success (0)
this.write_null(dest)?;
}
"pthread_getspecific" => {
let [key] = this.check_shim(abi, Conv::C , link_name, args)?;
let [key] = this.check_shim(abi, Conv::C, link_name, args)?;
let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
let active_thread = this.active_thread();
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
this.write_scalar(ptr, dest)?;
}
"pthread_setspecific" => {
let [key, new_ptr] = this.check_shim(abi, Conv::C , link_name, args)?;
let [key, new_ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
let active_thread = this.active_thread();
let new_data = this.read_scalar(new_ptr)?;
@ -514,151 +508,149 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Synchronization primitives
"pthread_mutexattr_init" => {
let [attr] = this.check_shim(abi, Conv::C , link_name, args)?;
let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_mutexattr_init(attr)?;
this.write_null(dest)?;
}
"pthread_mutexattr_settype" => {
let [attr, kind] = this.check_shim(abi, Conv::C , link_name, args)?;
let [attr, kind] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.pthread_mutexattr_settype(attr, kind)?;
this.write_scalar(result, dest)?;
}
"pthread_mutexattr_destroy" => {
let [attr] = this.check_shim(abi, Conv::C , link_name, args)?;
let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_mutexattr_destroy(attr)?;
this.write_null(dest)?;
}
"pthread_mutex_init" => {
let [mutex, attr] = this.check_shim(abi, Conv::C , link_name, args)?;
let [mutex, attr] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_mutex_init(mutex, attr)?;
this.write_null(dest)?;
}
"pthread_mutex_lock" => {
let [mutex] = this.check_shim(abi, Conv::C , link_name, args)?;
let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_mutex_lock(mutex, dest)?;
}
"pthread_mutex_trylock" => {
let [mutex] = this.check_shim(abi, Conv::C , link_name, args)?;
let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.pthread_mutex_trylock(mutex)?;
this.write_scalar(result, dest)?;
}
"pthread_mutex_unlock" => {
let [mutex] = this.check_shim(abi, Conv::C , link_name, args)?;
let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.pthread_mutex_unlock(mutex)?;
this.write_scalar(result, dest)?;
}
"pthread_mutex_destroy" => {
let [mutex] = this.check_shim(abi, Conv::C , link_name, args)?;
let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_mutex_destroy(mutex)?;
this.write_int(0, dest)?;
}
"pthread_rwlock_rdlock" => {
let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?;
let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_rwlock_rdlock(rwlock, dest)?;
}
"pthread_rwlock_tryrdlock" => {
let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?;
let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.pthread_rwlock_tryrdlock(rwlock)?;
this.write_scalar(result, dest)?;
}
"pthread_rwlock_wrlock" => {
let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?;
let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_rwlock_wrlock(rwlock, dest)?;
}
"pthread_rwlock_trywrlock" => {
let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?;
let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.pthread_rwlock_trywrlock(rwlock)?;
this.write_scalar(result, dest)?;
}
"pthread_rwlock_unlock" => {
let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?;
let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_rwlock_unlock(rwlock)?;
this.write_null(dest)?;
}
"pthread_rwlock_destroy" => {
let [rwlock] = this.check_shim(abi, Conv::C , link_name, args)?;
let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_rwlock_destroy(rwlock)?;
this.write_null(dest)?;
}
"pthread_condattr_init" => {
let [attr] = this.check_shim(abi, Conv::C , link_name, args)?;
let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_condattr_init(attr)?;
this.write_null(dest)?;
}
"pthread_condattr_setclock" => {
let [attr, clock_id] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [attr, clock_id] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.pthread_condattr_setclock(attr, clock_id)?;
this.write_scalar(result, dest)?;
}
"pthread_condattr_getclock" => {
let [attr, clock_id] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [attr, clock_id] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_condattr_getclock(attr, clock_id)?;
this.write_null(dest)?;
}
"pthread_condattr_destroy" => {
let [attr] = this.check_shim(abi, Conv::C , link_name, args)?;
let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_condattr_destroy(attr)?;
this.write_null(dest)?;
}
"pthread_cond_init" => {
let [cond, attr] = this.check_shim(abi, Conv::C , link_name, args)?;
let [cond, attr] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_cond_init(cond, attr)?;
this.write_null(dest)?;
}
"pthread_cond_signal" => {
let [cond] = this.check_shim(abi, Conv::C , link_name, args)?;
let [cond] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_cond_signal(cond)?;
this.write_null(dest)?;
}
"pthread_cond_broadcast" => {
let [cond] = this.check_shim(abi, Conv::C , link_name, args)?;
let [cond] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_cond_broadcast(cond)?;
this.write_null(dest)?;
}
"pthread_cond_wait" => {
let [cond, mutex] = this.check_shim(abi, Conv::C , link_name, args)?;
let [cond, mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_cond_wait(cond, mutex, dest)?;
}
"pthread_cond_timedwait" => {
let [cond, mutex, abstime] = this.check_shim(abi, Conv::C , link_name, args)?;
let [cond, mutex, abstime] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
}
"pthread_cond_destroy" => {
let [cond] = this.check_shim(abi, Conv::C , link_name, args)?;
let [cond] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_cond_destroy(cond)?;
this.write_null(dest)?;
}
// Threading
"pthread_create" => {
let [thread, attr, start, arg] = this.check_shim(abi, Conv::C , link_name, args)?;
let [thread, attr, start, arg] = this.check_shim(abi, Conv::C, link_name, args)?;
this.pthread_create(thread, attr, start, arg)?;
this.write_null(dest)?;
}
"pthread_join" => {
let [thread, retval] = this.check_shim(abi, Conv::C , link_name, args)?;
let [thread, retval] = this.check_shim(abi, Conv::C, link_name, args)?;
let res = this.pthread_join(thread, retval)?;
this.write_scalar(res, dest)?;
}
"pthread_detach" => {
let [thread] = this.check_shim(abi, Conv::C , link_name, args)?;
let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
let res = this.pthread_detach(thread)?;
this.write_scalar(res, dest)?;
}
"pthread_self" => {
let [] = this.check_shim(abi, Conv::C , link_name, args)?;
let [] = this.check_shim(abi, Conv::C, link_name, args)?;
let res = this.pthread_self()?;
this.write_scalar(res, dest)?;
}
"sched_yield" => {
let [] = this.check_shim(abi, Conv::C , link_name, args)?;
let [] = this.check_shim(abi, Conv::C, link_name, args)?;
this.sched_yield()?;
this.write_null(dest)?;
}
"nanosleep" => {
let [req, rem] = this.check_shim(abi, Conv::C , link_name, args)?;
let [req, rem] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.nanosleep(req, rem)?;
this.write_scalar(result, dest)?;
}
@ -671,8 +663,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
);
}
let [pid, cpusetsize, mask] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
let pid = this.read_scalar(pid)?.to_u32()?;
let cpusetsize = this.read_target_usize(cpusetsize)?;
let mask = this.read_pointer(mask)?;
@ -680,7 +671,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
let thread_id = match pid {
0 => this.active_thread(),
_ => throw_unsup_format!("`sched_getaffinity` is only supported with a pid of 0 (indicating the current thread)"),
_ =>
throw_unsup_format!(
"`sched_getaffinity` is only supported with a pid of 0 (indicating the current thread)"
),
};
// The mask is stored in chunks, and the size must be a whole number of chunks.
@ -694,7 +688,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
} else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) {
let cpuset = cpuset.clone();
// we only copy whole chunks of size_of::<c_ulong>()
let byte_count = Ord::min(cpuset.as_slice().len(), cpusetsize.try_into().unwrap());
let byte_count =
Ord::min(cpuset.as_slice().len(), cpusetsize.try_into().unwrap());
this.write_bytes_ptr(mask, cpuset.as_slice()[..byte_count].iter().copied())?;
this.write_null(dest)?;
} else {
@ -711,8 +706,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
);
}
let [pid, cpusetsize, mask] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
let pid = this.read_scalar(pid)?.to_u32()?;
let cpusetsize = this.read_target_usize(cpusetsize)?;
let mask = this.read_pointer(mask)?;
@ -720,7 +714,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
let thread_id = match pid {
0 => this.active_thread(),
_ => throw_unsup_format!("`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)"),
_ =>
throw_unsup_format!(
"`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)"
),
};
if this.ptr_is_null(mask)? {
@ -729,7 +726,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
// Any unspecified bytes are treated as zero here (none of the CPUs are configured).
// This is not exactly documented, so we assume that this is the behavior in practice.
let bits_slice = this.read_bytes_ptr_strip_provenance(mask, Size::from_bytes(cpusetsize))?;
let bits_slice =
this.read_bytes_ptr_strip_provenance(mask, Size::from_bytes(cpusetsize))?;
// This ignores the bytes beyond `CpuAffinityMask::CPU_MASK_BYTES`
let bits_array: [u8; CpuAffinityMask::CPU_MASK_BYTES] =
std::array::from_fn(|i| bits_slice.get(i).copied().unwrap_or(0));
@ -748,12 +746,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Miscellaneous
"isatty" => {
let [fd] = this.check_shim(abi, Conv::C , link_name, args)?;
let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.isatty(fd)?;
this.write_scalar(result, dest)?;
}
"pthread_atfork" => {
let [prepare, parent, child] = this.check_shim(abi, Conv::C , link_name, args)?;
let [prepare, parent, child] = this.check_shim(abi, Conv::C, link_name, args)?;
this.read_pointer(prepare)?;
this.read_pointer(parent)?;
this.read_pointer(child)?;
@ -763,15 +761,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"getentropy" => {
// This function is non-standard but exists with the same signature and behavior on
// Linux, macOS, FreeBSD and Solaris/Illumos.
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd" | "illumos" | "solaris" | "android") {
if !matches!(
&*this.tcx.sess.target.os,
"linux" | "macos" | "freebsd" | "illumos" | "solaris" | "android"
) {
throw_unsup_format!(
"`getentropy` is not supported on {}",
this.tcx.sess.target.os
);
}
let [buf, bufsize] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
let buf = this.read_pointer(buf)?;
let bufsize = this.read_target_usize(bufsize)?;
@ -789,8 +789,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"strerror_r" => {
let [errnum, buf, buflen] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [errnum, buf, buflen] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.strerror_r(errnum, buf, buflen)?;
this.write_scalar(result, dest)?;
}
@ -798,14 +797,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"getrandom" => {
// This function is non-standard but exists with the same signature and behavior on
// Linux, FreeBSD and Solaris/Illumos.
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android") {
if !matches!(
&*this.tcx.sess.target.os,
"linux" | "freebsd" | "illumos" | "solaris" | "android"
) {
throw_unsup_format!(
"`getrandom` is not supported on {}",
this.tcx.sess.target.os
);
}
let [ptr, len, flags] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr, len, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
let _flags = this.read_scalar(flags)?.to_i32()?;
@ -822,7 +823,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.tcx.sess.target.os
);
}
let [ptr, len] = this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr, len] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
this.gen_random(ptr, len)?;
@ -841,7 +842,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// For arm32 they did something custom, but similar enough that the same
// `_Unwind_RaiseException` impl in miri should work:
// https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos") {
if !matches!(
&*this.tcx.sess.target.os,
"linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos"
) {
throw_unsup_format!(
"`_Unwind_RaiseException` is not supported on {}",
this.tcx.sess.target.os
@ -853,43 +857,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(EmulateItemResult::NeedsUnwind);
}
"getuid" => {
let [] = this.check_shim(abi, Conv::C , link_name, args)?;
let [] = this.check_shim(abi, Conv::C, link_name, args)?;
// For now, just pretend we always have this fixed UID.
this.write_int(UID, dest)?;
}
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
"pthread_attr_getguardsize"
if this.frame_in_std() => {
let [_attr, guard_size] = this.check_shim(abi, Conv::C , link_name, args)?;
"pthread_attr_getguardsize" if this.frame_in_std() => {
let [_attr, guard_size] = this.check_shim(abi, Conv::C, link_name, args)?;
let guard_size = this.deref_pointer(guard_size)?;
let guard_size_layout = this.libc_ty_layout("size_t");
this.write_scalar(Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size)?;
this.write_scalar(
Scalar::from_uint(this.machine.page_size, guard_size_layout.size),
&guard_size,
)?;
// Return success (`0`).
this.write_null(dest)?;
}
| "pthread_attr_init"
| "pthread_attr_destroy"
if this.frame_in_std() => {
let [_] = this.check_shim(abi, Conv::C , link_name, args)?;
"pthread_attr_init" | "pthread_attr_destroy" if this.frame_in_std() => {
let [_] = this.check_shim(abi, Conv::C, link_name, args)?;
this.write_null(dest)?;
}
| "pthread_attr_setstacksize"
if this.frame_in_std() => {
let [_, _] = this.check_shim(abi, Conv::C , link_name, args)?;
"pthread_attr_setstacksize" if this.frame_in_std() => {
let [_, _] = this.check_shim(abi, Conv::C, link_name, args)?;
this.write_null(dest)?;
}
"pthread_attr_getstack"
if this.frame_in_std() => {
"pthread_attr_getstack" if this.frame_in_std() => {
// We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here.
// Hence we can mostly ignore the input `attr_place`.
let [attr_place, addr_place, size_place] =
this.check_shim(abi, Conv::C , link_name, args)?;
let _attr_place = this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?;
this.check_shim(abi, Conv::C, link_name, args)?;
let _attr_place =
this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?;
let addr_place = this.deref_pointer(addr_place)?;
let size_place = this.deref_pointer(size_place)?;
@ -906,24 +909,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_null(dest)?;
}
| "signal"
| "sigaltstack"
if this.frame_in_std() => {
let [_, _] = this.check_shim(abi, Conv::C , link_name, args)?;
"signal" | "sigaltstack" if this.frame_in_std() => {
let [_, _] = this.check_shim(abi, Conv::C, link_name, args)?;
this.write_null(dest)?;
}
| "sigaction"
| "mprotect"
if this.frame_in_std() => {
let [_, _, _] = this.check_shim(abi, Conv::C , link_name, args)?;
"sigaction" | "mprotect" if this.frame_in_std() => {
let [_, _, _] = this.check_shim(abi, Conv::C, link_name, args)?;
this.write_null(dest)?;
}
"getpwuid_r" | "__posix_getpwuid_r"
if this.frame_in_std() => {
"getpwuid_r" | "__posix_getpwuid_r" if this.frame_in_std() => {
// getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish
let [uid, pwd, buf, buflen, result] =
this.check_shim(abi, Conv::C , link_name, args)?;
this.check_shim(abi, Conv::C, link_name, args)?;
this.check_no_isolation("`getpwuid_r`")?;
let uid = this.read_scalar(uid)?.to_u32()?;
@ -961,11 +959,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
_ => {
let target_os = &*this.tcx.sess.target.os;
return match target_os {
"android" => android::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
"freebsd" => freebsd::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
"linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
"macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
"solaris" | "illumos" => solarish::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
"android" =>
android::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
),
"freebsd" =>
freebsd::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
),
"linux" =>
linux::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
),
"macos" =>
macos::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
),
"solaris" | "illumos" =>
solarish::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
),
_ => interp_ok(EmulateItemResult::NotSupported),
};
}

View file

@ -78,6 +78,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
// Sockets and pipes
"__xnet_socketpair" => {
let [domain, type_, protocol, sv] =
this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.socketpair(domain, type_, protocol, sv)?;
this.write_scalar(result, dest)?;
}
// Miscellaneous
"___errno" => {
let [] = this.check_shim(abi, Conv::C, link_name, args)?;

View file

@ -1,5 +1,4 @@
//@ignore-target: windows # only very limited libc on Windows
//@ignore-target: apple # `sched_setaffinity` is not supported on macOS
//@only-target: linux # these are Linux-specific APIs
//@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4
fn main() {

View file

@ -1,9 +1,8 @@
//@ignore-target: windows # No `memrchr` on Windows
//@ignore-target: apple # No `memrchr` on some apple targets
//@only-target: linux # `memrchr` is a GNU extension
use std::ptr;
// null is explicitly called out as UB in the C docs.
// null is explicitly called out as UB in the C docs for `memchr`.
fn main() {
unsafe {
libc::memrchr(ptr::null(), 0, 0); //~ERROR: null pointer

View file

@ -1,5 +1,4 @@
//@ignore-target: windows # only very limited libc on Windows
//@ignore-target: apple # `sched_{g, s}etaffinity` are not supported on macOS
//@only-target: linux # these are Linux-specific APIs
//@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4
#![feature(io_error_more)]
#![feature(pointer_is_aligned_to)]