Auto merge of #2493 - RalfJung:android, r=RalfJung

add very basic Android support

This is just enough to print to stdout. I won't push this any further, but having these basics should hopefully make it easier for others to do so.

Also slightly improve threading support on FreeBSD while we are at it.

Partially based on https://github.com/rust-lang/miri/pull/2011.
Fixes https://github.com/rust-lang/miri/issues/2010.
This commit is contained in:
bors 2022-08-18 13:13:21 +00:00
commit 339500f060
15 changed files with 160 additions and 23 deletions

3
ci.sh
View File

@ -83,7 +83,8 @@ case $HOST_TARGET in
MIRI_TEST_TARGET=i686-unknown-linux-gnu run_tests
MIRI_TEST_TARGET=aarch64-apple-darwin run_tests
MIRI_TEST_TARGET=i686-pc-windows-msvc run_tests
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec data_race env/var
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple atomic data_race env/var
MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic
MIRI_TEST_TARGET=thumbv7em-none-eabihf MIRI_NO_STD=1 run_tests_minimal no_std # no_std embedded architecture
;;
x86_64-apple-darwin)

View File

@ -1 +1 @@
8556e6620e4866526b3cea767ad8c20ae877a569
9c20b2a8cc7588decb6de25ac6a7912dcef24d65

View File

@ -954,5 +954,5 @@ pub fn get_local_crates(tcx: TyCtxt<'_>) -> Vec<CrateNum> {
/// Helper function used inside the shims of foreign functions to check that
/// `target_os` is a supported UNIX OS.
pub fn target_os_is_unix(target_os: &str) -> bool {
matches!(target_os, "linux" | "macos" | "freebsd")
matches!(target_os, "linux" | "macos" | "freebsd" | "android")
}

View File

@ -24,6 +24,7 @@
clippy::derive_hash_xor_eq,
clippy::too_many_arguments,
clippy::type_complexity,
clippy::single_element_loop,
// We are not implementing queries here so it's fine
rustc::potential_query_instability
)]

View File

@ -232,13 +232,15 @@ pub struct PrimitiveLayouts<'tcx> {
pub u32: TyAndLayout<'tcx>,
pub usize: TyAndLayout<'tcx>,
pub bool: TyAndLayout<'tcx>,
pub mut_raw_ptr: TyAndLayout<'tcx>,
pub mut_raw_ptr: TyAndLayout<'tcx>, // *mut ()
pub const_raw_ptr: TyAndLayout<'tcx>, // *const ()
}
impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, LayoutError<'tcx>> {
let tcx = layout_cx.tcx;
let mut_raw_ptr = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut });
let const_raw_ptr = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
Ok(Self {
unit: layout_cx.layout_of(tcx.mk_unit())?,
i8: layout_cx.layout_of(tcx.types.i8)?,
@ -251,6 +253,7 @@ impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
usize: layout_cx.layout_of(tcx.types.usize)?,
bool: layout_cx.layout_of(tcx.types.bool)?,
mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?,
const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?,
})
}
}
@ -431,6 +434,17 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
}
fn alloc_extern_static(
this: &mut MiriEvalContext<'mir, 'tcx>,
name: &str,
val: ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx> {
let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
this.write_immediate(*val, &place.into())?;
Self::add_extern_static(this, name, place.ptr);
Ok(())
}
/// Sets up the "extern statics" for this machine.
fn init_extern_statics(this: &mut MiriEvalContext<'mir, 'tcx>) -> InterpResult<'tcx> {
match this.tcx.sess.target.os.as_ref() {
@ -447,10 +461,8 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
// syscall that we do support).
for name in &["__cxa_thread_atexit_impl", "getrandom", "statx", "__clock_gettime64"]
{
let layout = this.machine.layouts.usize;
let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into())?;
this.write_scalar(Scalar::from_machine_usize(0, this), &place.into())?;
Self::add_extern_static(this, name, place.ptr);
let val = ImmTy::from_int(0, this.machine.layouts.usize);
Self::alloc_extern_static(this, name, val)?;
}
}
"freebsd" => {
@ -461,13 +473,27 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
this.machine.env_vars.environ.unwrap().ptr,
);
}
"android" => {
// "signal"
let layout = this.machine.layouts.const_raw_ptr;
let dlsym = Dlsym::from_str("signal".as_bytes(), &this.tcx.sess.target.os)?
.expect("`signal` must be an actual dlsym on android");
let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout);
Self::alloc_extern_static(this, "signal", val)?;
// A couple zero-initialized pointer-sized extern statics.
// Most of them are for weak symbols, which we all set to null (indicating that the
// symbol is not supported, and triggering fallback code.)
for name in &["bsd_signal"] {
let val = ImmTy::from_int(0, this.machine.layouts.usize);
Self::alloc_extern_static(this, name, val)?;
}
}
"windows" => {
// "_tls_used"
// This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
let layout = this.machine.layouts.u8;
let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into())?;
this.write_scalar(Scalar::from_u8(0), &place.into())?;
Self::add_extern_static(this, "_tls_used", place.ptr);
let val = ImmTy::from_int(0, this.machine.layouts.u8);
Self::alloc_extern_static(this, "_tls_used", val)?;
}
_ => {} // No "extern statics" supported on this target
}

