|
@@ -0,0 +1,1533 @@
|
|
|
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
+From: Tom Tan <[email protected]>
|
|
|
+Date: Fri, 26 Jul 2019 23:36:52 -0700
|
|
|
+Subject: Unwind V8 frames correctly on Windows ARM64
|
|
|
+
|
|
|
+On Windows ARM64, OS stack walking does not work because the V8 ARM64 backend
|
|
|
+doesn't emit unwinding info and also because it doesn't emit ABI compliant
|
|
|
+stack frames. This was fixed for Windows X64 (https://crrev.com/c/1469329) and
|
|
|
+documented below:
|
|
|
+
|
|
|
+https://docs.google.com/document/d/1-wf50jFlii0c_Pr52lm2ZU-49m220nhYMrHDi3vXnh0
|
|
|
+
|
|
|
+This problem can be fixed similarly for Windows ARM64 by observing that V8
|
|
|
+frames usually all have the same prolog which maintains a chain via frame
|
|
|
+pointer (fp or x29 register).
|
|
|
+
|
|
|
+stp fp, lr, [sp, ...]
|
|
|
+
|
|
|
+One exception is JSEntry which stops fp pointer chain and needs to be handled
|
|
|
+specially.
|
|
|
+
|
|
|
+So it is possible to define XDATA with UNWIND_CODE which specify how Windows
|
|
|
+should walk through V8 dynamic frames. The same as X64, since V8 Code objects
|
|
|
+are all allocated in the same code-range for an Isolate, it is possible to
|
|
|
+register at most 2 XDATA and a group of PDATA entries to cover stack walking
|
|
|
+for all the code generated inside that code-range. This is more than 1
|
|
|
+PDATA/XDATA because according to the Windows ARM64 exeption handling document,
|
|
|
+1 PDATA can cover less than 1MB code range (see below doc).
|
|
|
+
|
|
|
+https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
|
|
|
+
|
|
|
+This PR implements stackwalk for Windows ARM64 to be on par with X64, including
|
|
|
+embedded builtins, jitted code and wasm jitted code, but not including register
|
|
|
+handler for handling exception only, because there is no backward compatibility
|
|
|
+to maintain for Windows ARM64 which was released since 1709 windows build.
|
|
|
+
|
|
|
+Bug: chromium:893460
|
|
|
+Change-Id: Ic74cbdad8af5cf342185030a4c53796f12ea5429
|
|
|
+Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1701133
|
|
|
+Reviewed-by: Michael Starzinger <[email protected]>
|
|
|
+Reviewed-by: Jakob Gruber <[email protected]>
|
|
|
+Commit-Queue: Jakob Gruber <[email protected]>
|
|
|
+Cr-Commit-Position: refs/heads/master@{#63002}
|
|
|
+
|
|
|
+diff --git a/BUILD.gn b/BUILD.gn
|
|
|
+index 071a85c3f57ae92dcf90d47a13299e43f81753ec..d3cc60935541d080234ebc30b231904a48e68862 100644
|
|
|
+--- a/BUILD.gn
|
|
|
++++ b/BUILD.gn
|
|
|
+@@ -91,7 +91,7 @@ declare_args() {
|
|
|
+ # Enable embedded builtins.
|
|
|
+ v8_enable_embedded_builtins = true
|
|
|
+
|
|
|
+- # Enable the registration of unwinding info for Windows/x64.
|
|
|
++ # Enable the registration of unwinding info for Windows x64 and ARM64.
|
|
|
+ v8_win64_unwinding_info = true
|
|
|
+
|
|
|
+ # Enable code comments for builtins in the snapshot (impacts performance).
|
|
|
+@@ -3131,6 +3131,12 @@ v8_source_set("v8_base_without_compiler") {
|
|
|
+ "src/regexp/arm64/regexp-macro-assembler-arm64.h",
|
|
|
+ "src/wasm/baseline/arm64/liftoff-assembler-arm64.h",
|
|
|
+ ]
|
|
|
++ if (is_win) {
|
|
|
++ sources += [
|
|
|
++ "src/diagnostics/unwinding-info-win64.cc",
|
|
|
++ "src/diagnostics/unwinding-info-win64.h",
|
|
|
++ ]
|
|
|
++ }
|
|
|
+ jumbo_excluded_sources += [
|
|
|
+ # TODO([email protected]): fix this code so it doesn't need
|
|
|
+ # to be excluded, see the comments inside.
|
|
|
+diff --git a/src/api/api.cc b/src/api/api.cc
|
|
|
+index 6117189ad11a189e03c4db1307d919b818751b18..5b8f980929b21305f6846e8e6a3342926251efbf 100644
|
|
|
+--- a/src/api/api.cc
|
|
|
++++ b/src/api/api.cc
|
|
|
+@@ -121,9 +121,9 @@
|
|
|
+ #include <windows.h>
|
|
|
+ #include "include/v8-wasm-trap-handler-win.h"
|
|
|
+ #include "src/trap-handler/handler-inside-win.h"
|
|
|
+-#if V8_TARGET_ARCH_X64
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ #include "src/diagnostics/unwinding-info-win64.h"
|
|
|
+-#endif // V8_TARGET_ARCH_X64
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+ #endif // V8_OS_WIN
|
|
|
+
|
|
|
+ namespace v8 {
|
|
|
+@@ -5609,14 +5609,14 @@ bool V8::EnableWebAssemblyTrapHandler(bool use_v8_signal_handler) {
|
|
|
+ #if defined(V8_OS_WIN)
|
|
|
+ void V8::SetUnhandledExceptionCallback(
|
|
|
+ UnhandledExceptionCallback unhandled_exception_callback) {
|
|
|
+-#if defined(V8_TARGET_ARCH_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ v8::internal::win64_unwindinfo::SetUnhandledExceptionCallback(
|
|
|
+ unhandled_exception_callback);
|
|
|
+ #else
|
|
|
+- // Not implemented on ARM64.
|
|
|
+-#endif
|
|
|
++ // Not implemented, port needed.
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN
|
|
|
+
|
|
|
+ void v8::V8::SetEntropySource(EntropySource entropy_source) {
|
|
|
+ base::RandomNumberGenerator::SetEntropySource(entropy_source);
|
|
|
+diff --git a/src/builtins/arm64/builtins-arm64.cc b/src/builtins/arm64/builtins-arm64.cc
|
|
|
+index f81a1955eeb474c527f25f9e59edb0522e81824b..0619fbfe55e0efad72ae5f2cb9623285025cacb2 100644
|
|
|
+--- a/src/builtins/arm64/builtins-arm64.cc
|
|
|
++++ b/src/builtins/arm64/builtins-arm64.cc
|
|
|
+@@ -24,6 +24,10 @@
|
|
|
+ #include "src/runtime/runtime.h"
|
|
|
+ #include "src/wasm/wasm-objects.h"
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++#include "src/diagnostics/unwinding-info-win64.h"
|
|
|
++#endif // V8_OS_WIN
|
|
|
++
|
|
|
+ namespace v8 {
|
|
|
+ namespace internal {
|
|
|
+
|
|
|
+@@ -623,6 +627,23 @@ void Generate_JSEntryVariant(MacroAssembler* masm, StackFrame::Type type,
|
|
|
+ // will have no effect on the model or real hardware.
|
|
|
+ __ EnableInstrumentation();
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++ // Windows ARM64 relies on a frame pointer (fp/x29 which are aliases to each
|
|
|
++ // other) chain to do stack unwinding, but JSEntry breaks that by setting fp
|
|
|
++ // to point to bad_frame_pointer below. To fix unwind information for this
|
|
|
++ // case, JSEntry registers the offset (from current fp to the caller's fp
|
|
|
++ // saved by PushCalleeSavedRegisters on stack) to xdata_encoder which then
|
|
|
++ // emits the offset value as part of result unwind data accordingly. The
|
|
|
++ // current offset is kFramePointerOffset which includes bad_frame_pointer
|
|
|
++ // saved below plus kFramePointerOffsetInPushCalleeSavedRegisters.
|
|
|
++ const int kFramePointerOffset =
|
|
|
++ kFramePointerOffsetInPushCalleeSavedRegisters + kSystemPointerSize;
|
|
|
++ win64_unwindinfo::XdataEncoder* xdata_encoder = masm->GetXdataEncoder();
|
|
|
++ if (xdata_encoder) {
|
|
|
++ xdata_encoder->onFramePointerAdjustment(kFramePointerOffset);
|
|
|
++ }
|
|
|
++#endif
|
|
|
++
|
|
|
+ __ PushCalleeSavedRegisters();
|
|
|
+
|
|
|
+ // Set up the reserved register for 0.0.
|
|
|
+diff --git a/src/builtins/setup-builtins-internal.cc b/src/builtins/setup-builtins-internal.cc
|
|
|
+index 7b4a068300e20f56a1491a494ed8978dbb95398a..39cbf15b6504ce380d165b56ca20e52460d65b23 100644
|
|
|
+--- a/src/builtins/setup-builtins-internal.cc
|
|
|
++++ b/src/builtins/setup-builtins-internal.cc
|
|
|
+@@ -119,9 +119,9 @@ Code BuildWithMacroAssembler(Isolate* isolate, int32_t builtin_index,
|
|
|
+ .set_self_reference(masm.CodeObject())
|
|
|
+ .set_builtin_index(builtin_index)
|
|
|
+ .Build();
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ isolate->SetBuiltinUnwindData(builtin_index, masm.GetUnwindInfo());
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+ return *code;
|
|
|
+ }
|
|
|
+
|
|
|
+diff --git a/src/codegen/arm64/assembler-arm64.cc b/src/codegen/arm64/assembler-arm64.cc
|
|
|
+index 53da75760ba31bed3e3cf19397474b353bc83fdf..d94370f5d7d6456a21f681bd81afa54e06863ce3 100644
|
|
|
+--- a/src/codegen/arm64/assembler-arm64.cc
|
|
|
++++ b/src/codegen/arm64/assembler-arm64.cc
|
|
|
+@@ -520,6 +520,12 @@ Assembler::Assembler(const AssemblerOptions& options,
|
|
|
+ const_pool_blocked_nesting_ = 0;
|
|
|
+ veneer_pool_blocked_nesting_ = 0;
|
|
|
+ Reset();
|
|
|
++
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++ if (options.collect_win64_unwind_info) {
|
|
|
++ xdata_encoder_ = std::make_unique<win64_unwindinfo::XdataEncoder>(*this);
|
|
|
++ }
|
|
|
++#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ Assembler::~Assembler() {
|
|
|
+@@ -546,6 +552,14 @@ void Assembler::Reset() {
|
|
|
+ no_const_pool_before_ = 0;
|
|
|
+ }
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++win64_unwindinfo::BuiltinUnwindInfo Assembler::GetUnwindInfo() const {
|
|
|
++ DCHECK(options().collect_win64_unwind_info);
|
|
|
++ DCHECK_NOT_NULL(xdata_encoder_);
|
|
|
++ return xdata_encoder_->unwinding_info();
|
|
|
++}
|
|
|
++#endif
|
|
|
++
|
|
|
+ void Assembler::AllocateAndInstallRequestedHeapObjects(Isolate* isolate) {
|
|
|
+ DCHECK_IMPLIES(isolate == nullptr, heap_object_requests_.empty());
|
|
|
+ for (auto& request : heap_object_requests_) {
|
|
|
+@@ -1395,6 +1409,12 @@ void Assembler::ldp(const CPURegister& rt, const CPURegister& rt2,
|
|
|
+ void Assembler::stp(const CPURegister& rt, const CPURegister& rt2,
|
|
|
+ const MemOperand& dst) {
|
|
|
+ LoadStorePair(rt, rt2, dst, StorePairOpFor(rt, rt2));
|
|
|
++
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++ if (xdata_encoder_ && rt == x29 && rt2 == lr && dst.base().IsSP()) {
|
|
|
++ xdata_encoder_->onSaveFpLr();
|
|
|
++ }
|
|
|
++#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ void Assembler::ldpsw(const Register& rt, const Register& rt2,
|
|
|
+diff --git a/src/codegen/arm64/assembler-arm64.h b/src/codegen/arm64/assembler-arm64.h
|
|
|
+index fb5feb23074ac888e85a3676c1cbbb63126e72d5..7d1f10ad1132a51d5e93a0866ed2f13c4c9d4857 100644
|
|
|
+--- a/src/codegen/arm64/assembler-arm64.h
|
|
|
++++ b/src/codegen/arm64/assembler-arm64.h
|
|
|
+@@ -25,6 +25,10 @@
|
|
|
+ #undef mvn
|
|
|
+ #endif
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++#include "src/diagnostics/unwinding-info-win64.h"
|
|
|
++#endif // V8_OS_WIN
|
|
|
++
|
|
|
+ namespace v8 {
|
|
|
+ namespace internal {
|
|
|
+
|
|
|
+@@ -2452,6 +2456,14 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
|
|
|
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BlockPoolsScope);
|
|
|
+ };
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++ win64_unwindinfo::XdataEncoder* GetXdataEncoder() {
|
|
|
++ return xdata_encoder_.get();
|
|
|
++ }
|
|
|
++
|
|
|
++ win64_unwindinfo::BuiltinUnwindInfo GetUnwindInfo() const;
|
|
|
++#endif
|
|
|
++
|
|
|
+ protected:
|
|
|
+ inline const Register& AppropriateZeroRegFor(const CPURegister& reg) const;
|
|
|
+
|
|
|
+@@ -2774,6 +2786,10 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
|
|
|
+ // veneer margin (or kMaxInt if there are no unresolved branches).
|
|
|
+ int next_veneer_pool_check_;
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++ std::unique_ptr<win64_unwindinfo::XdataEncoder> xdata_encoder_;
|
|
|
++#endif
|
|
|
++
|
|
|
+ private:
|
|
|
+ // Avoid overflows for displacements etc.
|
|
|
+ static const int kMaximalBufferSize = 512 * MB;
|
|
|
+diff --git a/src/codegen/arm64/macro-assembler-arm64.cc b/src/codegen/arm64/macro-assembler-arm64.cc
|
|
|
+index aab9fc79a2c2c25e7f97a8bd41b3e25a1f2f0af1..6c559549759b198734154abf36353e6d730c8e46 100644
|
|
|
+--- a/src/codegen/arm64/macro-assembler-arm64.cc
|
|
|
++++ b/src/codegen/arm64/macro-assembler-arm64.cc
|
|
|
+@@ -1302,6 +1302,14 @@ void MacroAssembler::PushCalleeSavedRegisters() {
|
|
|
+ stp(d8, d9, tos);
|
|
|
+
|
|
|
+ stp(x29, x30, tos);
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++ // kFramePointerOffsetInPushCalleeSavedRegisters is the offset from tos at
|
|
|
++ // the end of this function to the saved caller's fp/x29 pointer. It includes
|
|
|
++ // registers from x19 to x28, which is 10 pointers defined by below stp
|
|
|
++ // instructions.
|
|
|
++ STATIC_ASSERT(kFramePointerOffsetInPushCalleeSavedRegisters ==
|
|
|
++ 10 * kSystemPointerSize);
|
|
|
++#endif // defined(V8_OS_WIN)
|
|
|
+ stp(x27, x28, tos);
|
|
|
+ stp(x25, x26, tos);
|
|
|
+ stp(x23, x24, tos);
|
|
|
+diff --git a/src/codegen/arm64/macro-assembler-arm64.h b/src/codegen/arm64/macro-assembler-arm64.h
|
|
|
+index 6961428f35a0c052c9faf6afb3db42b9afabe0cf..651524795cdf78935220ef24bac637cd013c9aae 100644
|
|
|
+--- a/src/codegen/arm64/macro-assembler-arm64.h
|
|
|
++++ b/src/codegen/arm64/macro-assembler-arm64.h
|
|
|
+@@ -83,6 +83,12 @@ inline MemOperand FieldMemOperand(Register object, int offset);
|
|
|
+ // ----------------------------------------------------------------------------
|
|
|
+ // MacroAssembler
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN)
|
|
|
++// This offset is originated from PushCalleeSavedRegisters.
|
|
|
++static constexpr int kFramePointerOffsetInPushCalleeSavedRegisters =
|
|
|
++ 10 * kSystemPointerSize;
|
|
|
++#endif // V8_OS_WIN
|
|
|
++
|
|
|
+ enum BranchType {
|
|
|
+ // Copies of architectural conditions.
|
|
|
+ // The associated conditions can be used in place of those, the code will
|
|
|
+diff --git a/src/common/globals.h b/src/common/globals.h
|
|
|
+index 5d4b957e84fc01e12854dc8fed23b967cd971dcd..b981670263f9091677591f806affc9def9044366 100644
|
|
|
+--- a/src/common/globals.h
|
|
|
++++ b/src/common/globals.h
|
|
|
+@@ -101,6 +101,14 @@ constexpr int kStackSpaceRequiredForCompilation = 40;
|
|
|
+ #define V8_OS_WIN_X64 true
|
|
|
+ #endif
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN) && defined(V8_TARGET_ARCH_ARM64)
|
|
|
++#define V8_OS_WIN_ARM64 true
|
|
|
++#endif
|
|
|
++
|
|
|
++#if defined(V8_OS_WIN_X64) || defined(V8_OS_WIN_ARM64)
|
|
|
++#define V8_OS_WIN64 true
|
|
|
++#endif
|
|
|
++
|
|
|
+ // Superclass for classes only using static method functions.
|
|
|
+ // The subclass of AllStatic cannot be instantiated at all.
|
|
|
+ class AllStatic {
|
|
|
+diff --git a/src/compiler/backend/code-generator.cc b/src/compiler/backend/code-generator.cc
|
|
|
+index bb83a8497bbe3e7a398eeb65b5050673311cbd6a..5d20d88cd1351184cac02d69eda4b9455fef328d 100644
|
|
|
+--- a/src/compiler/backend/code-generator.cc
|
|
|
++++ b/src/compiler/backend/code-generator.cc
|
|
|
+@@ -396,12 +396,12 @@ MaybeHandle<Code> CodeGenerator::FinalizeCode() {
|
|
|
+ CodeDesc desc;
|
|
|
+ tasm()->GetCode(isolate(), &desc, safepoints(), handler_table_offset_);
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ if (Builtins::IsBuiltinId(info_->builtin_index())) {
|
|
|
+ isolate_->SetBuiltinUnwindData(info_->builtin_index(),
|
|
|
+ tasm()->GetUnwindInfo());
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ if (unwinding_info_writer_.eh_frame_writer()) {
|
|
|
+ unwinding_info_writer_.eh_frame_writer()->GetEhFrame(&desc);
|
|
|
+diff --git a/src/diagnostics/unwinding-info-win64.cc b/src/diagnostics/unwinding-info-win64.cc
|
|
|
+index 8fb01dba9a55c75de677286baa19bb9edae74231..6cc53da51f16a45c3a8ad14ca0b2150bed43d766 100644
|
|
|
+--- a/src/diagnostics/unwinding-info-win64.cc
|
|
|
++++ b/src/diagnostics/unwinding-info-win64.cc
|
|
|
+@@ -4,12 +4,18 @@
|
|
|
+
|
|
|
+ #include "src/diagnostics/unwinding-info-win64.h"
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
+-
|
|
|
+ #include "src/codegen/macro-assembler.h"
|
|
|
+-#include "src/codegen/x64/assembler-x64.h"
|
|
|
+ #include "src/utils/allocation.h"
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN_X64)
|
|
|
++#include "src/codegen/x64/assembler-x64.h"
|
|
|
++#elif defined(V8_OS_WIN_ARM64)
|
|
|
++#include "src/codegen/arm64/assembler-arm64-inl.h"
|
|
|
++#include "src/codegen/arm64/macro-assembler-arm64-inl.h"
|
|
|
++#else
|
|
|
++#error "Unsupported OS"
|
|
|
++#endif // V8_OS_WIN_X64
|
|
|
++
|
|
|
+ namespace v8 {
|
|
|
+ namespace internal {
|
|
|
+ namespace win64_unwindinfo {
|
|
|
+@@ -22,9 +28,36 @@ bool CanRegisterUnwindInfoForNonABICompliantCodeRange() {
|
|
|
+
|
|
|
+ bool RegisterUnwindInfoForExceptionHandlingOnly() {
|
|
|
+ DCHECK(CanRegisterUnwindInfoForNonABICompliantCodeRange());
|
|
|
++#if defined(V8_OS_WIN_ARM64)
|
|
|
++ return !FLAG_win64_unwinding_info;
|
|
|
++#else
|
|
|
+ return !IsWindows8OrGreater() || !FLAG_win64_unwinding_info;
|
|
|
++#endif
|
|
|
++}
|
|
|
++
|
|
|
++v8::UnhandledExceptionCallback unhandled_exception_callback_g = nullptr;
|
|
|
++
|
|
|
++void SetUnhandledExceptionCallback(
|
|
|
++ v8::UnhandledExceptionCallback unhandled_exception_callback) {
|
|
|
++ unhandled_exception_callback_g = unhandled_exception_callback;
|
|
|
++}
|
|
|
++
|
|
|
++// This function is registered as exception handler for V8-generated code as
|
|
|
++// part of the registration of unwinding info. It is referenced by
|
|
|
++// RegisterNonABICompliantCodeRange(), below, and by the unwinding info for
|
|
|
++// builtins declared in the embedded blob.
|
|
|
++extern "C" __declspec(dllexport) int CRASH_HANDLER_FUNCTION_NAME(
|
|
|
++ PEXCEPTION_RECORD ExceptionRecord, ULONG64 EstablisherFrame,
|
|
|
++ PCONTEXT ContextRecord, PDISPATCHER_CONTEXT DispatcherContext) {
|
|
|
++ if (unhandled_exception_callback_g != nullptr) {
|
|
|
++ EXCEPTION_POINTERS info = {ExceptionRecord, ContextRecord};
|
|
|
++ return unhandled_exception_callback_g(&info);
|
|
|
++ }
|
|
|
++ return ExceptionContinueSearch;
|
|
|
+ }
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN_X64)
|
|
|
++
|
|
|
+ #pragma pack(push, 1)
|
|
|
+
|
|
|
+ /*
|
|
|
+@@ -49,9 +82,12 @@ struct UNWIND_INFO {
|
|
|
+ unsigned char FrameOffset : 4;
|
|
|
+ };
|
|
|
+
|
|
|
++static constexpr int kNumberOfUnwindCodes = 2;
|
|
|
++static constexpr int kMaxExceptionThunkSize = 12;
|
|
|
++
|
|
|
+ struct V8UnwindData {
|
|
|
+ UNWIND_INFO unwind_info;
|
|
|
+- UNWIND_CODE unwind_codes[2];
|
|
|
++ UNWIND_CODE unwind_codes[kNumberOfUnwindCodes];
|
|
|
+
|
|
|
+ V8UnwindData() {
|
|
|
+ static constexpr int kOpPushNonvol = 0;
|
|
|
+@@ -87,46 +123,244 @@ struct ExceptionHandlerUnwindData {
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
++struct CodeRangeUnwindingRecord {
|
|
|
++ void* dynamic_table;
|
|
|
++ uint32_t runtime_function_count;
|
|
|
++ V8UnwindData unwind_info;
|
|
|
++ uint32_t exception_handler;
|
|
|
++ uint8_t exception_thunk[kMaxExceptionThunkSize];
|
|
|
++ RUNTIME_FUNCTION runtime_function[kDefaultRuntimeFunctionCount];
|
|
|
++};
|
|
|
++
|
|
|
++struct ExceptionHandlerRecord {
|
|
|
++ uint32_t runtime_function_count;
|
|
|
++ RUNTIME_FUNCTION runtime_function[kDefaultRuntimeFunctionCount];
|
|
|
++ ExceptionHandlerUnwindData unwind_info;
|
|
|
++ uint32_t exception_handler;
|
|
|
++ uint8_t exception_thunk[kMaxExceptionThunkSize];
|
|
|
++};
|
|
|
++
|
|
|
+ #pragma pack(pop)
|
|
|
+
|
|
|
+-v8::UnhandledExceptionCallback unhandled_exception_callback_g = nullptr;
|
|
|
++std::vector<uint8_t> GetUnwindInfoForBuiltinFunctions() {
|
|
|
++ V8UnwindData xdata;
|
|
|
++ return std::vector<uint8_t>(
|
|
|
++ reinterpret_cast<uint8_t*>(&xdata),
|
|
|
++ reinterpret_cast<uint8_t*>(&xdata) + sizeof(xdata));
|
|
|
++}
|
|
|
+
|
|
|
+-void SetUnhandledExceptionCallback(
|
|
|
+- v8::UnhandledExceptionCallback unhandled_exception_callback) {
|
|
|
+- unhandled_exception_callback_g = unhandled_exception_callback;
|
|
|
++template <typename Record>
|
|
|
++void InitUnwindingRecord(Record* record, size_t code_size_in_bytes) {
|
|
|
++ // We assume that the first page of the code range is executable and
|
|
|
++ // committed and reserved to contain PDATA/XDATA.
|
|
|
++
|
|
|
++ // All addresses are 32bit relative offsets to start.
|
|
|
++ record->runtime_function[0].BeginAddress = 0;
|
|
|
++ record->runtime_function[0].EndAddress =
|
|
|
++ static_cast<DWORD>(code_size_in_bytes);
|
|
|
++ record->runtime_function[0].UnwindData = offsetof(Record, unwind_info);
|
|
|
++ record->runtime_function_count = 1;
|
|
|
++ record->exception_handler = offsetof(Record, exception_thunk);
|
|
|
++
|
|
|
++ // Hardcoded thunk.
|
|
|
++ AssemblerOptions options;
|
|
|
++ options.record_reloc_info_for_serialization = false;
|
|
|
++ MacroAssembler masm(nullptr, options, CodeObjectRequired::kNo,
|
|
|
++ NewAssemblerBuffer(64));
|
|
|
++ masm.movq(rax, reinterpret_cast<uint64_t>(&CRASH_HANDLER_FUNCTION_NAME));
|
|
|
++ masm.jmp(rax);
|
|
|
++ DCHECK_LE(masm.instruction_size(), sizeof(record->exception_thunk));
|
|
|
++ memcpy(&record->exception_thunk[0], masm.buffer_start(),
|
|
|
++ masm.instruction_size());
|
|
|
+ }
|
|
|
+
|
|
|
+-// This function is registered as exception handler for V8-generated code as
|
|
|
+-// part of the registration of unwinding info. It is referenced by
|
|
|
+-// RegisterNonABICompliantCodeRange(), below, and by the unwinding info for
|
|
|
+-// builtins declared in the embedded blob.
|
|
|
+-extern "C" __declspec(dllexport) int CRASH_HANDLER_FUNCTION_NAME(
|
|
|
+- PEXCEPTION_RECORD ExceptionRecord, ULONG64 EstablisherFrame,
|
|
|
+- PCONTEXT ContextRecord, PDISPATCHER_CONTEXT DispatcherContext) {
|
|
|
+- if (unhandled_exception_callback_g != nullptr) {
|
|
|
+- EXCEPTION_POINTERS info = {ExceptionRecord, ContextRecord};
|
|
|
+- return unhandled_exception_callback_g(&info);
|
|
|
+- }
|
|
|
+- return ExceptionContinueSearch;
|
|
|
++#elif defined(V8_OS_WIN_ARM64)
|
|
|
++
|
|
|
++#pragma pack(push, 1)
|
|
|
++
|
|
|
++// ARM64 unwind codes are defined in below doc.
|
|
|
++// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
|
|
|
++enum UnwindOp8Bit {
|
|
|
++ OpNop = 0xE3,
|
|
|
++ OpSaveFpLr = 0x40,
|
|
|
++ OpSaveFpLrX = 0x80,
|
|
|
++ OpSetFp = 0xE1,
|
|
|
++ OpEnd = 0xE4,
|
|
|
++};
|
|
|
++
|
|
|
++typedef uint32_t UNWIND_CODE;
|
|
|
++
|
|
|
++constexpr UNWIND_CODE Combine8BitUnwindCodes(uint8_t code0,
|
|
|
++ uint8_t code1 = OpNop,
|
|
|
++ uint8_t code2 = OpNop,
|
|
|
++ uint8_t code3 = OpNop) {
|
|
|
++ return static_cast<uint32_t>(code0) | (static_cast<uint32_t>(code1) << 8) |
|
|
|
++ (static_cast<uint32_t>(code2) << 16) |
|
|
|
++ (static_cast<uint32_t>(code3) << 24);
|
|
|
+ }
|
|
|
+
|
|
|
+-static constexpr int kMaxExceptionThunkSize = 12;
|
|
|
++// UNWIND_INFO defines the static part (first 32-bit) of the .xdata record in
|
|
|
++// below doc.
|
|
|
++// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#xdata-records
|
|
|
++struct UNWIND_INFO {
|
|
|
++ uint32_t FunctionLength : 18;
|
|
|
++ uint32_t Version : 2;
|
|
|
++ uint32_t X : 1;
|
|
|
++ uint32_t E : 1;
|
|
|
++ uint32_t EpilogCount : 5;
|
|
|
++ uint32_t CodeWords : 5;
|
|
|
++};
|
|
|
++
|
|
|
++static constexpr int kNumberOfUnwindCodes = 1;
|
|
|
++static constexpr int kMaxExceptionThunkSize = 16;
|
|
|
++static constexpr int kFunctionLengthShiftSize = 2;
|
|
|
++static constexpr int kFunctionLengthMask = (1 << kFunctionLengthShiftSize) - 1;
|
|
|
++static constexpr int kFramePointerAdjustmentShiftSize = 3;
|
|
|
++static constexpr int kFramePointerAdjustmentShiftMask =
|
|
|
++ (1 << kFramePointerAdjustmentShiftSize) - 1;
|
|
|
++
|
|
|
++struct V8UnwindData {
|
|
|
++ UNWIND_INFO unwind_info;
|
|
|
++ UNWIND_CODE unwind_codes[kNumberOfUnwindCodes];
|
|
|
++
|
|
|
++ V8UnwindData() {
|
|
|
++ memset(&unwind_info, 0, sizeof(UNWIND_INFO));
|
|
|
++ unwind_info.X = 1; // has exception handler after unwind-codes.
|
|
|
++ unwind_info.CodeWords = 1;
|
|
|
++
|
|
|
++ // stp fp, lr, [sp, #offset]!
|
|
|
++ unwind_codes[0] = Combine8BitUnwindCodes(OpSetFp, OpSaveFpLrX, OpEnd);
|
|
|
++ }
|
|
|
++};
|
|
|
+
|
|
|
+ struct CodeRangeUnwindingRecord {
|
|
|
+- RUNTIME_FUNCTION runtime_function;
|
|
|
++ void* dynamic_table;
|
|
|
++ uint32_t runtime_function_count;
|
|
|
+ V8UnwindData unwind_info;
|
|
|
+ uint32_t exception_handler;
|
|
|
+- uint8_t exception_thunk[kMaxExceptionThunkSize];
|
|
|
+- void* dynamic_table;
|
|
|
+-};
|
|
|
+
|
|
|
+-struct ExceptionHandlerRecord {
|
|
|
+- RUNTIME_FUNCTION runtime_function;
|
|
|
+- ExceptionHandlerUnwindData unwind_info;
|
|
|
+- uint32_t exception_handler;
|
|
|
++ // For Windows ARM64 unwinding, register 2 unwind_info for each code range,
|
|
|
++ // unwind_info for all full size ranges (1MB - 4 bytes) and unwind_info1 for
|
|
|
++ // the remaining non full size range. There is at most 1 range which is less
|
|
|
++ // than full size.
|
|
|
++ V8UnwindData unwind_info1;
|
|
|
++ uint32_t exception_handler1;
|
|
|
+ uint8_t exception_thunk[kMaxExceptionThunkSize];
|
|
|
++
|
|
|
++ // More RUNTIME_FUNCTION structs could follow below array because the number
|
|
|
++ // of RUNTIME_FUNCTION needed to cover given code range is computed at
|
|
|
++ // runtime.
|
|
|
++ RUNTIME_FUNCTION runtime_function[kDefaultRuntimeFunctionCount];
|
|
|
+ };
|
|
|
+
|
|
|
++#pragma pack(pop)
|
|
|
++
|
|
|
++std::vector<uint8_t> GetUnwindInfoForBuiltinFunction(uint32_t func_len,
|
|
|
++ int32_t fp_adjustment) {
|
|
|
++ DCHECK_LE(func_len, kMaxFunctionLength);
|
|
|
++ DCHECK_EQ((func_len & kFunctionLengthMask), 0);
|
|
|
++ USE(kFunctionLengthMask);
|
|
|
++
|
|
|
++ // Unwind code save_fplr requires the offset to be within range [0, 504].
|
|
|
++ // This range is defined in below doc for unwind code save_fplr.
|
|
|
++ // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
|
|
|
++ DCHECK_GE(fp_adjustment, 0);
|
|
|
++ DCHECK_LE(fp_adjustment, 504);
|
|
|
++ DCHECK_EQ((fp_adjustment & kFramePointerAdjustmentShiftMask), 0);
|
|
|
++ USE(kFramePointerAdjustmentShiftMask);
|
|
|
++
|
|
|
++ V8UnwindData xdata;
|
|
|
++ // FunctionLength is ensured to be aligned at instruction size and Windows
|
|
|
++ // ARM64 doesn't encoding its 2 LSB.
|
|
|
++ xdata.unwind_info.FunctionLength = func_len >> kFunctionLengthShiftSize;
|
|
|
++ xdata.unwind_info.CodeWords = 1;
|
|
|
++ xdata.unwind_codes[0] = Combine8BitUnwindCodes(
|
|
|
++ OpSetFp,
|
|
|
++ (OpSaveFpLr | (fp_adjustment >> kFramePointerAdjustmentShiftSize)),
|
|
|
++ OpEnd);
|
|
|
++
|
|
|
++ return std::vector<uint8_t>(
|
|
|
++ reinterpret_cast<uint8_t*>(&xdata),
|
|
|
++ reinterpret_cast<uint8_t*>(&xdata) + sizeof(xdata));
|
|
|
++}
|
|
|
++
|
|
|
++template <typename Record>
|
|
|
++void InitUnwindingRecord(Record* record, size_t code_size_in_bytes) {
|
|
|
++ // We assume that the first page of the code range is executable and
|
|
|
++ // committed and reserved to contain multiple PDATA/XDATA to cover the whole
|
|
|
++ // range. All addresses are 32bit relative offsets to start.
|
|
|
++
|
|
|
++ // Maximum RUNTIME_FUNCTION count available in reserved memory, this includes
|
|
|
++ // static part in Record as kDefaultRuntimeFunctionCount plus dynamic part in
|
|
|
++ // the remaining reserved memory.
|
|
|
++ constexpr uint32_t max_runtime_function_count = static_cast<uint32_t>(
|
|
|
++ (kOSPageSize - sizeof(Record)) / sizeof(RUNTIME_FUNCTION) +
|
|
|
++ kDefaultRuntimeFunctionCount);
|
|
|
++
|
|
|
++ uint32_t runtime_function_index = 0;
|
|
|
++ uint32_t current_unwind_start_address = 0;
|
|
|
++ int64_t remaining_size_in_bytes = static_cast<int64_t>(code_size_in_bytes);
|
|
|
++
|
|
|
++ // Divide the code range into chunks in size kMaxFunctionLength and create a
|
|
|
++ // RUNTIME_FUNCTION for each of them. All the chunks in the same size can
|
|
|
++ // share 1 unwind_info struct, but a separate unwind_info is needed for the
|
|
|
++ // last chunk if it is smaller than kMaxFunctionLength, because unlike X64,
|
|
|
++ // unwind_info encodes the function/chunk length.
|
|
|
++ while (remaining_size_in_bytes >= kMaxFunctionLength &&
|
|
|
++ runtime_function_index < max_runtime_function_count) {
|
|
|
++ record->runtime_function[runtime_function_index].BeginAddress =
|
|
|
++ current_unwind_start_address;
|
|
|
++ record->runtime_function[runtime_function_index].UnwindData =
|
|
|
++ static_cast<DWORD>(offsetof(Record, unwind_info));
|
|
|
++
|
|
|
++ runtime_function_index++;
|
|
|
++ current_unwind_start_address += kMaxFunctionLength;
|
|
|
++ remaining_size_in_bytes -= kMaxFunctionLength;
|
|
|
++ }
|
|
|
++ // FunctionLength is ensured to be aligned at instruction size and Windows
|
|
|
++ // ARM64 doesn't encoding 2 LSB.
|
|
|
++ record->unwind_info.unwind_info.FunctionLength = kMaxFunctionLength >> 2;
|
|
|
++
|
|
|
++ if (remaining_size_in_bytes > 0 &&
|
|
|
++ runtime_function_index < max_runtime_function_count) {
|
|
|
++ DCHECK_EQ(remaining_size_in_bytes % kInstrSize, 0);
|
|
|
++
|
|
|
++ record->unwind_info1.unwind_info.FunctionLength = static_cast<uint32_t>(
|
|
|
++ remaining_size_in_bytes >> kFunctionLengthShiftSize);
|
|
|
++ record->runtime_function[runtime_function_index].BeginAddress =
|
|
|
++ current_unwind_start_address;
|
|
|
++ record->runtime_function[runtime_function_index].UnwindData =
|
|
|
++ static_cast<DWORD>(offsetof(Record, unwind_info1));
|
|
|
++
|
|
|
++ remaining_size_in_bytes -= kMaxFunctionLength;
|
|
|
++ record->exception_handler1 = offsetof(Record, exception_thunk);
|
|
|
++ record->runtime_function_count = runtime_function_index + 1;
|
|
|
++ } else {
|
|
|
++ record->runtime_function_count = runtime_function_index;
|
|
|
++ }
|
|
|
++
|
|
|
++ // 1 page can cover kMaximalCodeRangeSize for ARM64 (128MB). If
|
|
|
++ // kMaximalCodeRangeSize is changed for ARM64 and makes 1 page insufficient to
|
|
|
++ // cover it, more pages will need to reserved for unwind data.
|
|
|
++ DCHECK_LE(remaining_size_in_bytes, 0);
|
|
|
++
|
|
|
++ record->exception_handler = offsetof(Record, exception_thunk);
|
|
|
++
|
|
|
++ // Hardcoded thunk.
|
|
|
++ AssemblerOptions options;
|
|
|
++ options.record_reloc_info_for_serialization = false;
|
|
|
++ TurboAssembler masm(nullptr, options, CodeObjectRequired::kNo,
|
|
|
++ NewAssemblerBuffer(64));
|
|
|
++ masm.Mov(x16,
|
|
|
++ Operand(reinterpret_cast<uint64_t>(&CRASH_HANDLER_FUNCTION_NAME)));
|
|
|
++ masm.Br(x16);
|
|
|
++ DCHECK_LE(masm.instruction_size(), sizeof(record->exception_thunk));
|
|
|
++ memcpy(&record->exception_thunk[0], masm.buffer_start(),
|
|
|
++ masm.instruction_size());
|
|
|
++}
|
|
|
++
|
|
|
++#endif // V8_OS_WIN_X64
|
|
|
++
|
|
|
+ namespace {
|
|
|
+
|
|
|
+ V8_DECLARE_ONCE(load_ntdll_unwinding_functions_once);
|
|
|
+@@ -185,37 +419,6 @@ void DeleteGrowableFunctionTable(PVOID dynamic_table) {
|
|
|
+
|
|
|
+ } // namespace
|
|
|
+
|
|
|
+-std::vector<uint8_t> GetUnwindInfoForBuiltinFunctions() {
|
|
|
+- V8UnwindData xdata;
|
|
|
+- return std::vector<uint8_t>(
|
|
|
+- reinterpret_cast<uint8_t*>(&xdata),
|
|
|
+- reinterpret_cast<uint8_t*>(&xdata) + sizeof(xdata));
|
|
|
+-}
|
|
|
+-
|
|
|
+-template <typename Record>
|
|
|
+-void InitUnwindingRecord(Record* record, size_t code_size_in_bytes) {
|
|
|
+- // We assume that the first page of the code range is executable and
|
|
|
+- // committed and reserved to contain PDATA/XDATA.
|
|
|
+-
|
|
|
+- // All addresses are 32bit relative offsets to start.
|
|
|
+- record->runtime_function.BeginAddress = 0;
|
|
|
+- record->runtime_function.EndAddress = static_cast<DWORD>(code_size_in_bytes);
|
|
|
+- record->runtime_function.UnwindData = offsetof(Record, unwind_info);
|
|
|
+-
|
|
|
+- record->exception_handler = offsetof(Record, exception_thunk);
|
|
|
+-
|
|
|
+- // Hardcoded thunk.
|
|
|
+- AssemblerOptions options;
|
|
|
+- options.record_reloc_info_for_serialization = false;
|
|
|
+- MacroAssembler masm(nullptr, options, CodeObjectRequired::kNo,
|
|
|
+- NewAssemblerBuffer(64));
|
|
|
+- masm.movq(rax, reinterpret_cast<uint64_t>(&CRASH_HANDLER_FUNCTION_NAME));
|
|
|
+- masm.jmp(rax);
|
|
|
+- DCHECK_LE(masm.instruction_size(), sizeof(record->exception_thunk));
|
|
|
+- memcpy(&record->exception_thunk[0], masm.buffer_start(),
|
|
|
+- masm.instruction_size());
|
|
|
+-}
|
|
|
+-
|
|
|
+ void RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) {
|
|
|
+ DCHECK(CanRegisterUnwindInfoForNonABICompliantCodeRange());
|
|
|
+
|
|
|
+@@ -231,24 +434,30 @@ void RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) {
|
|
|
+ // by the embedder (like Crashpad).
|
|
|
+
|
|
|
+ if (RegisterUnwindInfoForExceptionHandlingOnly()) {
|
|
|
++#if defined(V8_OS_WIN_X64)
|
|
|
++ // Windows ARM64 starts since 1709 Windows build, no need to have exception
|
|
|
++ // handling only unwind info for compatibility.
|
|
|
+ if (unhandled_exception_callback_g) {
|
|
|
+ ExceptionHandlerRecord* record = new (start) ExceptionHandlerRecord();
|
|
|
+ InitUnwindingRecord(record, size_in_bytes);
|
|
|
+
|
|
|
+- CHECK(::RtlAddFunctionTable(&record->runtime_function, 1,
|
|
|
++ CHECK(::RtlAddFunctionTable(record->runtime_function,
|
|
|
++ kDefaultRuntimeFunctionCount,
|
|
|
+ reinterpret_cast<DWORD64>(start)));
|
|
|
+
|
|
|
+ // Protect reserved page against modifications.
|
|
|
+ DWORD old_protect;
|
|
|
+- CHECK(VirtualProtect(start, sizeof(CodeRangeUnwindingRecord),
|
|
|
++ CHECK(VirtualProtect(start, sizeof(ExceptionHandlerRecord),
|
|
|
+ PAGE_EXECUTE_READ, &old_protect));
|
|
|
+ }
|
|
|
++#endif // V8_OS_WIN_X64
|
|
|
+ } else {
|
|
|
+ CodeRangeUnwindingRecord* record = new (start) CodeRangeUnwindingRecord();
|
|
|
+ InitUnwindingRecord(record, size_in_bytes);
|
|
|
+
|
|
|
+ CHECK(AddGrowableFunctionTable(
|
|
|
+- &record->dynamic_table, &record->runtime_function, 1, 1,
|
|
|
++ &record->dynamic_table, record->runtime_function,
|
|
|
++ record->runtime_function_count, record->runtime_function_count,
|
|
|
+ reinterpret_cast<DWORD64>(start),
|
|
|
+ reinterpret_cast<DWORD64>(reinterpret_cast<uint8_t*>(start) +
|
|
|
+ size_in_bytes)));
|
|
|
+@@ -264,11 +473,15 @@ void UnregisterNonABICompliantCodeRange(void* start) {
|
|
|
+ DCHECK(CanRegisterUnwindInfoForNonABICompliantCodeRange());
|
|
|
+
|
|
|
+ if (RegisterUnwindInfoForExceptionHandlingOnly()) {
|
|
|
++#if defined(V8_OS_WIN_X64)
|
|
|
++ // Windows ARM64 starts since 1709 Windows build, no need to have exception
|
|
|
++ // handling only unwind info for compatibility.
|
|
|
+ if (unhandled_exception_callback_g) {
|
|
|
+ ExceptionHandlerRecord* record =
|
|
|
+ reinterpret_cast<ExceptionHandlerRecord*>(start);
|
|
|
+- CHECK(::RtlDeleteFunctionTable(&record->runtime_function));
|
|
|
++ CHECK(::RtlDeleteFunctionTable(record->runtime_function));
|
|
|
+ }
|
|
|
++#endif // V8_OS_WIN_X64
|
|
|
+ } else {
|
|
|
+ CodeRangeUnwindingRecord* record =
|
|
|
+ reinterpret_cast<CodeRangeUnwindingRecord*>(start);
|
|
|
+@@ -278,19 +491,41 @@ void UnregisterNonABICompliantCodeRange(void* start) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
++#if defined(V8_OS_WIN_X64)
|
|
|
++
|
|
|
+ void XdataEncoder::onPushRbp() {
|
|
|
+- current_push_rbp_offset_ = assembler_.pc_offset() - kPushRbpInstructionLength;
|
|
|
++ current_frame_code_offset_ =
|
|
|
++ assembler_.pc_offset() - kPushRbpInstructionLength;
|
|
|
+ }
|
|
|
+
|
|
|
+ void XdataEncoder::onMovRbpRsp() {
|
|
|
+- if (current_push_rbp_offset_ >= 0 &&
|
|
|
+- current_push_rbp_offset_ == assembler_.pc_offset() - kRbpPrefixLength) {
|
|
|
+- fp_offsets_.push_back(current_push_rbp_offset_);
|
|
|
++ if (current_frame_code_offset_ >= 0 &&
|
|
|
++ current_frame_code_offset_ == assembler_.pc_offset() - kRbpPrefixLength) {
|
|
|
++ fp_offsets_.push_back(current_frame_code_offset_);
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++#elif defined(V8_OS_WIN_ARM64)
|
|
|
++
|
|
|
++void XdataEncoder::onSaveFpLr() {
|
|
|
++ current_frame_code_offset_ = assembler_.pc_offset() - 4;
|
|
|
++ fp_offsets_.push_back(current_frame_code_offset_);
|
|
|
++ fp_adjustments_.push_back(current_frame_adjustment_);
|
|
|
++ if (current_frame_adjustment_ != 0) {
|
|
|
++ current_frame_adjustment_ = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
++void XdataEncoder::onFramePointerAdjustment(int bytes) {
|
|
|
++ // According to below doc, offset for save_fplr is aligned to pointer size.
|
|
|
++ // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
|
|
|
++ DCHECK_EQ((bytes & kPointerAlignmentMask), 0);
|
|
|
++
|
|
|
++ current_frame_adjustment_ = bytes;
|
|
|
++}
|
|
|
++
|
|
|
++#endif // V8_OS_WIN_X64
|
|
|
++
|
|
|
+ } // namespace win64_unwindinfo
|
|
|
+ } // namespace internal
|
|
|
+ } // namespace v8
|
|
|
+-
|
|
|
+-#endif // defined(V8_OS_WIN_X64)
|
|
|
+diff --git a/src/diagnostics/unwinding-info-win64.h b/src/diagnostics/unwinding-info-win64.h
|
|
|
+index f6611e7e2ec5a8afc1769886593f112eb4ad0f89..8f8c9469ebf92c1f81af8364486cd45f4778b248 100644
|
|
|
+--- a/src/diagnostics/unwinding-info-win64.h
|
|
|
++++ b/src/diagnostics/unwinding-info-win64.h
|
|
|
+@@ -9,7 +9,7 @@
|
|
|
+ #include "include/v8config.h"
|
|
|
+ #include "src/common/globals.h"
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ #include "src/base/win32-headers.h"
|
|
|
+
|
|
|
+ namespace v8 {
|
|
|
+@@ -21,11 +21,7 @@ namespace win64_unwindinfo {
|
|
|
+ #define CRASH_HANDLER_FUNCTION_NAME_STRING \
|
|
|
+ "CrashForExceptionInNonABICompliantCodeRange"
|
|
|
+
|
|
|
+-static const int kPushRbpInstructionLength = 1;
|
|
|
+-static const int kMovRbpRspInstructionLength = 3;
|
|
|
+-static const int kRbpPrefixCodes = 2;
|
|
|
+-static const int kRbpPrefixLength =
|
|
|
+- kPushRbpInstructionLength + kMovRbpRspInstructionLength;
|
|
|
++static const int kOSPageSize = 4096;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns true if V8 is configured to emit unwinding data for embedded in the
|
|
|
+@@ -50,15 +46,33 @@ bool CanRegisterUnwindInfoForNonABICompliantCodeRange();
|
|
|
+ void SetUnhandledExceptionCallback(
|
|
|
+ v8::UnhandledExceptionCallback unhandled_exception_callback);
|
|
|
+
|
|
|
++void RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes);
|
|
|
++void UnregisterNonABICompliantCodeRange(void* start);
|
|
|
++
|
|
|
+ /**
|
|
|
+- * Returns a vector of bytes that contains the Win64 unwind data used for all
|
|
|
++ * Default count of RUNTIME_FUNCTION needed. For Windows X64, 1 RUNTIME_FUNCTION
|
|
|
++ * covers 4GB range which is sufficient to cover the whole code range of an
|
|
|
++ * isolate or WASM module. For Windows ARM64, 1 RUNTIME_FUNCTION covers
|
|
|
++ * kMaxFunctionLength bytes so multiple RUNTIME_FUNCTION structs could be needed
|
|
|
++ * to cover the whole code range of an isolate or WASM module. The extra
|
|
|
++ * RUNTIME_FUNCTIONs are assumed following the first one in the reserved page.
|
|
|
++ */
|
|
|
++static const uint32_t kDefaultRuntimeFunctionCount = 1;
|
|
|
++
|
|
|
++#if defined(V8_OS_WIN_X64)
|
|
|
++
|
|
|
++static const int kPushRbpInstructionLength = 1;
|
|
|
++static const int kMovRbpRspInstructionLength = 3;
|
|
|
++static const int kRbpPrefixCodes = 2;
|
|
|
++static const int kRbpPrefixLength =
|
|
|
++ kPushRbpInstructionLength + kMovRbpRspInstructionLength;
|
|
|
++
|
|
|
++/**
|
|
|
++ * Returns a vector of bytes that contains the Win X64 unwind data used for all
|
|
|
+ * V8 builtin functions.
|
|
|
+ */
|
|
|
+ std::vector<uint8_t> GetUnwindInfoForBuiltinFunctions();
|
|
|
+
|
|
|
+-void RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes);
|
|
|
+-void UnregisterNonABICompliantCodeRange(void* start);
|
|
|
+-
|
|
|
+ class BuiltinUnwindInfo {
|
|
|
+ public:
|
|
|
+ BuiltinUnwindInfo() : is_leaf_function_(true) {}
|
|
|
+@@ -76,7 +90,7 @@ class BuiltinUnwindInfo {
|
|
|
+ class XdataEncoder {
|
|
|
+ public:
|
|
|
+ explicit XdataEncoder(const Assembler& assembler)
|
|
|
+- : assembler_(assembler), current_push_rbp_offset_(-1) {}
|
|
|
++ : assembler_(assembler), current_frame_code_offset_(-1) {}
|
|
|
+
|
|
|
+ void onPushRbp();
|
|
|
+ void onMovRbpRsp();
|
|
|
+@@ -88,14 +102,77 @@ class XdataEncoder {
|
|
|
+ private:
|
|
|
+ const Assembler& assembler_;
|
|
|
+ std::vector<int> fp_offsets_;
|
|
|
+- int current_push_rbp_offset_;
|
|
|
++ int current_frame_code_offset_;
|
|
|
+ };
|
|
|
+
|
|
|
+-} // namespace win64_unwindinfo
|
|
|
++#elif defined(V8_OS_WIN_ARM64)
|
|
|
+
|
|
|
++/**
|
|
|
++ * Base on below doc, unwind record has 18 bits (unsigned) to encode function
|
|
|
++ * length, besides 2 LSB which are always 0.
|
|
|
++ * https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#xdata-records
|
|
|
++ */
|
|
|
++static const int kMaxFunctionLength = ((1 << 18) - 1) << 2;
|
|
|
++
|
|
|
++/**
|
|
|
++ * Returns a vector of bytes that contains the Win ARM64 unwind data used for
|
|
|
++ * all V8 builtin functions.
|
|
|
++ *
|
|
|
++ * func_len: length in bytes of current function/region to unwind.
|
|
|
++ * fp_adjustment: offset of the saved caller's fp based on fp in current frame.
|
|
|
++ * this is necessary to encode unwind data for Windows stack
|
|
|
++ * unwinder to find correct caller's fp.
|
|
|
++ */
|
|
|
++std::vector<uint8_t> GetUnwindInfoForBuiltinFunction(uint32_t func_len,
|
|
|
++ int32_t fp_adjustment);
|
|
|
++class BuiltinUnwindInfo {
|
|
|
++ public:
|
|
|
++ BuiltinUnwindInfo() : is_leaf_function_(true) {}
|
|
|
++ explicit BuiltinUnwindInfo(const std::vector<int>& fp_offsets,
|
|
|
++ const std::vector<int>& fp_adjustments)
|
|
|
++ : is_leaf_function_(false),
|
|
|
++ fp_offsets_(fp_offsets),
|
|
|
++ fp_adjustments_(fp_adjustments) {}
|
|
|
++
|
|
|
++ const std::vector<int>& fp_adjustments() const { return fp_adjustments_; }
|
|
|
++
|
|
|
++ bool is_leaf_function() const { return is_leaf_function_; }
|
|
|
++ const std::vector<int>& fp_offsets() const { return fp_offsets_; }
|
|
|
++
|
|
|
++ private:
|
|
|
++ bool is_leaf_function_;
|
|
|
++ std::vector<int> fp_offsets_;
|
|
|
++ std::vector<int> fp_adjustments_;
|
|
|
++};
|
|
|
++
|
|
|
++class XdataEncoder {
|
|
|
++ public:
|
|
|
++ explicit XdataEncoder(const Assembler& assembler)
|
|
|
++ : assembler_(assembler),
|
|
|
++ current_frame_code_offset_(-1),
|
|
|
++ current_frame_adjustment_(0) {}
|
|
|
++
|
|
|
++ void onSaveFpLr();
|
|
|
++ void onFramePointerAdjustment(int bytes);
|
|
|
++
|
|
|
++ BuiltinUnwindInfo unwinding_info() const {
|
|
|
++ return BuiltinUnwindInfo(fp_offsets_, fp_adjustments_);
|
|
|
++ }
|
|
|
++
|
|
|
++ private:
|
|
|
++ const Assembler& assembler_;
|
|
|
++ std::vector<int> fp_offsets_;
|
|
|
++ int current_frame_code_offset_;
|
|
|
++ int current_frame_adjustment_;
|
|
|
++ std::vector<int> fp_adjustments_;
|
|
|
++};
|
|
|
++
|
|
|
++#endif
|
|
|
++
|
|
|
++} // namespace win64_unwindinfo
|
|
|
+ } // namespace internal
|
|
|
+ } // namespace v8
|
|
|
+
|
|
|
+-#endif // defined(V8_OS_WIN_X64)
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ #endif // V8_DIAGNOSTICS_UNWINDING_INFO_WIN64_H_
|
|
|
+diff --git a/src/execution/isolate.cc b/src/execution/isolate.cc
|
|
|
+index 8a8db12ca3f59577436d148bb2693c836720af2d..380131730c7e87eced53d7a4d68d328cc9b01b8a 100644
|
|
|
+--- a/src/execution/isolate.cc
|
|
|
++++ b/src/execution/isolate.cc
|
|
|
+@@ -85,9 +85,9 @@
|
|
|
+ #include "unicode/uobject.h"
|
|
|
+ #endif // V8_INTL_SUPPORT
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ #include "src/diagnostics/unwinding-info-win64.h"
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ extern "C" const uint8_t* v8_Default_embedded_blob_;
|
|
|
+ extern "C" uint32_t v8_Default_embedded_blob_size_;
|
|
|
+@@ -2935,7 +2935,7 @@ void Isolate::Deinit() {
|
|
|
+ heap_profiler()->StopSamplingHeapProfiler();
|
|
|
+ }
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ if (win64_unwindinfo::CanRegisterUnwindInfoForNonABICompliantCodeRange() &&
|
|
|
+ heap()->memory_allocator()) {
|
|
|
+ const base::AddressRegion& code_range =
|
|
|
+@@ -2943,7 +2943,7 @@ void Isolate::Deinit() {
|
|
|
+ void* start = reinterpret_cast<void*>(code_range.begin());
|
|
|
+ win64_unwindinfo::UnregisterNonABICompliantCodeRange(start);
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ debug()->Unload();
|
|
|
+
|
|
|
+@@ -3512,7 +3512,7 @@ bool Isolate::Init(ReadOnlyDeserializer* read_only_deserializer,
|
|
|
+ sampling_flags);
|
|
|
+ }
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ if (win64_unwindinfo::CanRegisterUnwindInfoForNonABICompliantCodeRange()) {
|
|
|
+ const base::AddressRegion& code_range =
|
|
|
+ heap()->memory_allocator()->code_range();
|
|
|
+@@ -3520,7 +3520,7 @@ bool Isolate::Init(ReadOnlyDeserializer* read_only_deserializer,
|
|
|
+ size_t size_in_bytes = code_range.size();
|
|
|
+ win64_unwindinfo::RegisterNonABICompliantCodeRange(start, size_in_bytes);
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ if (create_heap_objects && FLAG_profile_deserialization) {
|
|
|
+ double ms = timer.Elapsed().InMillisecondsF();
|
|
|
+@@ -4324,7 +4324,7 @@ void Isolate::PrepareBuiltinSourcePositionMap() {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ void Isolate::SetBuiltinUnwindData(
|
|
|
+ int builtin_index,
|
|
|
+ const win64_unwindinfo::BuiltinUnwindInfo& unwinding_info) {
|
|
|
+@@ -4332,7 +4332,7 @@ void Isolate::SetBuiltinUnwindData(
|
|
|
+ embedded_file_writer_->SetBuiltinUnwindData(builtin_index, unwinding_info);
|
|
|
+ }
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ void Isolate::SetPrepareStackTraceCallback(PrepareStackTraceCallback callback) {
|
|
|
+ prepare_stack_trace_callback_ = callback;
|
|
|
+diff --git a/src/execution/isolate.h b/src/execution/isolate.h
|
|
|
+index 4b4bf9cd7c61687b0f36da1eaa672979c59dab67..33e43c81113ea88d611aa134edc82c2e6d06cfde 100644
|
|
|
+--- a/src/execution/isolate.h
|
|
|
++++ b/src/execution/isolate.h
|
|
|
+@@ -1484,11 +1484,11 @@ class Isolate final : private HiddenFactory {
|
|
|
+ // annotate the builtin blob with debugging information.
|
|
|
+ void PrepareBuiltinSourcePositionMap();
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ void SetBuiltinUnwindData(
|
|
|
+ int builtin_index,
|
|
|
+ const win64_unwindinfo::BuiltinUnwindInfo& unwinding_info);
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ void SetPrepareStackTraceCallback(PrepareStackTraceCallback callback);
|
|
|
+ MaybeHandle<Object> RunPrepareStackTraceCallback(Handle<Context>,
|
|
|
+diff --git a/src/snapshot/embedded/embedded-file-writer.cc b/src/snapshot/embedded/embedded-file-writer.cc
|
|
|
+index 4703ef48224f9f155ef2f8f0cd47507968a3fdfe..5f57993fc3255a68ce6ebcc2368b1c02b1953eff 100644
|
|
|
+--- a/src/snapshot/embedded/embedded-file-writer.cc
|
|
|
++++ b/src/snapshot/embedded/embedded-file-writer.cc
|
|
|
+@@ -92,7 +92,7 @@ void EmbeddedFileWriter::WriteFileEpilogue(PlatformEmbeddedFileWriterBase* w,
|
|
|
+ w->Newline();
|
|
|
+ }
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ {
|
|
|
+ i::EmbeddedVector<char, kTemporaryStringLength> unwind_info_symbol;
|
|
|
+ i::SNPrintF(unwind_info_symbol, "%s_Builtins_UnwindInfo",
|
|
|
+@@ -102,7 +102,7 @@ void EmbeddedFileWriter::WriteFileEpilogue(PlatformEmbeddedFileWriterBase* w,
|
|
|
+ EmbeddedBlobDataSymbol().c_str(), blob,
|
|
|
+ reinterpret_cast<const void*>(&unwind_infos_[0]));
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ w->FileEpilogue();
|
|
|
+ }
|
|
|
+diff --git a/src/snapshot/embedded/embedded-file-writer.h b/src/snapshot/embedded/embedded-file-writer.h
|
|
|
+index c26465ae6a7d26b799ad2582784973638fbd41cd..e487b9be9bcc77e97d2da6109e1218f3fee69ce4 100644
|
|
|
+--- a/src/snapshot/embedded/embedded-file-writer.h
|
|
|
++++ b/src/snapshot/embedded/embedded-file-writer.h
|
|
|
+@@ -13,9 +13,9 @@
|
|
|
+ #include "src/snapshot/embedded/embedded-data.h"
|
|
|
+ #include "src/snapshot/embedded/platform-embedded-file-writer-base.h"
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ #include "src/diagnostics/unwinding-info-win64.h"
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ namespace v8 {
|
|
|
+ namespace internal {
|
|
|
+@@ -35,11 +35,11 @@ class EmbeddedFileWriterInterface {
|
|
|
+ // compiled builtin Code objects with trampolines.
|
|
|
+ virtual void PrepareBuiltinSourcePositionMap(Builtins* builtins) = 0;
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ virtual void SetBuiltinUnwindData(
|
|
|
+ int builtin_index,
|
|
|
+ const win64_unwindinfo::BuiltinUnwindInfo& unwinding_info) = 0;
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+ };
|
|
|
+
|
|
|
+ // Generates the embedded.S file which is later compiled into the final v8
|
|
|
+@@ -59,14 +59,14 @@ class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
|
|
|
+
|
|
|
+ void PrepareBuiltinSourcePositionMap(Builtins* builtins) override;
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ void SetBuiltinUnwindData(
|
|
|
+ int builtin_index,
|
|
|
+ const win64_unwindinfo::BuiltinUnwindInfo& unwinding_info) override {
|
|
|
+ DCHECK_LT(builtin_index, Builtins::builtin_count);
|
|
|
+ unwind_infos_[builtin_index] = unwinding_info;
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ void SetEmbeddedFile(const char* embedded_src_path) {
|
|
|
+ embedded_src_path_ = embedded_src_path;
|
|
|
+@@ -172,9 +172,6 @@ class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
|
|
|
+ const i::EmbeddedData* blob) const;
|
|
|
+
|
|
|
+ #if defined(V8_OS_WIN_X64)
|
|
|
+- std::string BuiltinsUnwindInfoLabel() const;
|
|
|
+- void WriteUnwindInfo(PlatformEmbeddedFileWriterBase* w,
|
|
|
+- const i::EmbeddedData* blob) const;
|
|
|
+ void WriteUnwindInfoEntry(PlatformEmbeddedFileWriterBase* w,
|
|
|
+ uint64_t rva_start, uint64_t rva_end) const;
|
|
|
+ #endif
|
|
|
+@@ -194,9 +191,9 @@ class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
|
|
|
+ private:
|
|
|
+ std::vector<byte> source_positions_[Builtins::builtin_count];
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ win64_unwindinfo::BuiltinUnwindInfo unwind_infos_[Builtins::builtin_count];
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ std::map<const char*, int> external_filenames_;
|
|
|
+ std::vector<const char*> external_filenames_by_index_;
|
|
|
+diff --git a/src/snapshot/embedded/platform-embedded-file-writer-win.cc b/src/snapshot/embedded/platform-embedded-file-writer-win.cc
|
|
|
+index 69457e11a5537a6500b20adb43051e25f645403f..9a9a26fbd0abef6f5b1967cdbd5fe3fcbaef4f20 100644
|
|
|
+--- a/src/snapshot/embedded/platform-embedded-file-writer-win.cc
|
|
|
++++ b/src/snapshot/embedded/platform-embedded-file-writer-win.cc
|
|
|
+@@ -6,13 +6,14 @@
|
|
|
+
|
|
|
+ #include <algorithm>
|
|
|
+
|
|
|
+-#include "src/common/globals.h" // For V8_OS_WIN_X64.
|
|
|
++#include "src/common/globals.h" // For V8_OS_WIN64
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ #include "src/builtins/builtins.h"
|
|
|
+ #include "src/diagnostics/unwinding-info-win64.h"
|
|
|
+ #include "src/snapshot/embedded/embedded-data.h"
|
|
|
+-#endif
|
|
|
++#include "src/snapshot/embedded/embedded-file-writer.h"
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ namespace v8 {
|
|
|
+ namespace internal {
|
|
|
+@@ -213,20 +214,118 @@ void EmitUnwindData(PlatformEmbeddedFileWriterWin* w,
|
|
|
+ w->EndPdataSection();
|
|
|
+ w->Newline();
|
|
|
+ }
|
|
|
+-#endif // defined(V8_OS_WIN_X64)
|
|
|
++
|
|
|
++#elif defined(V8_OS_WIN_ARM64)
|
|
|
++
|
|
|
++void EmitUnwindData(PlatformEmbeddedFileWriterWin* w,
|
|
|
++ const char* unwind_info_symbol,
|
|
|
++ const char* embedded_blob_data_symbol,
|
|
|
++ const EmbeddedData* blob,
|
|
|
++ const win64_unwindinfo::BuiltinUnwindInfo* unwind_infos) {
|
|
|
++ DCHECK(win64_unwindinfo::CanEmitUnwindInfoForBuiltins());
|
|
|
++
|
|
|
++ // Fairly arbitrary but should fit all symbol names.
|
|
|
++ static constexpr int kTemporaryStringLength = 256;
|
|
|
++ i::EmbeddedVector<char, kTemporaryStringLength> unwind_info_full_symbol;
|
|
|
++
|
|
|
++ // Emit a RUNTIME_FUNCTION (PDATA) entry for each builtin function, as
|
|
|
++ // documented here:
|
|
|
++ // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling.
|
|
|
++ w->Comment(
|
|
|
++ "pdata for all the code in the embedded blob (structs of type "
|
|
|
++ "RUNTIME_FUNCTION).");
|
|
|
++ w->Comment(" BeginAddress");
|
|
|
++ w->Comment(" UnwindInfoAddress");
|
|
|
++ w->StartPdataSection();
|
|
|
++ std::vector<int> code_chunks;
|
|
|
++ std::vector<int> fp_adjustments;
|
|
|
++
|
|
|
++ for (int i = 0; i < Builtins::builtin_count; i++) {
|
|
|
++ if (!blob->ContainsBuiltin(i)) continue;
|
|
|
++ if (unwind_infos[i].is_leaf_function()) continue;
|
|
|
++
|
|
|
++ uint64_t builtin_start_offset = blob->InstructionStartOfBuiltin(i) -
|
|
|
++ reinterpret_cast<Address>(blob->data());
|
|
|
++ uint32_t builtin_size = blob->InstructionSizeOfBuiltin(i);
|
|
|
++
|
|
|
++ const std::vector<int>& xdata_desc = unwind_infos[i].fp_offsets();
|
|
|
++ const std::vector<int>& xdata_fp_adjustments =
|
|
|
++ unwind_infos[i].fp_adjustments();
|
|
|
++ DCHECK_EQ(xdata_desc.size(), xdata_fp_adjustments.size());
|
|
|
++
|
|
|
++ for (size_t j = 0; j < xdata_desc.size(); j++) {
|
|
|
++ int chunk_start = xdata_desc[j];
|
|
|
++ int chunk_end =
|
|
|
++ (j < xdata_desc.size() - 1) ? xdata_desc[j + 1] : builtin_size;
|
|
|
++ int chunk_len = ::RoundUp(chunk_end - chunk_start, kInstrSize);
|
|
|
++
|
|
|
++ while (chunk_len > 0) {
|
|
|
++ int allowed_chunk_len =
|
|
|
++ std::min(chunk_len, win64_unwindinfo::kMaxFunctionLength);
|
|
|
++ chunk_len -= win64_unwindinfo::kMaxFunctionLength;
|
|
|
++
|
|
|
++ // Record the chunk length and fp_adjustment for emitting UNWIND_INFO
|
|
|
++ // later.
|
|
|
++ code_chunks.push_back(allowed_chunk_len);
|
|
|
++ fp_adjustments.push_back(xdata_fp_adjustments[j]);
|
|
|
++ i::SNPrintF(unwind_info_full_symbol, "%s_%u", unwind_info_symbol,
|
|
|
++ code_chunks.size());
|
|
|
++ w->DeclareRvaToSymbol(embedded_blob_data_symbol,
|
|
|
++ builtin_start_offset + chunk_start);
|
|
|
++ w->DeclareRvaToSymbol(unwind_info_full_symbol.begin());
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++ w->EndPdataSection();
|
|
|
++ w->Newline();
|
|
|
++
|
|
|
++ // Emit an UNWIND_INFO (XDATA) structs, which contains the unwinding
|
|
|
++ // information.
|
|
|
++ w->DeclareExternalFunction(CRASH_HANDLER_FUNCTION_NAME_STRING);
|
|
|
++ w->StartXdataSection();
|
|
|
++ {
|
|
|
++ for (size_t i = 0; i < code_chunks.size(); i++) {
|
|
|
++ i::SNPrintF(unwind_info_full_symbol, "%s_%u", unwind_info_symbol, i + 1);
|
|
|
++ w->DeclareLabel(unwind_info_full_symbol.begin());
|
|
|
++ std::vector<uint8_t> xdata =
|
|
|
++ win64_unwindinfo::GetUnwindInfoForBuiltinFunction(code_chunks[i],
|
|
|
++ fp_adjustments[i]);
|
|
|
++
|
|
|
++ w->IndentedDataDirective(kByte);
|
|
|
++ for (size_t j = 0; j < xdata.size(); j++) {
|
|
|
++ if (j > 0) fprintf(w->fp(), ",");
|
|
|
++ w->HexLiteral(xdata[j]);
|
|
|
++ }
|
|
|
++ w->Newline();
|
|
|
++ w->DeclareRvaToSymbol(CRASH_HANDLER_FUNCTION_NAME_STRING);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ w->EndXdataSection();
|
|
|
++ w->Newline();
|
|
|
++}
|
|
|
++
|
|
|
++#endif // V8_OS_WIN_X64
|
|
|
+
|
|
|
+ } // namespace
|
|
|
+
|
|
|
+ void PlatformEmbeddedFileWriterWin::MaybeEmitUnwindData(
|
|
|
+ const char* unwind_info_symbol, const char* embedded_blob_data_symbol,
|
|
|
+ const EmbeddedData* blob, const void* unwind_infos) {
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++// Windows ARM64 supports cross build which could require unwind info for
|
|
|
++// host_os. Ignore this case because it is only used in build time.
|
|
|
++#if defined(V8_OS_WIN_ARM64)
|
|
|
++ if (target_arch_ != EmbeddedTargetArch::kArm64) {
|
|
|
++ return;
|
|
|
++ }
|
|
|
++#endif // V8_OS_WIN_ARM64
|
|
|
++
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ if (win64_unwindinfo::CanEmitUnwindInfoForBuiltins()) {
|
|
|
+ EmitUnwindData(this, unwind_info_symbol, embedded_blob_data_symbol, blob,
|
|
|
+ reinterpret_cast<const win64_unwindinfo::BuiltinUnwindInfo*>(
|
|
|
+ unwind_infos));
|
|
|
+ }
|
|
|
+-#endif // defined(V8_OS_WIN_X64)
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+ }
|
|
|
+
|
|
|
+ // Windows, MSVC, not arm/arm64.
|
|
|
+@@ -544,6 +643,7 @@ void PlatformEmbeddedFileWriterWin::DeclareFunctionBegin(const char* name) {
|
|
|
+ if (target_arch_ == EmbeddedTargetArch::kArm64) {
|
|
|
+ // Windows ARM64 assembly is in GAS syntax, but ".type" is invalid directive
|
|
|
+ // in PE/COFF for Windows.
|
|
|
++ DeclareSymbolGlobal(name);
|
|
|
+ } else {
|
|
|
+ // The directives for inserting debugging information on Windows come
|
|
|
+ // from the PE (Portable Executable) and COFF (Common Object File Format)
|
|
|
+diff --git a/src/wasm/wasm-code-manager.cc b/src/wasm/wasm-code-manager.cc
|
|
|
+index 2eddce3d956ffcfbf5c668d5eb6e778c66959e45..9be2e75c3e9ed7bfbf31dd7cbbe9c4a634b0abc4 100644
|
|
|
+--- a/src/wasm/wasm-code-manager.cc
|
|
|
++++ b/src/wasm/wasm-code-manager.cc
|
|
|
+@@ -29,9 +29,9 @@
|
|
|
+ #include "src/wasm/wasm-objects-inl.h"
|
|
|
+ #include "src/wasm/wasm-objects.h"
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ #include "src/diagnostics/unwinding-info-win64.h"
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ #define TRACE_HEAP(...) \
|
|
|
+ do { \
|
|
|
+@@ -630,7 +630,7 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled,
|
|
|
+ CompilationState::New(*shared_this, std::move(async_counters));
|
|
|
+ DCHECK_NOT_NULL(module_);
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ // On some platforms, specifically Win64, we need to reserve some pages at
|
|
|
+ // the beginning of an executable space.
|
|
|
+ // See src/heap/spaces.cc, MemoryAllocator::InitializeCodePageAllocator() and
|
|
|
+@@ -640,7 +640,7 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled,
|
|
|
+ ->CanRegisterUnwindInfoForNonABICompliantCodeRange()) {
|
|
|
+ code_allocator_.AllocateForCode(this, Heap::GetCodeRangeReservedAreaSize());
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ uint32_t num_wasm_functions = module_->num_declared_functions;
|
|
|
+ if (num_wasm_functions > 0) {
|
|
|
+@@ -1159,21 +1159,21 @@ WasmCodeManager::WasmCodeManager(WasmMemoryTracker* memory_tracker,
|
|
|
+ size_t max_committed)
|
|
|
+ : memory_tracker_(memory_tracker),
|
|
|
+ max_committed_code_space_(max_committed),
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ is_win64_unwind_info_disabled_for_testing_(false),
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+ total_committed_code_space_(0),
|
|
|
+ critical_committed_code_space_(max_committed / 2) {
|
|
|
+ DCHECK_LE(max_committed, kMaxWasmCodeMemory);
|
|
|
+ }
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ bool WasmCodeManager::CanRegisterUnwindInfoForNonABICompliantCodeRange() const {
|
|
|
+ return win64_unwindinfo::CanRegisterUnwindInfoForNonABICompliantCodeRange() &&
|
|
|
+ FLAG_win64_unwinding_info &&
|
|
|
+ !is_win64_unwind_info_disabled_for_testing_;
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ bool WasmCodeManager::Commit(base::AddressRegion region) {
|
|
|
+ // TODO(v8:8462): Remove eager commit once perf supports remapping.
|
|
|
+@@ -1341,12 +1341,12 @@ std::shared_ptr<NativeModule> WasmCodeManager::NewNativeModule(
|
|
|
+ TRACE_HEAP("New NativeModule %p: Mem: %" PRIuPTR ",+%zu\n", ret.get(), start,
|
|
|
+ size);
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ if (CanRegisterUnwindInfoForNonABICompliantCodeRange()) {
|
|
|
+ win64_unwindinfo::RegisterNonABICompliantCodeRange(
|
|
|
+ reinterpret_cast<void*>(start), size);
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ base::MutexGuard lock(&native_modules_mutex_);
|
|
|
+ lookup_map_.insert(std::make_pair(start, std::make_pair(end, ret.get())));
|
|
|
+@@ -1460,12 +1460,12 @@ void WasmCodeManager::FreeNativeModule(Vector<VirtualMemory> owned_code_space,
|
|
|
+ TRACE_HEAP("VMem Release: 0x%" PRIxPTR ":0x%" PRIxPTR " (%zu)\n",
|
|
|
+ code_space.address(), code_space.end(), code_space.size());
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ if (CanRegisterUnwindInfoForNonABICompliantCodeRange()) {
|
|
|
+ win64_unwindinfo::UnregisterNonABICompliantCodeRange(
|
|
|
+ reinterpret_cast<void*>(code_space.address()));
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ lookup_map_.erase(code_space.address());
|
|
|
+ memory_tracker_->ReleaseReservation(code_space.size());
|
|
|
+diff --git a/src/wasm/wasm-code-manager.h b/src/wasm/wasm-code-manager.h
|
|
|
+index 49c287df2c8efa718e2599206e069c26fc03b255..1fd00743996acae7b6c84517baa512e15bc32386 100644
|
|
|
+--- a/src/wasm/wasm-code-manager.h
|
|
|
++++ b/src/wasm/wasm-code-manager.h
|
|
|
+@@ -605,9 +605,9 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ bool CanRegisterUnwindInfoForNonABICompliantCodeRange() const;
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ NativeModule* LookupNativeModule(Address pc) const;
|
|
|
+ WasmCode* LookupCode(Address pc) const;
|
|
|
+@@ -617,11 +617,11 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
|
|
|
+
|
|
|
+ void SetMaxCommittedMemoryForTesting(size_t limit);
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ void DisableWin64UnwindInfoForTesting() {
|
|
|
+ is_win64_unwind_info_disabled_for_testing_ = true;
|
|
|
+ }
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ static size_t EstimateNativeModuleCodeSize(const WasmModule* module);
|
|
|
+ static size_t EstimateNativeModuleNonCodeSize(const WasmModule* module);
|
|
|
+@@ -649,9 +649,9 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
|
|
|
+
|
|
|
+ size_t max_committed_code_space_;
|
|
|
+
|
|
|
+-#if defined(V8_OS_WIN_X64)
|
|
|
++#if defined(V8_OS_WIN64)
|
|
|
+ bool is_win64_unwind_info_disabled_for_testing_;
|
|
|
+-#endif
|
|
|
++#endif // V8_OS_WIN64
|
|
|
+
|
|
|
+ std::atomic<size_t> total_committed_code_space_;
|
|
|
+ // If the committed code space exceeds {critical_committed_code_space_}, then
|
|
|
+diff --git a/test/cctest/BUILD.gn b/test/cctest/BUILD.gn
|
|
|
+index 42396087eedd844b07f8ca842ce1a0c17d8a94f7..f2d5693793da6d45593b3f18712e959fcac8eecf 100644
|
|
|
+--- a/test/cctest/BUILD.gn
|
|
|
++++ b/test/cctest/BUILD.gn
|
|
|
+@@ -310,6 +310,9 @@ v8_source_set("cctest_sources") {
|
|
|
+ "test-utils-arm64.cc",
|
|
|
+ "test-utils-arm64.h",
|
|
|
+ ]
|
|
|
++ if (is_win) {
|
|
|
++ sources += [ "test-stack-unwinding-win64.cc" ]
|
|
|
++ }
|
|
|
+ } else if (v8_current_cpu == "x86") {
|
|
|
+ sources += [ ### gcmole(arch:ia32) ###
|
|
|
+ "test-assembler-ia32.cc",
|
|
|
+@@ -348,7 +351,7 @@ v8_source_set("cctest_sources") {
|
|
|
+ "test-macro-assembler-x64.cc",
|
|
|
+ ]
|
|
|
+ if (is_win) {
|
|
|
+- sources += [ "test-stack-unwinding-x64.cc" ]
|
|
|
++ sources += [ "test-stack-unwinding-win64.cc" ]
|
|
|
+ }
|
|
|
+ } else if (v8_current_cpu == "ppc" || v8_current_cpu == "ppc64") {
|
|
|
+ sources += [ ### gcmole(arch:ppc) ###
|
|
|
+diff --git a/test/cctest/test-stack-unwinding-x64.cc b/test/cctest/test-stack-unwinding-win64.cc
|
|
|
+similarity index 76%
|
|
|
+rename from test/cctest/test-stack-unwinding-x64.cc
|
|
|
+rename to test/cctest/test-stack-unwinding-win64.cc
|
|
|
+index 583e14111ad676cf7cb9984407ca9cd0bf9f5f7c..84f1318a29525ae5c71275ecaa6096dfaabcc90c 100644
|
|
|
+--- a/test/cctest/test-stack-unwinding-x64.cc
|
|
|
++++ b/test/cctest/test-stack-unwinding-win64.cc
|
|
|
+@@ -6,9 +6,15 @@
|
|
|
+ #include "src/init/v8.h"
|
|
|
+ #include "test/cctest/cctest.h"
|
|
|
+
|
|
|
+-class UnwindingWinX64Callbacks {
|
|
|
++#if defined(V8_OS_WIN_X64)
|
|
|
++#define CONTEXT_PC(context) (context.Rip)
|
|
|
++#elif defined(V8_OS_WIN_ARM64)
|
|
|
++#define CONTEXT_PC(context) (context.Pc)
|
|
|
++#endif
|
|
|
++
|
|
|
++class UnwindingWin64Callbacks {
|
|
|
+ public:
|
|
|
+- UnwindingWinX64Callbacks() = default;
|
|
|
++ UnwindingWin64Callbacks() = default;
|
|
|
+
|
|
|
+ static void Getter(v8::Local<v8::String> name,
|
|
|
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
+@@ -31,25 +37,26 @@ class UnwindingWinX64Callbacks {
|
|
|
+ int iframe = 0;
|
|
|
+ while (++iframe < max_frames) {
|
|
|
+ uint64_t image_base;
|
|
|
+- PRUNTIME_FUNCTION function_entry =
|
|
|
+- ::RtlLookupFunctionEntry(context_record.Rip, &image_base, nullptr);
|
|
|
++ PRUNTIME_FUNCTION function_entry = ::RtlLookupFunctionEntry(
|
|
|
++ CONTEXT_PC(context_record), &image_base, nullptr);
|
|
|
+ if (!function_entry) break;
|
|
|
+
|
|
|
+ void* handler_data;
|
|
|
+ uint64_t establisher_frame;
|
|
|
+- ::RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, context_record.Rip,
|
|
|
+- function_entry, &context_record, &handler_data,
|
|
|
+- &establisher_frame, NULL);
|
|
|
++ ::RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base,
|
|
|
++ CONTEXT_PC(context_record), function_entry,
|
|
|
++ &context_record, &handler_data, &establisher_frame,
|
|
|
++ NULL);
|
|
|
+ }
|
|
|
+ return iframe;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+-// Verifies that stack unwinding data has been correctly registered on Win/x64.
|
|
|
+-UNINITIALIZED_TEST(StackUnwindingWinX64) {
|
|
|
++// Verifies that stack unwinding data has been correctly registered on Win64.
|
|
|
++UNINITIALIZED_TEST(StackUnwindingWin64) {
|
|
|
+ #ifdef V8_WIN64_UNWINDING_INFO
|
|
|
+
|
|
|
+- static const char* unwinding_win_x64_test_source =
|
|
|
++ static const char* unwinding_win64_test_source =
|
|
|
+ "function start(count) {\n"
|
|
|
+ " for (var i = 0; i < count; i++) {\n"
|
|
|
+ " var o = instance.foo;\n"
|
|
|
+@@ -79,18 +86,18 @@ UNINITIALIZED_TEST(StackUnwindingWinX64) {
|
|
|
+ v8::Local<v8::ObjectTemplate> instance_template =
|
|
|
+ func_template->InstanceTemplate();
|
|
|
+
|
|
|
+- UnwindingWinX64Callbacks accessors;
|
|
|
++ UnwindingWin64Callbacks accessors;
|
|
|
+ v8::Local<v8::External> data = v8::External::New(isolate, &accessors);
|
|
|
+ instance_template->SetAccessor(v8_str("foo"),
|
|
|
+- &UnwindingWinX64Callbacks::Getter,
|
|
|
+- &UnwindingWinX64Callbacks::Setter, data);
|
|
|
++ &UnwindingWin64Callbacks::Getter,
|
|
|
++ &UnwindingWin64Callbacks::Setter, data);
|
|
|
+ v8::Local<v8::Function> func =
|
|
|
+ func_template->GetFunction(env.local()).ToLocalChecked();
|
|
|
+ v8::Local<v8::Object> instance =
|
|
|
+ func->NewInstance(env.local()).ToLocalChecked();
|
|
|
+ env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
|
|
|
+
|
|
|
+- CompileRun(unwinding_win_x64_test_source);
|
|
|
++ CompileRun(unwinding_win64_test_source);
|
|
|
+ v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
|
|
|
+ env->Global()->Get(env.local(), v8_str("start")).ToLocalChecked());
|
|
|
+
|
|
|
+@@ -106,3 +113,5 @@ UNINITIALIZED_TEST(StackUnwindingWinX64) {
|
|
|
+
|
|
|
+ #endif // V8_WIN64_UNWINDING_INFO
|
|
|
+ }
|
|
|
++
|
|
|
++#undef CONTEXT_PC
|