ARM64 backend, part 9 / 11: wasmtime support.
This commit adds a few odds and ends required to build wasmtime on ARM64 with the new backend. In particular, it adds: - Support for the `Arm64Call` relocation type. - Support for fetching the trap PC when a signal is received. - A hook for `SIGTRAP`, which is sent by the `brk` opcode (in contrast to x86's `SIGILL`). With the patch sequence up to and including this patch applied, `wasmtime` can now compile and successfully execute code on arm64. Not all tests pass yet, but basic Wasm/WASI tests work correctly.
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::Compilation;
|
use crate::Compilation;
|
||||||
use cranelift_codegen::binemit::Reloc;
|
use cranelift_codegen::binemit::Reloc;
|
||||||
use std::ptr::write_unaligned;
|
use std::ptr::{read_unaligned, write_unaligned};
|
||||||
use wasmtime_environ::{Module, Relocation, RelocationTarget};
|
use wasmtime_environ::{Module, Relocation, RelocationTarget};
|
||||||
use wasmtime_runtime::libcalls;
|
use wasmtime_runtime::libcalls;
|
||||||
use wasmtime_runtime::VMFunctionBody;
|
use wasmtime_runtime::VMFunctionBody;
|
||||||
@@ -101,6 +101,23 @@ fn apply_reloc(
|
|||||||
Reloc::X86PCRelRodata4 => {
|
Reloc::X86PCRelRodata4 => {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
Reloc::Arm64Call => unsafe {
|
||||||
|
let reloc_address = body.add(r.offset as usize) as usize;
|
||||||
|
let reloc_addend = r.addend as isize;
|
||||||
|
let reloc_delta = (target_func_address as u64).wrapping_sub(reloc_address as u64);
|
||||||
|
// TODO: come up with a PLT-like solution for longer calls. We can't extend the
|
||||||
|
// code segment at this point, but we could conservatively allocate space at the
|
||||||
|
// end of the function during codegen, a fixed amount per call, to allow for
|
||||||
|
// potential branch islands.
|
||||||
|
assert!((reloc_delta as i64) < (1 << 27));
|
||||||
|
assert!((reloc_delta as i64) >= -(1 << 27));
|
||||||
|
let reloc_delta = reloc_delta as u32;
|
||||||
|
let reloc_delta = reloc_delta.wrapping_add(reloc_addend as u32);
|
||||||
|
let delta_bits = reloc_delta >> 2;
|
||||||
|
let insn = read_unaligned(reloc_address as *const u32);
|
||||||
|
let new_insn = (insn & 0xfc00_0000) | (delta_bits & 0x03ff_ffff);
|
||||||
|
write_unaligned(reloc_address as *mut u32, new_insn);
|
||||||
|
},
|
||||||
_ => panic!("unsupported reloc kind"),
|
_ => panic!("unsupported reloc kind"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,13 +125,10 @@ fn apply_reloc(
|
|||||||
// A declaration for the stack probe function in Rust's standard library, for
|
// A declaration for the stack probe function in Rust's standard library, for
|
||||||
// catching callstack overflow.
|
// catching callstack overflow.
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(any(
|
if #[cfg(all(
|
||||||
target_arch="aarch64",
|
|
||||||
all(
|
|
||||||
target_os = "windows",
|
target_os = "windows",
|
||||||
target_env = "msvc",
|
target_env = "msvc",
|
||||||
target_pointer_width = "64"
|
target_pointer_width = "64"
|
||||||
)
|
|
||||||
))] {
|
))] {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn __chkstk();
|
pub fn __chkstk();
|
||||||
@@ -132,6 +146,8 @@ cfg_if::cfg_if! {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn __rust_probestack();
|
pub fn __rust_probestack();
|
||||||
}
|
}
|
||||||
static PROBESTACK: unsafe extern "C" fn() = __rust_probestack;
|
static PROBESTACK: unsafe extern "C" fn() = empty_probestack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn empty_probestack() {}
|
||||||
|
|||||||
@@ -26,3 +26,12 @@ void* GetPcFromUContext(ucontext_t *cx) {
|
|||||||
return (void*) cx->uc_mcontext->__ss.__rip;
|
return (void*) cx->uc_mcontext->__ss.__rip;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__) && defined(__aarch64__)
|
||||||
|
#include <sys/ucontext.h>
|
||||||
|
|
||||||
|
void* GetPcFromUContext(ucontext_t *cx) {
|
||||||
|
return (void*) cx->uc_mcontext.pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __linux__ && __aarch64__
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ cfg_if::cfg_if! {
|
|||||||
static mut PREV_SIGBUS: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
static mut PREV_SIGBUS: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
||||||
static mut PREV_SIGILL: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
static mut PREV_SIGILL: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
||||||
static mut PREV_SIGFPE: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
static mut PREV_SIGFPE: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
||||||
|
static mut PREV_SIGTRAP: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
||||||
|
|
||||||
unsafe fn platform_init() {
|
unsafe fn platform_init() {
|
||||||
let register = |slot: &mut MaybeUninit<libc::sigaction>, signal: i32| {
|
let register = |slot: &mut MaybeUninit<libc::sigaction>, signal: i32| {
|
||||||
@@ -70,6 +71,9 @@ cfg_if::cfg_if! {
|
|||||||
register(&mut PREV_SIGFPE, libc::SIGFPE);
|
register(&mut PREV_SIGFPE, libc::SIGFPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on ARM64, we use `brk` to report traps, which generates SIGTRAP.
|
||||||
|
register(&mut PREV_SIGTRAP, libc::SIGTRAP);
|
||||||
|
|
||||||
// On ARM, handle Unaligned Accesses.
|
// On ARM, handle Unaligned Accesses.
|
||||||
// On Darwin, guard page accesses are raised as SIGBUS.
|
// On Darwin, guard page accesses are raised as SIGBUS.
|
||||||
if cfg!(target_arch = "arm") || cfg!(target_os = "macos") {
|
if cfg!(target_arch = "arm") || cfg!(target_os = "macos") {
|
||||||
@@ -87,6 +91,7 @@ cfg_if::cfg_if! {
|
|||||||
libc::SIGBUS => &PREV_SIGBUS,
|
libc::SIGBUS => &PREV_SIGBUS,
|
||||||
libc::SIGFPE => &PREV_SIGFPE,
|
libc::SIGFPE => &PREV_SIGFPE,
|
||||||
libc::SIGILL => &PREV_SIGILL,
|
libc::SIGILL => &PREV_SIGILL,
|
||||||
|
libc::SIGTRAP => &PREV_SIGTRAP,
|
||||||
_ => panic!("unknown signal: {}", signum),
|
_ => panic!("unknown signal: {}", signum),
|
||||||
};
|
};
|
||||||
let handled = tls::with(|info| {
|
let handled = tls::with(|info| {
|
||||||
@@ -158,6 +163,12 @@ cfg_if::cfg_if! {
|
|||||||
if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] {
|
if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] {
|
||||||
let cx = &*(cx as *const libc::ucontext_t);
|
let cx = &*(cx as *const libc::ucontext_t);
|
||||||
cx.uc_mcontext.gregs[libc::REG_RIP as usize] as *const u8
|
cx.uc_mcontext.gregs[libc::REG_RIP as usize] as *const u8
|
||||||
|
} else if #[cfg(all(target_os = "linux", target_arch = "aarch64"))] {
|
||||||
|
// libc doesn't seem to support Linux/aarch64 at the moment?
|
||||||
|
extern "C" {
|
||||||
|
fn GetPcFromUContext(cx: *mut libc::c_void) -> *const u8;
|
||||||
|
}
|
||||||
|
GetPcFromUContext(cx)
|
||||||
} else if #[cfg(target_os = "macos")] {
|
} else if #[cfg(target_os = "macos")] {
|
||||||
// FIXME(rust-lang/libc#1702) - once that lands and is
|
// FIXME(rust-lang/libc#1702) - once that lands and is
|
||||||
// released we should inline the definition here
|
// released we should inline the definition here
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ mod tests {
|
|||||||
.downcast::<Trap>()?;
|
.downcast::<Trap>()?;
|
||||||
assert!(
|
assert!(
|
||||||
trap.message()
|
trap.message()
|
||||||
.starts_with("wasm trap: out of bounds memory access"),
|
.starts_with("wasm trap: out of bounds"),
|
||||||
"bad trap message: {:?}",
|
"bad trap message: {:?}",
|
||||||
trap.message()
|
trap.message()
|
||||||
);
|
);
|
||||||
@@ -149,7 +149,7 @@ mod tests {
|
|||||||
.downcast::<Trap>()?;
|
.downcast::<Trap>()?;
|
||||||
assert!(trap
|
assert!(trap
|
||||||
.message()
|
.message()
|
||||||
.starts_with("wasm trap: out of bounds memory access"));
|
.starts_with("wasm trap: out of bounds"));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user