From ada1e3c5426f33d52fcdd8be8d85c5e33185eafc Mon Sep 17 00:00:00 2001 From: Jason White Date: Thu, 27 Oct 2022 10:56:09 -0700 Subject: [PATCH] Get tests building under aarch64 Reviewed By: VladimirMakaev Differential Revision: D40701839 fbshipit-source-id: e60053c7d21696e7f7dd120420b896dee3d65ba7 --- reverie-examples/chunky_print.rs | 19 +- reverie-examples/pedigree.rs | 36 ++-- reverie-syscalls/src/syscalls/family.rs | 14 +- reverie-syscalls/src/syscalls/mod.rs | 8 +- tests/basics.rs | 29 +-- tests/busywait.rs | 27 ++- tests/c_tests/getpid-pie.c | 9 + tests/convert.rs | 2 + tests/cpuid.rs | 3 + tests/rdtsc.rs | 3 + tests/signalfd.rs | 1 + tests/stat.rs | 32 +--- tests/timer_semantics.rs | 233 ++++++++++-------------- tests/vfork.rs | 4 + 14 files changed, 186 insertions(+), 234 deletions(-) diff --git a/reverie-examples/chunky_print.rs b/reverie-examples/chunky_print.rs index 3ef9067..e7b870d 100644 --- a/reverie-examples/chunky_print.rs +++ b/reverie-examples/chunky_print.rs @@ -196,13 +196,22 @@ impl Tool for ChunkyPrintLocal { let _ = guest.send_rpc(Msg::Tick).await; match call { // Here we make some attempt to catch redirections: + // FIXME: De-dup the dup + #[cfg(target_arch = "x86_64")] Syscall::Dup2(d) => { - let newfd = d.newfd(); - if newfd == 1 { - self.stdout_disconnected.store(true, Ordering::SeqCst); + match d.newfd() { + 1 => 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 diff --git a/reverie-examples/pedigree.rs b/reverie-examples/pedigree.rs index e410888..970f925 100644 --- a/reverie-examples/pedigree.rs +++ b/reverie-examples/pedigree.rs @@ -56,21 +56,10 @@ impl Tool for PedigreeLocal { syscall: Syscall, ) -> Result { match syscall { - Syscall::Fork(_) | Syscall::Vfork(_) | Syscall::Clone(_) => { - 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) - } - Syscall::Getpid(_) - | Syscall::Getppid(_) - | Syscall::Gettid(_) - | Syscall::Getpgid(_) - | Syscall::Getpgrp(_) => { + #[cfg(target_arch = "x86_64")] + Syscall::Fork(_) | Syscall::Vfork(_) => self.handle_fork(syscall, guest).await, + Syscall::Clone(_) => self.handle_fork(syscall, guest).await, + Syscall::Getpid(_) | Syscall::Getppid(_) | Syscall::Gettid(_) | Syscall::Getpgid(_) => { let pid = guest.inject(syscall).await?; let vpid = nix::unistd::Pid::try_from(&self.0).unwrap(); 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, + ) -> Result { + 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] async fn main() -> Result<(), Error> { let args = CommonToolArguments::from_args(); diff --git a/reverie-syscalls/src/syscalls/family.rs b/reverie-syscalls/src/syscalls/family.rs index 5c55d53..5559191 100644 --- a/reverie-syscalls/src/syscalls/family.rs +++ b/reverie-syscalls/src/syscalls/family.rs @@ -61,39 +61,43 @@ impl From for Syscall { /// Represents the stat family of syscalls. All of these have an associated stat /// buffer. // Stat not available in aarch64 -#[cfg(not(target_arch = "aarch64"))] #[derive(From, Debug, Copy, Clone, Eq, PartialEq)] #[allow(missing_docs)] pub enum StatFamily { + #[cfg(not(target_arch = "aarch64"))] Stat(super::Stat), Fstat(super::Fstat), + #[cfg(not(target_arch = "aarch64"))] Lstat(super::Lstat), + #[cfg(not(target_arch = "aarch64"))] Newfstatat(super::Newfstatat), } -// Stat not available in aarch64 -#[cfg(not(target_arch = "aarch64"))] impl StatFamily { /// Get address of the stat buffer. Returns `None` if a NULL pointer was /// specified. pub fn stat(&self) -> Option { match self { + #[cfg(not(target_arch = "aarch64"))] Self::Stat(s) => s.stat(), Self::Fstat(s) => s.stat(), + #[cfg(not(target_arch = "aarch64"))] Self::Lstat(s) => s.stat(), + #[cfg(not(target_arch = "aarch64"))] Self::Newfstatat(s) => s.stat(), } } } -// Stat not available in aarch64 -#[cfg(not(target_arch = "aarch64"))] impl From for Syscall { fn from(family: StatFamily) -> Syscall { match family { + #[cfg(not(target_arch = "aarch64"))] StatFamily::Stat(syscall) => Syscall::Stat(syscall), StatFamily::Fstat(syscall) => Syscall::Fstat(syscall), + #[cfg(not(target_arch = "aarch64"))] StatFamily::Lstat(syscall) => Syscall::Lstat(syscall), + #[cfg(not(target_arch = "aarch64"))] StatFamily::Newfstatat(syscall) => Syscall::Newfstatat(syscall), } } diff --git a/reverie-syscalls/src/syscalls/mod.rs b/reverie-syscalls/src/syscalls/mod.rs index ddbf4a9..b6c4ca0 100644 --- a/reverie-syscalls/src/syscalls/mod.rs +++ b/reverie-syscalls/src/syscalls/mod.rs @@ -79,7 +79,6 @@ syscall_list! { close => Close, #[cfg(not(target_arch = "aarch64"))] stat => Stat, - #[cfg(not(target_arch = "aarch64"))] fstat => Fstat, #[cfg(not(target_arch = "aarch64"))] lstat => Lstat, @@ -392,6 +391,7 @@ syscall_list! { fchownat => Fchownat, #[cfg(not(target_arch = "aarch64"))] futimesat => Futimesat, + // TODO: Rename this to Fstatat. #[cfg(not(target_arch = "aarch64"))] newfstatat => Newfstatat, unlinkat => Unlinkat, @@ -573,8 +573,6 @@ typed_syscall! { } } -// Fstat not available in aarch64 -#[cfg(not(target_arch = "aarch64"))] typed_syscall! { pub struct Fstat { fd: i32, @@ -2671,7 +2669,8 @@ typed_syscall! { flags: AtFlags, } } -// Stat not available in aarch64 + +// Newfstatat not available in aarch64 #[cfg(not(target_arch = "aarch64"))] impl From for Newfstatat { fn from(stat: Stat) -> Self { @@ -2704,6 +2703,7 @@ typed_syscall! { flags: AtFlags, } } + // Unlink not available in aarch64 #[cfg(not(target_arch = "aarch64"))] impl From for Unlinkat { diff --git a/tests/basics.rs b/tests/basics.rs index 25a77c4..a2a3327 100644 --- a/tests/basics.rs +++ b/tests/basics.rs @@ -226,8 +226,8 @@ fn i_should_segfault() { use reverie_ptrace::testing::test_fn; let (output, _) = test_fn::(|| { unsafe { - let invalid_pointer = 0x5u64 as *mut u64; - std::ptr::write(invalid_pointer, 0xdeadbeefu64); + let invalid_ptr = 0x5u64 as *mut u64; + invalid_ptr.write(0xdeadbeefu64); }; }) .unwrap(); @@ -240,26 +240,15 @@ fn i_should_segfault_2() { use nix::sys::signal::Signal::SIGSEGV; use reverie_ptrace::testing::test_fn; - #[inline] - #[cfg(not(feature = "llvm_asm"))] - unsafe fn do_segfault() { - let null_ptr: *const usize = core::ptr::null(); - core::arch::asm!( - "jmp {0}", - in(reg) null_ptr, - ) + pub fn do_segfault() { + let invalid_ptr = 0x1234 as *const usize; + let result = unsafe { invalid_ptr.read() }; + // Print so the above doesn't get optimized out. We will never get here + // because the above segfaults. + println!("{}", result); } - #[inline] - #[cfg(feature = "llvm_asm")] - #[allow(deprecated)] - unsafe fn do_segfault() { - llvm_asm!(r#"mov $$0, %rax - jmpq *%rax - "#:::"rax") - } - - let (output, _) = test_fn::(|| unsafe { do_segfault() }).unwrap(); + let (output, _) = test_fn::(|| do_segfault()).unwrap(); assert_eq!(output.status, ExitStatus::Signaled(SIGSEGV, true),); } diff --git a/tests/busywait.rs b/tests/busywait.rs index 9ffe622..c4a91dd 100644 --- a/tests/busywait.rs +++ b/tests/busywait.rs @@ -17,7 +17,6 @@ use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicU64; use std::sync::atomic::Ordering; -use raw_cpuid::cpuid; use reverie::syscalls::Syscall; use reverie::CpuIdResult; use reverie::Errno; @@ -27,8 +26,6 @@ use reverie::GlobalRPC; use reverie::GlobalTool; use reverie::Guest; use reverie::Pid; -use reverie::Rdtsc; -use reverie::RdtscResult; use reverie::Signal; use reverie::Tid; use reverie::TimerSchedule; @@ -124,6 +121,7 @@ impl Tool for LocalState { guest.tail_inject(syscall).await } + #[cfg(target_arch = "x86_64")] async fn handle_cpuid_event>( &self, guest: &mut T, @@ -131,16 +129,17 @@ impl Tool for LocalState { ecx: u32, ) -> Result { 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>( &self, guest: &mut T, - request: Rdtsc, - ) -> Result { + request: reverie::Rdtsc, + ) -> Result { guest.send_rpc(IncrMsg::Increment).await; - Ok(RdtscResult::new(request)) + Ok(reverie::RdtscResult::new(request)) } async fn handle_signal_event>( @@ -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))] mod tests { use std::time::Duration; @@ -196,6 +188,13 @@ mod tests { 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] fn guest_busywait_no_timer() { let start = Instant::now(); diff --git a/tests/c_tests/getpid-pie.c b/tests/c_tests/getpid-pie.c index 67147fd..0866b22 100644 --- a/tests/c_tests/getpid-pie.c +++ b/tests/c_tests/getpid-pie.c @@ -21,6 +21,7 @@ * 748: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1) * 74f: 00 */ +#if defined(__x86_64__) __attribute__((noinline)) static int sys_getpid(void) { int ret; asm volatile( @@ -29,6 +30,14 @@ __attribute__((noinline)) static int sys_getpid(void) { : "=r"(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 pid0 = getpid(); diff --git a/tests/convert.rs b/tests/convert.rs index 070d586..114c4ff 100644 --- a/tests/convert.rs +++ b/tests/convert.rs @@ -7,6 +7,8 @@ */ #![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 // args should not be clobbered, even with the conversion. diff --git a/tests/cpuid.rs b/tests/cpuid.rs index ea53a4c..62f1b5d 100644 --- a/tests/cpuid.rs +++ b/tests/cpuid.rs @@ -8,6 +8,9 @@ //! Tests cpuid interception +// cpuid interception is only available on x86_64 +#![cfg(target_arch = "x86_64")] + use raw_cpuid::CpuIdResult; use reverie::Errno; use reverie::GlobalTool; diff --git a/tests/rdtsc.rs b/tests/rdtsc.rs index 66e9c44..c5d3af6 100644 --- a/tests/rdtsc.rs +++ b/tests/rdtsc.rs @@ -6,6 +6,9 @@ * 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::Ordering; diff --git a/tests/signalfd.rs b/tests/signalfd.rs index bc3f99b..111ea80 100644 --- a/tests/signalfd.rs +++ b/tests/signalfd.rs @@ -35,6 +35,7 @@ impl Tool for LocalState { let exit_failure = ExitGroup::new().with_status(1); match syscall { // glibc should wrap signalfd -> signalfd4(2). + #[cfg(target_arch = "x86_64")] Syscall::Signalfd(_) => guest.tail_inject(exit_failure).await, Syscall::Signalfd4(_) => { let (_, args) = syscall.into_parts(); diff --git a/tests/stat.rs b/tests/stat.rs index 28f5c7f..def85c8 100644 --- a/tests/stat.rs +++ b/tests/stat.rs @@ -8,11 +8,6 @@ // 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 serde::Deserialize; use serde::Serialize; @@ -20,31 +15,8 @@ use serde::Serialize; #[derive(Debug, Serialize, Deserialize, Default, Clone)] struct LocalState; -async fn handle_newfstatat>( - guest: &mut T, - call: syscalls::Newfstatat, -) -> Result { - let res = guest.inject(call).await; - - println!("{} = {:?}", call.display(&guest.memory()), res); - - Ok(res?) -} - #[reverie::tool] -impl Tool for LocalState { - async fn handle_syscall_event>( - &self, - guest: &mut T, - syscall: Syscall, - ) -> Result { - 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, - } - } -} +impl Tool for LocalState {} #[cfg(all(not(sanitized), test))] mod tests { @@ -79,7 +51,7 @@ mod tests { // glibc doesn't provide wrapper for statx unsafe fn statx( dirfd: i32, - path: *const i8, + path: *const libc::c_char, flags: i32, mask: u32, statxbuf: *mut libc::statx, diff --git a/tests/timer_semantics.rs b/tests/timer_semantics.rs index 6363ea0..e677825 100644 --- a/tests/timer_semantics.rs +++ b/tests/timer_semantics.rs @@ -11,10 +11,6 @@ //! 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. -#![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::Ordering; @@ -202,126 +198,8 @@ async fn raise_sigwinch>(guest: &mut T) -> Tgkill { .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::( - 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))] -mod timer_tests { +mod tests { //! These tests are highly sensitive to the number of branches executed //! 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 @@ -330,12 +208,103 @@ mod timer_tests { //! tests. use reverie_ptrace::ret_without_perf; + use reverie_ptrace::testing::check_fn; use reverie_ptrace::testing::check_fn_with_config; use reverie_ptrace::testing::do_branches; use test_case::test_case; 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::( + 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_imprecise)] #[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] fn clock_accuracy() { @@ -722,14 +681,6 @@ mod clock_tests { }); 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] fn basic() { diff --git a/tests/vfork.rs b/tests/vfork.rs index d2c52b7..35f1ba2 100644 --- a/tests/vfork.rs +++ b/tests/vfork.rs @@ -6,6 +6,10 @@ * 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. use reverie::syscalls::Syscall;