View File

@ -0,0 +1,54 @@
use rustc_middle::mir;
use crate::helpers::check_arg_count;
use crate::*;
#[derive(Debug, Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum Dlsym {
signal,
}
impl Dlsym {
// Returns an error for unsupported symbols, and None if this symbol
// should become a NULL pointer (pretend it does not exist).
pub fn from_str<'tcx>(name: &str) -> InterpResult<'tcx, Option<Dlsym>> {
Ok(match name {
"signal" => Some(Dlsym::signal),
"android_set_abort_message" => None,
_ => throw_unsup_format!("unsupported Android dlsym: {}", name),
})
}
}
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
fn call_dlsym(
&mut self,
dlsym: Dlsym,
args: &[OpTy<'tcx, Provenance>],
dest: &PlaceTy<'tcx, Provenance>,
ret: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let ret = ret.expect("we don't support any diverging dlsym");
assert!(this.tcx.sess.target.os == "android");
match dlsym {
Dlsym::signal => {
if !this.frame_in_std() {
throw_unsup_format!(
"`signal` support is crude and just enough for libstd to work"
);
}
let &[ref _sig, ref _func] = check_arg_count(args)?;
this.write_null(dest)?;
}
}
log::trace!("{:?}", this.dump_place(**dest));
this.go_to_block(ret);
Ok(())
}
}

View File

@ -0,0 +1,26 @@
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;
use crate::*;
use shims::foreign_items::EmulateByNameResult;
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
fn emulate_foreign_item_by_name(
&mut self,
link_name: Symbol,
_abi: Abi,
_args: &[OpTy<'tcx, Provenance>],
_dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
let _this = self.eval_context_mut();
#[allow(clippy::match_single_binding)]
match link_name.as_str() {
_ => return Ok(EmulateByNameResult::NotSupported),
}
#[allow(unreachable_code)]
Ok(EmulateByNameResult::NeedsJumping)
}
}

View File

@ -0,0 +1,2 @@
pub mod dlsym;
pub mod foreign_items;

View File

