[traphandler] Add arm64 simulator support on Windows

This ports the trap handler implementation for the arm64 simulator
from POSIX to Windows. Apart from different registers being used
for passing parameters, and different access to these register
values in the signal handler, the implementation is exactly the same.

The new logic is being used for sanitizer builds which automatically
target arm64 via the simulator, or if manually compiling an arm64
simulator build on x64. I manually tested the latter.

Also, the existing unit test is enabled for Mac (which was missing)
and Windows now.

R=ahaas@chromium.org, mseaborn@chromium.org

Bug: v8:11955
Cq-Include-Trybots: luci.v8.try:v8_win64_asan_rel_ng
Cq-Include-Trybots: luci.v8.try:v8_mac64_asan_rel_ng
Change-Id: Ia62405b28808a3cc9f199e3f43a45ffc4bda491b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3163256
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76902}
This commit is contained in:
Clemens Backes 2021-09-17 13:05:18 +02:00 committed by V8 LUCI CQ
parent 62133215bc
commit 367f86de7e
6 changed files with 71 additions and 36 deletions

View File

@ -3487,7 +3487,8 @@ v8_header_set("v8_internal_headers") {
(current_cpu == "x64" && (is_linux || is_chromeos || is_mac))) {
sources += [ "src/trap-handler/handler-inside-posix.h" ]
}
if (current_cpu == "x64" && (is_linux || is_chromeos || is_mac)) {
if (current_cpu == "x64" &&
(is_linux || is_chromeos || is_mac || is_win)) {
sources += [ "src/trap-handler/trap-handler-simulator.h" ]
}
}
@ -4411,16 +4412,22 @@ v8_source_set("v8_base_without_compiler") {
"src/regexp/arm64/regexp-macro-assembler-arm64.cc",
]
if (v8_enable_webassembly) {
# Trap handling is enabled on arm64 Mac and in simulators on x64 on Linux
# and Mac.
# Trap handling is enabled on arm64 Mac and in simulators on x64 on Linux,
# Mac, and Windows.
if ((current_cpu == "arm64" && is_mac) ||
(current_cpu == "x64" && (is_linux || is_chromeos || is_mac))) {
sources += [
"src/trap-handler/handler-inside-posix.cc",
"src/trap-handler/handler-outside-posix.cc",
]
} else if (current_cpu == "x64" && is_win) {
sources += [
"src/trap-handler/handler-inside-win.cc",
"src/trap-handler/handler-outside-win.cc",
]
}
if (current_cpu == "x64" && (is_linux || is_chromeos || is_mac)) {
if (current_cpu == "x64" &&
(is_linux || is_chromeos || is_mac || is_win)) {
sources += [ "src/trap-handler/handler-outside-simulator.cc" ]
}
}

View File

@ -88,7 +88,7 @@ class UnmaskOobSignalScope {
#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR
// This is the address where we continue on a failed "ProbeMemory". It's defined
// in "handler-outside-simulators.cc".
// in "handler-outside-simulator.cc".
extern "C" char v8_probe_memory_continuation[];
#endif // V8_TRAP_HANDLER_VIA_SIMULATOR

View File

@ -30,6 +30,10 @@
#include "src/trap-handler/trap-handler-internal.h"
#include "src/trap-handler/trap-handler.h"
#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR
#include "src/trap-handler/trap-handler-simulator.h"
#endif
namespace v8 {
namespace internal {
namespace trap_handler {
@ -49,6 +53,12 @@ struct TEB {
PVOID thread_local_storage_pointer;
};
#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR
// This is the address where we continue on a failed "ProbeMemory". It's defined
// in "handler-outside-simulator.cc".
extern "C" char v8_probe_memory_continuation[];
#endif // V8_TRAP_HANDLER_VIA_SIMULATOR
bool TryHandleWasmTrap(EXCEPTION_POINTERS* exception) {
// VectoredExceptionHandlers need extreme caution. Do as little as possible
// to determine if the exception should be handled or not. Exceptions can be
@ -71,17 +81,16 @@ bool TryHandleWasmTrap(EXCEPTION_POINTERS* exception) {
// need to run to initialize values may not have run yet, but that is not
// the case for any thread_locals used here).
TEB* pteb = reinterpret_cast<TEB*>(NtCurrentTeb());
if (!pteb->thread_local_storage_pointer) {
return false;
}
if (!pteb->thread_local_storage_pointer) return false;
// Now safe to run more advanced logic, which may access thread_locals
// Ensure the faulting thread was actually running Wasm code.
if (!IsThreadInWasm()) {
return false;
}
if (!IsThreadInWasm()) return false;
// Clear g_thread_in_wasm_code, primarily to protect against nested faults.
// The only path that resets the flag to true is if we find a landing pad (in
// which case this function returns true). Otherwise we leave the flag unset
// since we do not return to wasm code.
g_thread_in_wasm_code = false;
const EXCEPTION_RECORD* record = exception->ExceptionRecord;
@ -89,17 +98,28 @@ bool TryHandleWasmTrap(EXCEPTION_POINTERS* exception) {
uintptr_t fault_addr = reinterpret_cast<uintptr_t>(record->ExceptionAddress);
uintptr_t landing_pad = 0;
if (TryFindLandingPad(fault_addr, &landing_pad)) {
#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR
// Only handle signals triggered by the load in {ProbeMemory}.
if (fault_addr != reinterpret_cast<uintptr_t>(&ProbeMemory)) return false;
// The simulated ip will be in the second parameter register (%rdx).
uintptr_t simulated_ip = exception->ContextRecord->Rdx;
if (!TryFindLandingPad(simulated_ip, &landing_pad)) return false;
TH_DCHECK(landing_pad != 0);
exception->ContextRecord->Rax = landing_pad;
// Continue at the memory probing continuation.
exception->ContextRecord->Rip =
reinterpret_cast<uintptr_t>(&v8_probe_memory_continuation);
#else
if (!TryFindLandingPad(fault_addr, &landing_pad)) return false;
// Tell the caller to return to the landing pad.
exception->ContextRecord->Rip = landing_pad;
#endif
// We will return to wasm code, so restore the g_thread_in_wasm_code flag.
g_thread_in_wasm_code = true;
return true;
}
// If we get here, it's not a recoverable wasm fault, so we go to the next
// handler. Leave the g_thread_in_wasm_code flag unset since we do not return
// to wasm code.
return false;
}
LONG HandleWasmTrap(EXCEPTION_POINTERS* exception) {

View File

@ -15,10 +15,14 @@
asm(
".globl " SYMBOL(ProbeMemory) " \n"
SYMBOL(ProbeMemory) ": \n"
// First parameter (address) passed in %rdi.
// The second parameter (pc) is unused here. It is read by the trap handler
// instead.
// First parameter (address) passed in %rdi on Linux/Mac, and %rcx on Windows.
// The second parameter (pc) is unused here. It is read by the trap handler
// instead.
#if V8_OS_WIN
" movb (%rcx), %al \n"
#else
" movb (%rdi), %al \n"
#endif // V8_OS_WIN
// Return 0 on success.
" xorl %eax, %eax \n"
// Place an additional "ret" here instead of falling through to the one

View File

@ -25,8 +25,9 @@ namespace trap_handler {
// Arm64 (non-simulator) on Mac.
#elif V8_TARGET_ARCH_ARM64 && V8_HOST_ARCH_ARM64 && V8_OS_MACOSX
#define V8_TRAP_HANDLER_SUPPORTED true
// Arm64 simulator on x64 on Linux or Mac.
#elif V8_TARGET_ARCH_ARM64 && V8_HOST_ARCH_X64 && (V8_OS_LINUX || V8_OS_MACOSX)
// Arm64 simulator on x64 on Linux, Mac, or Windows.
#elif V8_TARGET_ARCH_ARM64 && V8_HOST_ARCH_X64 && \
(V8_OS_LINUX || V8_OS_MACOSX || V8_OS_WIN)
#define V8_TRAP_HANDLER_VIA_SIMULATOR
#define V8_TRAP_HANDLER_SUPPORTED true
// Everything else is unsupported.

View File

@ -493,19 +493,22 @@ v8_source_set("unittests_sources") {
]
}
if (is_posix && v8_enable_webassembly) {
if (v8_enable_webassembly) {
if (is_posix) {
sources += [ "wasm/trap-handler-posix-unittest.cc" ]
}
if (is_win && v8_enable_webassembly) {
if (is_win) {
sources += [ "wasm/trap-handler-win-unittest.cc" ]
}
# Include this test only on arm64 simulator builds on x64 on Linux.
if (current_cpu == "x64" && v8_current_cpu == "arm64" && is_linux &&
v8_enable_webassembly) {
# Include this test only on arm64 simulator builds on x64 on Linux, Mac and
# Windows.
if (current_cpu == "x64" && v8_current_cpu == "arm64" &&
(is_linux || is_mac || is_win)) {
sources += [ "wasm/trap-handler-simulator-unittest.cc" ]
}
}
configs = [
"../..:cppgc_base_config",