mirror of
https://github.com/facebookexperimental/reverie.git
synced 2025-01-23 13:10:04 +00:00
Convert llvm_asm to asm
Summary: The `llvm_asm` nightly feature was removed in Rust 1.59 and `asm!()` was simultaneously stabilized, so we need to make the switch so things continue compiling. This retains the `llvm_asm` code so that it can continue compiling on old llvm versions. This will be removed when that is updated. Reviewed By: johnhurt Differential Revision: D34561246 fbshipit-source-id: 42cd2c7e83cea9a5c21bd5871463bb1a42be139d
This commit is contained in:
parent
75e875fe28
commit
193965dfe5
6 changed files with 143 additions and 15 deletions
|
@ -36,7 +36,7 @@
|
||||||
#![feature(async_closure)]
|
#![feature(async_closure)]
|
||||||
#![feature(internal_output_capture)]
|
#![feature(internal_output_capture)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![feature(llvm_asm)]
|
#![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
|
||||||
#![feature(map_first_last)]
|
#![feature(map_first_last)]
|
||||||
#![feature(bench_black_box)]
|
#![feature(bench_black_box)]
|
||||||
|
|
||||||
|
|
|
@ -569,10 +569,30 @@ macro_rules! ret_without_perf {
|
||||||
/// Perform exactly `count+1` conditional branch instructions. Useful for
|
/// Perform exactly `count+1` conditional branch instructions. Useful for
|
||||||
/// testing timer-related code.
|
/// testing timer-related code.
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[cfg(not(feature = "llvm_asm"))]
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn do_branches(mut count: u64) {
|
||||||
|
// Anything but assembly is unreliable between debug and release
|
||||||
|
unsafe {
|
||||||
|
// Loop until carry flag is set, indicating underflow
|
||||||
|
core::arch::asm!(
|
||||||
|
"2:",
|
||||||
|
"sub {0}, 1",
|
||||||
|
"jnc 2b",
|
||||||
|
inout(reg) count,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(count, u64::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform exactly `count+1` conditional branch instructions. Useful for
|
||||||
|
/// testing timer-related code.
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[cfg(feature = "llvm_asm")]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn do_branches(count: u64) {
|
pub fn do_branches(count: u64) {
|
||||||
// Anything but assembly is unreliable between debug and release
|
// Anything but assembly is unreliable between debug and release
|
||||||
// TODO: Switch to `asm!()` when our LLVM version supports it.
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
unsafe {
|
unsafe {
|
||||||
// Loop until carry flag is set, indicating underflow
|
// Loop until carry flag is set, indicating underflow
|
||||||
|
|
|
@ -13,7 +13,6 @@ use core::mem;
|
||||||
use perf_event_open_sys::bindings as perf;
|
use perf_event_open_sys::bindings as perf;
|
||||||
use raw_cpuid::{CpuId, FeatureInfo};
|
use raw_cpuid::{CpuId, FeatureInfo};
|
||||||
use reverie::Errno;
|
use reverie::Errno;
|
||||||
use std::hint::black_box;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
|
@ -68,6 +67,7 @@ pub(crate) enum PmuValidationError {
|
||||||
#[error("Intel Kvm-In-Txcp bug found")]
|
#[error("Intel Kvm-In-Txcp bug found")]
|
||||||
IntelKvmInTxcpBugDetected,
|
IntelKvmInTxcpBugDetected,
|
||||||
|
|
||||||
|
#[cfg(feature = "llvm_asm")]
|
||||||
#[error("Overcount triggered by PMU interrupts detected due to Xen PMU virtualization bug")]
|
#[error("Overcount triggered by PMU interrupts detected due to Xen PMU virtualization bug")]
|
||||||
IntelXenPmiBugDetected,
|
IntelXenPmiBugDetected,
|
||||||
}
|
}
|
||||||
|
@ -361,7 +361,7 @@ fn is_amd_zen(cpu_feature: FeatureInfo) -> bool {
|
||||||
|
|
||||||
/// This is a transcription of the function with the same name in Mozilla-RR it will
|
/// This is a transcription of the function with the same name in Mozilla-RR it will
|
||||||
/// check for bugs specific to cpu architectures
|
/// check for bugs specific to cpu architectures
|
||||||
fn check_for_arch_bugs(precise_ip: bool) -> Result<(), PmuValidationError> {
|
fn check_for_arch_bugs(_precise_ip: bool) -> Result<(), PmuValidationError> {
|
||||||
let c = CpuId::new();
|
let c = CpuId::new();
|
||||||
let vendor = c.get_vendor_info().unwrap();
|
let vendor = c.get_vendor_info().unwrap();
|
||||||
let feature_info = c
|
let feature_info = c
|
||||||
|
@ -371,7 +371,12 @@ fn check_for_arch_bugs(precise_ip: bool) -> Result<(), PmuValidationError> {
|
||||||
|
|
||||||
match vendor_str {
|
match vendor_str {
|
||||||
AMD_VENDOR if is_amd_zen(feature_info) => check_for_zen_speclockmap(),
|
AMD_VENDOR if is_amd_zen(feature_info) => check_for_zen_speclockmap(),
|
||||||
INTEL_VENDOR => check_for_kvm_in_txcp_bug().and(check_for_xen_pmi_bug(precise_ip)),
|
INTEL_VENDOR => {
|
||||||
|
check_for_kvm_in_txcp_bug()?;
|
||||||
|
#[cfg(feature = "llvm_asm")]
|
||||||
|
check_for_xen_pmi_bug(_precise_ip)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
s => panic!("Unknown CPU vendor: {}", s),
|
s => panic!("Unknown CPU vendor: {}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,6 +400,18 @@ fn check_for_zen_speclockmap() -> Result<(), PmuValidationError> {
|
||||||
let count = read_counter(&fd)?;
|
let count = read_counter(&fd)?;
|
||||||
|
|
||||||
// A lock add is known to increase the perf counter we're looking at.
|
// A lock add is known to increase the perf counter we're looking at.
|
||||||
|
#[cfg(not(feature = "llvm_asm"))]
|
||||||
|
unsafe {
|
||||||
|
let _prev: usize;
|
||||||
|
core::arch::asm!(
|
||||||
|
"lock",
|
||||||
|
"xadd [{}], {}",
|
||||||
|
in(reg) val,
|
||||||
|
inout(reg) to_add => _prev,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "llvm_asm")]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
unsafe {
|
unsafe {
|
||||||
let _prev: usize;
|
let _prev: usize;
|
||||||
|
@ -437,6 +454,8 @@ fn check_for_kvm_in_txcp_bug() -> Result<(), PmuValidationError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Convert this big block of llvm_asm over to the new asm syntax.
|
||||||
|
#[cfg(feature = "llvm_asm")]
|
||||||
fn check_for_xen_pmi_bug(precise_ip: bool) -> Result<(), PmuValidationError> {
|
fn check_for_xen_pmi_bug(precise_ip: bool) -> Result<(), PmuValidationError> {
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
let mut count: i32 = -1;
|
let mut count: i32 = -1;
|
||||||
|
@ -451,7 +470,7 @@ fn check_for_xen_pmi_bug(precise_ip: bool) -> Result<(), PmuValidationError> {
|
||||||
20764791
|
20764791
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut accumulator = black_box(make_accumulator_seed());
|
let mut accumulator = core::hint::black_box(make_accumulator_seed());
|
||||||
let mut expected_accumulator = accumulator;
|
let mut expected_accumulator = accumulator;
|
||||||
|
|
||||||
// reproduce the assembly here to calculate what the final accumulator value should be
|
// reproduce the assembly here to calculate what the final accumulator value should be
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* This source code is licensed under the BSD-style license found in the
|
* This source code is licensed under the BSD-style license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#![feature(llvm_asm)]
|
#![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
|
||||||
|
|
||||||
//! Basic tests that don't fall into some other category.
|
//! Basic tests that don't fall into some other category.
|
||||||
|
|
||||||
|
@ -221,12 +221,27 @@ fn i_should_segfault() {
|
||||||
fn i_should_segfault_2() {
|
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;
|
||||||
let (output, _) = test_fn::<NoopTool, _>(|| unsafe {
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(not(feature = "llvm_asm"))]
|
||||||
|
unsafe fn do_segfault() {
|
||||||
|
let null_ptr: *const usize = core::ptr::null();
|
||||||
|
asm!(
|
||||||
|
"jmp {0}",
|
||||||
|
in(reg) null_ptr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "llvm_asm")]
|
||||||
|
#[allow(deprecated)]
|
||||||
|
unsafe fn do_segfault() {
|
||||||
llvm_asm!(r#"mov $$0, %rax
|
llvm_asm!(r#"mov $$0, %rax
|
||||||
jmpq *%rax
|
jmpq *%rax
|
||||||
"#:::"rax")
|
"#:::"rax")
|
||||||
})
|
}
|
||||||
.unwrap();
|
|
||||||
|
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),);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* This source code is licensed under the BSD-style license found in the
|
* This source code is licensed under the BSD-style license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#![feature(llvm_asm)]
|
#![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -61,7 +61,64 @@ mod tests {
|
||||||
use reverie_ptrace::testing::check_fn;
|
use reverie_ptrace::testing::check_fn;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[cfg(not(feature = "llvm_asm"))]
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
|
unsafe fn open_syscall_sanity_check() -> i32 {
|
||||||
|
let path = b"/dev/null\0".as_ptr() as usize;
|
||||||
|
let flags: usize = 0x8000; // O_LARGEFILE
|
||||||
|
let mode: usize = 0o644;
|
||||||
|
|
||||||
|
let mut ret: usize;
|
||||||
|
|
||||||
|
// // The following asm block does this:
|
||||||
|
// let ret = open("/dev/null", 0x8000, 0644);
|
||||||
|
// if ret >= -4095 as u64 { exit_group(1) }
|
||||||
|
// // Sanity check input registers to ensure they didn't change.
|
||||||
|
// if %rsi != 0x8000 { exit_group(1) }
|
||||||
|
// if %rdx != 0644 { exit_roup(1) }
|
||||||
|
// return fd
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov r8, {arg1}",
|
||||||
|
"syscall",
|
||||||
|
// if (ret >= -4095 as u64) goto 1
|
||||||
|
"cmp 0xfffffffffffff001"
|
||||||
|
"jae 2f",
|
||||||
|
// if (rax != r8) goto label1;
|
||||||
|
"cmp rdi, r8",
|
||||||
|
"jne 2f",
|
||||||
|
// if (rsi != 0x8000) goto label1;
|
||||||
|
"cmp rsi, 0x8000",
|
||||||
|
"jne 2f",
|
||||||
|
// if (rdx != 0644) goto label1;
|
||||||
|
"cmp rdx, 0x1a4",
|
||||||
|
"jne 2f",
|
||||||
|
// Otherwise, we're successful.
|
||||||
|
"jmp 3f",
|
||||||
|
"2:",
|
||||||
|
// Set syscall arg1 to label1
|
||||||
|
"mov rdi, 0x1",
|
||||||
|
// Set syscall to exit_group
|
||||||
|
"mov rax, {sys_exit_group}",
|
||||||
|
// Do the syscall
|
||||||
|
"syscall",
|
||||||
|
"3:",
|
||||||
|
lateout("rax") ret,
|
||||||
|
in("rax") n,
|
||||||
|
arg1 = inlateout("rdi") path, // Reused for the exit_group syscall.
|
||||||
|
in("rsi") flags,
|
||||||
|
in("rdx") mode,
|
||||||
|
out("r8") _, // Clobbered
|
||||||
|
out("rcx") _, // rcx is used to store old rip
|
||||||
|
out("r11") _, // r11 is used to store old rflags
|
||||||
|
);
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[cfg(feature = "llvm_asm")]
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
#[allow(deprecated)]
|
||||||
unsafe fn open_syscall_sanity_check() -> i32 {
|
unsafe fn open_syscall_sanity_check() -> i32 {
|
||||||
let mut ret;
|
let mut ret;
|
||||||
let path = b"/dev/null\0";
|
let path = b"/dev/null\0";
|
||||||
|
|
|
@ -12,8 +12,7 @@
|
||||||
//! 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.
|
||||||
|
|
||||||
#![feature(llvm_asm)]
|
#![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
|
||||||
|
|
||||||
use core::arch::x86_64::{__cpuid, __rdtscp, _rdtsc};
|
use core::arch::x86_64::{__cpuid, __rdtscp, _rdtsc};
|
||||||
use libc;
|
use libc;
|
||||||
use reverie::{
|
use reverie::{
|
||||||
|
@ -189,7 +188,25 @@ 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(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) {
|
unsafe fn syscall_no_branches(no: libc::c_long, arg1: libc::c_long) {
|
||||||
llvm_asm!("
|
llvm_asm!("
|
||||||
mov $0, %rax
|
mov $0, %rax
|
||||||
|
|
Loading…
Reference in a new issue