mirror of
https://github.com/facebookexperimental/reverie.git
synced 2025-02-02 09:45:45 +00:00
Get tests building under aarch64
Reviewed By: VladimirMakaev Differential Revision: D40701839 fbshipit-source-id: e60053c7d21696e7f7dd120420b896dee3d65ba7
This commit is contained in:
parent
21f7f9a8a6
commit
ada1e3c542
14 changed files with 186 additions and 234 deletions
|
@ -196,13 +196,22 @@ impl Tool for ChunkyPrintLocal {
|
||||||
let _ = guest.send_rpc(Msg::Tick).await;
|
let _ = guest.send_rpc(Msg::Tick).await;
|
||||||
match call {
|
match call {
|
||||||
// Here we make some attempt to catch redirections:
|
// Here we make some attempt to catch redirections:
|
||||||
|
// FIXME: De-dup the dup
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
Syscall::Dup2(d) => {
|
Syscall::Dup2(d) => {
|
||||||
let newfd = d.newfd();
|
match d.newfd() {
|
||||||
if newfd == 1 {
|
1 => self.stdout_disconnected.store(true, Ordering::SeqCst),
|
||||||
self.stdout_disconnected.store(true, Ordering::SeqCst);
|
2 => self.stderr_disconnected.store(true, Ordering::SeqCst),
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
if newfd == 2 {
|
|
||||||
self.stderr_disconnected.store(true, Ordering::SeqCst);
|
guest.tail_inject(call).await
|
||||||
|
}
|
||||||
|
Syscall::Dup3(d) => {
|
||||||
|
match d.newfd() {
|
||||||
|
1 => self.stdout_disconnected.store(true, Ordering::SeqCst),
|
||||||
|
2 => self.stderr_disconnected.store(true, Ordering::SeqCst),
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
guest.tail_inject(call).await
|
guest.tail_inject(call).await
|
||||||
|
|
|
@ -56,21 +56,10 @@ impl Tool for PedigreeLocal {
|
||||||
syscall: Syscall,
|
syscall: Syscall,
|
||||||
) -> Result<i64, Error> {
|
) -> Result<i64, Error> {
|
||||||
match syscall {
|
match syscall {
|
||||||
Syscall::Fork(_) | Syscall::Vfork(_) | Syscall::Clone(_) => {
|
#[cfg(target_arch = "x86_64")]
|
||||||
let retval = guest.inject(syscall).await?;
|
Syscall::Fork(_) | Syscall::Vfork(_) => self.handle_fork(syscall, guest).await,
|
||||||
let pedigree = guest.thread_state_mut().0.fork_mut();
|
Syscall::Clone(_) => self.handle_fork(syscall, guest).await,
|
||||||
trace!(
|
Syscall::Getpid(_) | Syscall::Getppid(_) | Syscall::Gettid(_) | Syscall::Getpgid(_) => {
|
||||||
"got new pedigree: {:?} => {:x?}",
|
|
||||||
pedigree,
|
|
||||||
nix::unistd::Pid::try_from(&pedigree)
|
|
||||||
);
|
|
||||||
Ok(retval)
|
|
||||||
}
|
|
||||||
Syscall::Getpid(_)
|
|
||||||
| Syscall::Getppid(_)
|
|
||||||
| Syscall::Gettid(_)
|
|
||||||
| Syscall::Getpgid(_)
|
|
||||||
| Syscall::Getpgrp(_) => {
|
|
||||||
let pid = guest.inject(syscall).await?;
|
let pid = guest.inject(syscall).await?;
|
||||||
let vpid = nix::unistd::Pid::try_from(&self.0).unwrap();
|
let vpid = nix::unistd::Pid::try_from(&self.0).unwrap();
|
||||||
trace!("getpid returned {:?} vpid: {:?}", pid, vpid);
|
trace!("getpid returned {:?} vpid: {:?}", pid, vpid);
|
||||||
|
@ -84,6 +73,23 @@ impl Tool for PedigreeLocal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PedigreeLocal {
|
||||||
|
async fn handle_fork(
|
||||||
|
&self,
|
||||||
|
syscall: Syscall,
|
||||||
|
guest: &mut impl Guest<Self>,
|
||||||
|
) -> Result<i64, Error> {
|
||||||
|
let retval = guest.inject(syscall).await?;
|
||||||
|
let pedigree = guest.thread_state_mut().0.fork_mut();
|
||||||
|
trace!(
|
||||||
|
"got new pedigree: {:?} => {:x?}",
|
||||||
|
pedigree,
|
||||||
|
nix::unistd::Pid::try_from(&pedigree)
|
||||||
|
);
|
||||||
|
Ok(retval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Error> {
|
async fn main() -> Result<(), Error> {
|
||||||
let args = CommonToolArguments::from_args();
|
let args = CommonToolArguments::from_args();
|
||||||
|
|
|
@ -61,39 +61,43 @@ impl From<WriteFamily> for Syscall {
|
||||||
/// Represents the stat family of syscalls. All of these have an associated stat
|
/// Represents the stat family of syscalls. All of these have an associated stat
|
||||||
/// buffer.
|
/// buffer.
|
||||||
// Stat not available in aarch64
|
// Stat not available in aarch64
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
|
||||||
#[derive(From, Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(From, Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum StatFamily {
|
pub enum StatFamily {
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
Stat(super::Stat),
|
Stat(super::Stat),
|
||||||
Fstat(super::Fstat),
|
Fstat(super::Fstat),
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
Lstat(super::Lstat),
|
Lstat(super::Lstat),
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
Newfstatat(super::Newfstatat),
|
Newfstatat(super::Newfstatat),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat not available in aarch64
|
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
|
||||||
impl StatFamily {
|
impl StatFamily {
|
||||||
/// Get address of the stat buffer. Returns `None` if a NULL pointer was
|
/// Get address of the stat buffer. Returns `None` if a NULL pointer was
|
||||||
/// specified.
|
/// specified.
|
||||||
pub fn stat(&self) -> Option<StatPtr> {
|
pub fn stat(&self) -> Option<StatPtr> {
|
||||||
match self {
|
match self {
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
Self::Stat(s) => s.stat(),
|
Self::Stat(s) => s.stat(),
|
||||||
Self::Fstat(s) => s.stat(),
|
Self::Fstat(s) => s.stat(),
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
Self::Lstat(s) => s.stat(),
|
Self::Lstat(s) => s.stat(),
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
Self::Newfstatat(s) => s.stat(),
|
Self::Newfstatat(s) => s.stat(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat not available in aarch64
|
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
|
||||||
impl From<StatFamily> for Syscall {
|
impl From<StatFamily> for Syscall {
|
||||||
fn from(family: StatFamily) -> Syscall {
|
fn from(family: StatFamily) -> Syscall {
|
||||||
match family {
|
match family {
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
StatFamily::Stat(syscall) => Syscall::Stat(syscall),
|
StatFamily::Stat(syscall) => Syscall::Stat(syscall),
|
||||||
StatFamily::Fstat(syscall) => Syscall::Fstat(syscall),
|
StatFamily::Fstat(syscall) => Syscall::Fstat(syscall),
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
StatFamily::Lstat(syscall) => Syscall::Lstat(syscall),
|
StatFamily::Lstat(syscall) => Syscall::Lstat(syscall),
|
||||||
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
StatFamily::Newfstatat(syscall) => Syscall::Newfstatat(syscall),
|
StatFamily::Newfstatat(syscall) => Syscall::Newfstatat(syscall),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,6 @@ syscall_list! {
|
||||||
close => Close,
|
close => Close,
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
stat => Stat,
|
stat => Stat,
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
|
||||||
fstat => Fstat,
|
fstat => Fstat,
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
lstat => Lstat,
|
lstat => Lstat,
|
||||||
|
@ -392,6 +391,7 @@ syscall_list! {
|
||||||
fchownat => Fchownat,
|
fchownat => Fchownat,
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
futimesat => Futimesat,
|
futimesat => Futimesat,
|
||||||
|
// TODO: Rename this to Fstatat.
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
newfstatat => Newfstatat,
|
newfstatat => Newfstatat,
|
||||||
unlinkat => Unlinkat,
|
unlinkat => Unlinkat,
|
||||||
|
@ -573,8 +573,6 @@ typed_syscall! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fstat not available in aarch64
|
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
|
||||||
typed_syscall! {
|
typed_syscall! {
|
||||||
pub struct Fstat {
|
pub struct Fstat {
|
||||||
fd: i32,
|
fd: i32,
|
||||||
|
@ -2671,7 +2669,8 @@ typed_syscall! {
|
||||||
flags: AtFlags,
|
flags: AtFlags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Stat not available in aarch64
|
|
||||||
|
// Newfstatat not available in aarch64
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
impl From<Stat> for Newfstatat {
|
impl From<Stat> for Newfstatat {
|
||||||
fn from(stat: Stat) -> Self {
|
fn from(stat: Stat) -> Self {
|
||||||
|
@ -2704,6 +2703,7 @@ typed_syscall! {
|
||||||
flags: AtFlags,
|
flags: AtFlags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlink not available in aarch64
|
// Unlink not available in aarch64
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
impl From<Unlink> for Unlinkat {
|
impl From<Unlink> for Unlinkat {
|
||||||
|
|
|
@ -226,8 +226,8 @@ fn i_should_segfault() {
|
||||||
use reverie_ptrace::testing::test_fn;
|
use reverie_ptrace::testing::test_fn;
|
||||||
let (output, _) = test_fn::<NoopTool, _>(|| {
|
let (output, _) = test_fn::<NoopTool, _>(|| {
|
||||||
unsafe {
|
unsafe {
|
||||||
let invalid_pointer = 0x5u64 as *mut u64;
|
let invalid_ptr = 0x5u64 as *mut u64;
|
||||||
std::ptr::write(invalid_pointer, 0xdeadbeefu64);
|
invalid_ptr.write(0xdeadbeefu64);
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -240,26 +240,15 @@ fn i_should_segfault_2() {
|
||||||
use nix::sys::signal::Signal::SIGSEGV;
|
use nix::sys::signal::Signal::SIGSEGV;
|
||||||
use reverie_ptrace::testing::test_fn;
|
use reverie_ptrace::testing::test_fn;
|
||||||
|
|
||||||
#[inline]
|
pub fn do_segfault() {
|
||||||
#[cfg(not(feature = "llvm_asm"))]
|
let invalid_ptr = 0x1234 as *const usize;
|
||||||
unsafe fn do_segfault() {
|
let result = unsafe { invalid_ptr.read() };
|
||||||
let null_ptr: *const usize = core::ptr::null();
|
// Print so the above doesn't get optimized out. We will never get here
|
||||||
core::arch::asm!(
|
// because the above segfaults.
|
||||||
"jmp {0}",
|
println!("{}", result);
|
||||||
in(reg) null_ptr,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
let (output, _) = test_fn::<NoopTool, _>(|| do_segfault()).unwrap();
|
||||||
#[cfg(feature = "llvm_asm")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
unsafe fn do_segfault() {
|
|
||||||
llvm_asm!(r#"mov $$0, %rax
|
|
||||||
jmpq *%rax
|
|
||||||
"#:::"rax")
|
|
||||||
}
|
|
||||||
|
|
||||||
let (output, _) = test_fn::<NoopTool, _>(|| unsafe { do_segfault() }).unwrap();
|
|
||||||
assert_eq!(output.status, ExitStatus::Signaled(SIGSEGV, true),);
|
assert_eq!(output.status, ExitStatus::Signaled(SIGSEGV, true),);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use raw_cpuid::cpuid;
|
|
||||||
use reverie::syscalls::Syscall;
|
use reverie::syscalls::Syscall;
|
||||||
use reverie::CpuIdResult;
|
use reverie::CpuIdResult;
|
||||||
use reverie::Errno;
|
use reverie::Errno;
|
||||||
|
@ -27,8 +26,6 @@ use reverie::GlobalRPC;
|
||||||
use reverie::GlobalTool;
|
use reverie::GlobalTool;
|
||||||
use reverie::Guest;
|
use reverie::Guest;
|
||||||
use reverie::Pid;
|
use reverie::Pid;
|
||||||
use reverie::Rdtsc;
|
|
||||||
use reverie::RdtscResult;
|
|
||||||
use reverie::Signal;
|
use reverie::Signal;
|
||||||
use reverie::Tid;
|
use reverie::Tid;
|
||||||
use reverie::TimerSchedule;
|
use reverie::TimerSchedule;
|
||||||
|
@ -124,6 +121,7 @@ impl Tool for LocalState {
|
||||||
guest.tail_inject(syscall).await
|
guest.tail_inject(syscall).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
async fn handle_cpuid_event<T: Guest<Self>>(
|
async fn handle_cpuid_event<T: Guest<Self>>(
|
||||||
&self,
|
&self,
|
||||||
guest: &mut T,
|
guest: &mut T,
|
||||||
|
@ -131,16 +129,17 @@ impl Tool for LocalState {
|
||||||
ecx: u32,
|
ecx: u32,
|
||||||
) -> Result<CpuIdResult, Errno> {
|
) -> Result<CpuIdResult, Errno> {
|
||||||
guest.send_rpc(IncrMsg::Increment).await;
|
guest.send_rpc(IncrMsg::Increment).await;
|
||||||
Ok(cpuid!(eax, ecx))
|
Ok(raw_cpuid::cpuid!(eax, ecx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
async fn handle_rdtsc_event<T: Guest<Self>>(
|
async fn handle_rdtsc_event<T: Guest<Self>>(
|
||||||
&self,
|
&self,
|
||||||
guest: &mut T,
|
guest: &mut T,
|
||||||
request: Rdtsc,
|
request: reverie::Rdtsc,
|
||||||
) -> Result<RdtscResult, Errno> {
|
) -> Result<reverie::RdtscResult, Errno> {
|
||||||
guest.send_rpc(IncrMsg::Increment).await;
|
guest.send_rpc(IncrMsg::Increment).await;
|
||||||
Ok(RdtscResult::new(request))
|
Ok(reverie::RdtscResult::new(request))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_signal_event<T: Guest<Self>>(
|
async fn handle_signal_event<T: Guest<Self>>(
|
||||||
|
@ -179,13 +178,6 @@ impl Tool for LocalState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inform the Tool to begin counting events via a specific syscall
|
|
||||||
fn do_marker_syscall() {
|
|
||||||
unsafe {
|
|
||||||
libc::clock_getres(libc::CLOCK_MONOTONIC, std::ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(sanitized), test))]
|
#[cfg(all(not(sanitized), test))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -196,6 +188,13 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// Inform the Tool to begin counting events via a specific syscall
|
||||||
|
fn do_marker_syscall() {
|
||||||
|
unsafe {
|
||||||
|
libc::clock_getres(libc::CLOCK_MONOTONIC, std::ptr::null_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn guest_busywait_no_timer() {
|
fn guest_busywait_no_timer() {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
* 748: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
|
* 748: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
* 74f: 00
|
* 74f: 00
|
||||||
*/
|
*/
|
||||||
|
#if defined(__x86_64__)
|
||||||
__attribute__((noinline)) static int sys_getpid(void) {
|
__attribute__((noinline)) static int sys_getpid(void) {
|
||||||
int ret;
|
int ret;
|
||||||
asm volatile(
|
asm volatile(
|
||||||
|
@ -29,6 +30,14 @@ __attribute__((noinline)) static int sys_getpid(void) {
|
||||||
: "=r"(ret));
|
: "=r"(ret));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
__attribute__((noinline)) static int sys_getpid(void) {
|
||||||
|
register long x8 __asm__("x8") = 172;
|
||||||
|
register long x0 __asm__("x0");
|
||||||
|
asm volatile("svc 0" : "=r"(x0) : "r"(x8) : "memory", "cc");
|
||||||
|
return (int)x0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
int pid0 = getpid();
|
int pid0 = getpid();
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
|
#![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
|
||||||
|
// FIXME: This test does some very x86_64-specific things.
|
||||||
|
#![cfg(target_arch = "x86_64")]
|
||||||
|
|
||||||
// when we convert syscall, such as open -> openat, the old syscall
|
// when we convert syscall, such as open -> openat, the old syscall
|
||||||
// args should not be clobbered, even with the conversion.
|
// args should not be clobbered, even with the conversion.
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
|
|
||||||
//! Tests cpuid interception
|
//! Tests cpuid interception
|
||||||
|
|
||||||
|
// cpuid interception is only available on x86_64
|
||||||
|
#![cfg(target_arch = "x86_64")]
|
||||||
|
|
||||||
use raw_cpuid::CpuIdResult;
|
use raw_cpuid::CpuIdResult;
|
||||||
use reverie::Errno;
|
use reverie::Errno;
|
||||||
use reverie::GlobalTool;
|
use reverie::GlobalTool;
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// rdtsc interception is only available on x86_64
|
||||||
|
#![cfg(target_arch = "x86_64")]
|
||||||
|
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ impl Tool for LocalState {
|
||||||
let exit_failure = ExitGroup::new().with_status(1);
|
let exit_failure = ExitGroup::new().with_status(1);
|
||||||
match syscall {
|
match syscall {
|
||||||
// glibc should wrap signalfd -> signalfd4(2).
|
// glibc should wrap signalfd -> signalfd4(2).
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
Syscall::Signalfd(_) => guest.tail_inject(exit_failure).await,
|
Syscall::Signalfd(_) => guest.tail_inject(exit_failure).await,
|
||||||
Syscall::Signalfd4(_) => {
|
Syscall::Signalfd4(_) => {
|
||||||
let (_, args) = syscall.into_parts();
|
let (_, args) = syscall.into_parts();
|
||||||
|
|
|
@ -8,11 +8,6 @@
|
||||||
|
|
||||||
// reinject stat* as fstatat unittest
|
// reinject stat* as fstatat unittest
|
||||||
|
|
||||||
use reverie::syscalls;
|
|
||||||
use reverie::syscalls::Displayable;
|
|
||||||
use reverie::syscalls::Syscall;
|
|
||||||
use reverie::Error;
|
|
||||||
use reverie::Guest;
|
|
||||||
use reverie::Tool;
|
use reverie::Tool;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -20,31 +15,8 @@ use serde::Serialize;
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
struct LocalState;
|
struct LocalState;
|
||||||
|
|
||||||
async fn handle_newfstatat<T: Guest<LocalState>>(
|
|
||||||
guest: &mut T,
|
|
||||||
call: syscalls::Newfstatat,
|
|
||||||
) -> Result<i64, Error> {
|
|
||||||
let res = guest.inject(call).await;
|
|
||||||
|
|
||||||
println!("{} = {:?}", call.display(&guest.memory()), res);
|
|
||||||
|
|
||||||
Ok(res?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[reverie::tool]
|
#[reverie::tool]
|
||||||
impl Tool for LocalState {
|
impl Tool for LocalState {}
|
||||||
async fn handle_syscall_event<T: Guest<Self>>(
|
|
||||||
&self,
|
|
||||||
guest: &mut T,
|
|
||||||
syscall: Syscall,
|
|
||||||
) -> Result<i64, Error> {
|
|
||||||
match syscall {
|
|
||||||
Syscall::Stat(stat) => handle_newfstatat(guest, stat.into()).await,
|
|
||||||
Syscall::Lstat(lstat) => handle_newfstatat(guest, lstat.into()).await,
|
|
||||||
_ => guest.tail_inject(syscall).await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(sanitized), test))]
|
#[cfg(all(not(sanitized), test))]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -79,7 +51,7 @@ mod tests {
|
||||||
// glibc doesn't provide wrapper for statx
|
// glibc doesn't provide wrapper for statx
|
||||||
unsafe fn statx(
|
unsafe fn statx(
|
||||||
dirfd: i32,
|
dirfd: i32,
|
||||||
path: *const i8,
|
path: *const libc::c_char,
|
||||||
flags: i32,
|
flags: i32,
|
||||||
mask: u32,
|
mask: u32,
|
||||||
statxbuf: *mut libc::statx,
|
statxbuf: *mut libc::statx,
|
||||||
|
|
|
@ -11,10 +11,6 @@
|
||||||
//! Syscalls are abused to communicate from the guest to the tool instructions
|
//! Syscalls are abused to communicate from the guest to the tool instructions
|
||||||
//! necessary to carry out the test, such as setting timers or reading clocks.
|
//! necessary to carry out the test, such as setting timers or reading clocks.
|
||||||
|
|
||||||
#![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
|
|
||||||
use core::arch::x86_64::__cpuid;
|
|
||||||
use core::arch::x86_64::__rdtscp;
|
|
||||||
use core::arch::x86_64::_rdtsc;
|
|
||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
@ -202,126 +198,8 @@ async fn raise_sigwinch<T: Guest<LocalState>>(guest: &mut T) -> Tgkill {
|
||||||
.with_sig(libc::SIGWINCH)
|
.with_sig(libc::SIGWINCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Use the syscalls crate for doing this when it switches to using the
|
|
||||||
// `asm!()` macro instead of asm inside of a C file.
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
|
||||||
#[cfg(not(feature = "llvm_asm"))]
|
|
||||||
unsafe fn syscall_no_branches(no: libc::c_long, arg1: libc::c_long) {
|
|
||||||
let mut _ret: u64;
|
|
||||||
core::arch::asm!(
|
|
||||||
"syscall",
|
|
||||||
lateout("rax") _ret,
|
|
||||||
in("rax") no,
|
|
||||||
in("rdi") arg1,
|
|
||||||
out("rcx") _, // rcx is used to store old rip
|
|
||||||
out("r11") _, // r11 is used to store old rflags
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
|
||||||
#[cfg(feature = "llvm_asm")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
unsafe fn syscall_no_branches(no: libc::c_long, arg1: libc::c_long) {
|
|
||||||
llvm_asm!("
|
|
||||||
mov $0, %rax
|
|
||||||
mov $1, %rdi
|
|
||||||
xor %rsi, %rsi
|
|
||||||
xor %rdx, %rdx
|
|
||||||
xor %r10, %r10
|
|
||||||
xor %r8, %r8
|
|
||||||
xor %r9, %r9
|
|
||||||
syscall
|
|
||||||
"
|
|
||||||
: /* no output */
|
|
||||||
: "r"(no), "r"(arg1)
|
|
||||||
: "cc", "rax", "rdi", "rsi", "rdx", "r10", "r8", "r9", /* from syscall: */ "rcx", "r11"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sched_precise() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_clock_getres, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sched_precise_alternate_rcb_count() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_msgrcv, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sched_imprecise() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_timer_getoverrun, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mark_clock() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_clock_settime, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_clock(delta: u64) {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_clock_adjtime, delta as i64) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_clock_at_next_timer(value: u64) {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_timer_gettime, value as i64) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_syscall() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_clock_gettime, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn immediate_exit() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_exit, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sched_precise_and_raise() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_fanotify_init, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sched_imprecise_and_raise() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_fanotify_mark, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sched_precise_and_inject() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_msgctl, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sched_imprecise_and_inject() {
|
|
||||||
unsafe { syscall_no_branches(libc::SYS_msgget, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cpuid() {
|
|
||||||
unsafe {
|
|
||||||
__cpuid(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rdtsc() {
|
|
||||||
unsafe {
|
|
||||||
_rdtsc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rdtscp() {
|
|
||||||
unsafe {
|
|
||||||
let mut x = 0u32;
|
|
||||||
__rdtscp(&mut x as *mut _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ts_check_fn(rcbs: u64, f: impl FnOnce()) -> GlobalState {
|
|
||||||
use reverie_ptrace::testing::check_fn_with_config;
|
|
||||||
check_fn_with_config::<LocalState, _>(
|
|
||||||
f,
|
|
||||||
Config {
|
|
||||||
timeout_rcbs: rcbs,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const MANY_RCBS: u64 = 10000; // Normal perf signaling
|
|
||||||
const LESS_RCBS: u64 = 15; // Low enough to use artificial signaling
|
|
||||||
|
|
||||||
#[cfg(all(not(sanitized), test))]
|
#[cfg(all(not(sanitized), test))]
|
||||||
mod timer_tests {
|
mod tests {
|
||||||
//! These tests are highly sensitive to the number of branches executed
|
//! These tests are highly sensitive to the number of branches executed
|
||||||
//! in the guest, and this must remain consistent between opt and debug
|
//! in the guest, and this must remain consistent between opt and debug
|
||||||
//! mode. If you pass non-constant values into do_branches and need them to
|
//! mode. If you pass non-constant values into do_branches and need them to
|
||||||
|
@ -330,12 +208,103 @@ mod timer_tests {
|
||||||
//! tests.
|
//! tests.
|
||||||
|
|
||||||
use reverie_ptrace::ret_without_perf;
|
use reverie_ptrace::ret_without_perf;
|
||||||
|
use reverie_ptrace::testing::check_fn;
|
||||||
use reverie_ptrace::testing::check_fn_with_config;
|
use reverie_ptrace::testing::check_fn_with_config;
|
||||||
use reverie_ptrace::testing::do_branches;
|
use reverie_ptrace::testing::do_branches;
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn syscall_no_branches(no: Sysno, arg1: libc::c_long) {
|
||||||
|
syscalls::syscall!(no, arg1, 0, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sched_precise() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::clock_getres, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sched_precise_alternate_rcb_count() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::msgrcv, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sched_imprecise() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::timer_getoverrun, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_clock() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::clock_settime, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_clock(delta: u64) {
|
||||||
|
unsafe { syscall_no_branches(Sysno::clock_adjtime, delta as i64) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_clock_at_next_timer(value: u64) {
|
||||||
|
unsafe { syscall_no_branches(Sysno::timer_gettime, value as i64) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_syscall() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::clock_gettime, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn immediate_exit() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::exit, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sched_precise_and_raise() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::fanotify_init, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sched_imprecise_and_raise() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::fanotify_mark, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sched_precise_and_inject() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::msgctl, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sched_imprecise_and_inject() {
|
||||||
|
unsafe { syscall_no_branches(Sysno::msgget, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpuid() {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
unsafe {
|
||||||
|
core::arch::x86_64::__cpuid(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rdtsc() {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
unsafe {
|
||||||
|
core::arch::x86_64::_rdtsc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rdtscp() {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
unsafe {
|
||||||
|
let mut x = 0u32;
|
||||||
|
core::arch::x86_64::__rdtscp(&mut x as *mut _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ts_check_fn(rcbs: u64, f: impl FnOnce()) -> GlobalState {
|
||||||
|
use reverie_ptrace::testing::check_fn_with_config;
|
||||||
|
check_fn_with_config::<LocalState, _>(
|
||||||
|
f,
|
||||||
|
Config {
|
||||||
|
timeout_rcbs: rcbs,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const MANY_RCBS: u64 = 10000; // Normal perf signaling
|
||||||
|
const LESS_RCBS: u64 = 15; // Low enough to use artificial signaling
|
||||||
|
|
||||||
#[test_case(MANY_RCBS, sched_precise)]
|
#[test_case(MANY_RCBS, sched_precise)]
|
||||||
#[test_case(MANY_RCBS, sched_imprecise)]
|
#[test_case(MANY_RCBS, sched_imprecise)]
|
||||||
#[test_case(LESS_RCBS, sched_precise)]
|
#[test_case(LESS_RCBS, sched_precise)]
|
||||||
|
@ -641,16 +610,6 @@ mod timer_tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(sanitized), test))]
|
|
||||||
mod clock_tests {
|
|
||||||
use reverie_ptrace::ret_without_perf;
|
|
||||||
use reverie_ptrace::testing::check_fn;
|
|
||||||
use reverie_ptrace::testing::do_branches;
|
|
||||||
use test_case::test_case;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn clock_accuracy() {
|
fn clock_accuracy() {
|
||||||
|
@ -722,14 +681,6 @@ mod clock_tests {
|
||||||
});
|
});
|
||||||
assert_eq!(gs.num_timer_evts.into_inner(), 1);
|
assert_eq!(gs.num_timer_evts.into_inner(), 1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(sanitized), test))]
|
|
||||||
mod general {
|
|
||||||
use reverie_ptrace::ret_without_perf;
|
|
||||||
use reverie_ptrace::testing::check_fn_with_config;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic() {
|
fn basic() {
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// FIXME: aarch64 doesn't have a `vfork` syscall. Instead, it uses the `clone`
|
||||||
|
// syscall. This test should work with both methods of doing a `vfork`.
|
||||||
|
#![cfg(target_arch = "x86_64")]
|
||||||
|
|
||||||
// signal handling related tests.
|
// signal handling related tests.
|
||||||
|
|
||||||
use reverie::syscalls::Syscall;
|
use reverie::syscalls::Syscall;
|
||||||
|
|
Loading…
Reference in a new issue