@ -2,15 +2,17 @@ use rustc_middle::mir;
use rustc_target::spec::abi::Abi;
use crate::*;
use shims::unix::android::dlsym as android;
use shims::unix::freebsd::dlsym as freebsd;
use shims::unix::linux::dlsym as linux;
use shims::unix::macos::dlsym as macos;
#[derive(Debug, Copy, Clone)]
pub enum Dlsym {
Android(android::Dlsym),
FreeBsd(freebsd::Dlsym),
Linux(linux::Dlsym),
MacOs(macos::Dlsym),
FreeBsd(freebsd::Dlsym),
}
impl Dlsym {
@ -18,10 +20,11 @@ impl Dlsym {
// should become a NULL pointer (pretend it does not exist).
pub fn from_str<'tcx>(name: &str, target_os: &str) -> InterpResult<'tcx, Option<Dlsym>> {
Ok(match target_os {
"android" => android::Dlsym::from_str(name)?.map(Dlsym::Android),
"freebsd" => freebsd::Dlsym::from_str(name)?.map(Dlsym::FreeBsd),
"linux" => linux::Dlsym::from_str(name)?.map(Dlsym::Linux),
"macos" => macos::Dlsym::from_str(name)?.map(Dlsym::MacOs),
"freebsd" => freebsd::Dlsym::from_str(name)?.map(Dlsym::FreeBsd),
_ => unreachable!(),
_ => panic!("unsupported Unix OS {target_os}"),
})
}
}
@ -41,10 +44,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.check_abi(abi, Abi::C { unwind: false })?;
match dlsym {
Dlsym::Linux(dlsym) => linux::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
Dlsym::MacOs(dlsym) => macos::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
Dlsym::Android(dlsym) =>
android::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
Dlsym::FreeBsd(dlsym) =>
freebsd::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
Dlsym::Linux(dlsym) => linux::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
Dlsym::MacOs(dlsym) => macos::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
}
}
}

View File

@ -24,6 +24,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
let this = self.eval_context_mut();
#[rustfmt::skip]
match link_name.as_str() {
// Environment related shims
"getenv" => {
@ -588,11 +589,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// Platform-specific shims
_ => {
match this.tcx.sess.target.os.as_ref() {
let target_os = &*this.tcx.sess.target.os;
match target_os {
"android" => return shims::unix::android::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest),
"freebsd" => return shims::unix::freebsd::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest),
"linux" => return shims::unix::linux::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest),
"macos" => return shims::unix::macos::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest),
"freebsd" => return shims::unix::freebsd::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest),
_ => unreachable!(),
_ => panic!("unsupported Unix OS {target_os}"),
}
}
};

View File

@ -28,5 +28,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
assert!(this.tcx.sess.target.os == "freebsd");
match dlsym {}
//trace!("{:?}", this.dump_place(**dest));
//this.go_to_block(ret);
//Ok(())
}
}

View File

@ -3,6 +3,7 @@ use rustc_target::spec::abi::Abi;
use crate::*;
use shims::foreign_items::EmulateByNameResult;
use shims::unix::thread::EvalContextExt as _;
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
@ -16,12 +17,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
let this = self.eval_context_mut();
match link_name.as_str() {
// Linux's `pthread_getattr_np` equivalent
// Threading
"pthread_attr_get_np" if this.frame_in_std() => {
let [_thread, _attr] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.write_null(dest)?;
}
"pthread_set_name_np" => {
let [thread, name] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let res = this.pthread_setname_np(
this.read_scalar(thread)?.check_init()?,
this.read_scalar(name)?.check_init()?,
)?;
this.write_scalar(res, dest)?;
}
// errno
"__error" => {

View File

@ -32,5 +32,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
assert!(this.tcx.sess.target.os == "linux");
match dlsym {}
//trace!("{:?}", this.dump_place(**dest));
//this.go_to_block(ret);
//Ok(())
}
}

View File

@ -5,6 +5,7 @@ mod fs;
mod sync;
mod thread;
mod android;
mod freebsd;
mod linux;
mod macos;

View File

@ -45,7 +45,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
Dlsym::NtWriteFile => {
if !this.frame_in_std() {
throw_unsup_format!(
"NtWriteFile support is crude and just enough for stdout to work"
"`NtWriteFile` support is crude and just enough for stdout to work"
);
}
@ -68,7 +68,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
if byte_offset != 0 {
throw_unsup_format!(
"NtWriteFile ByteOffset paremeter is non-null, which is unsupported"
"`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported"
);
}