Commit 3f1f001a authored by Tom Tan's avatar Tom Tan Committed by Commit Bot

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/+/1701133Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63002}
parent 84748f5b
......@@ -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).
......@@ -3123,6 +3123,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(mostynb@vewd.com): fix this code so it doesn't need
# to be excluded, see the comments inside.
......
......@@ -122,9 +122,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 {
......@@ -5588,14 +5588,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);
......
......@@ -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.
......
......@@ -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;
}
......
......@@ -327,6 +327,12 @@ Assembler::Assembler(const AssemblerOptions& options,
constpool_(this) {
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() {
......@@ -349,6 +355,14 @@ void Assembler::Reset() {
next_veneer_pool_check_ = kMaxInt;
}
#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_) {
......@@ -1179,6 +1193,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,
......
......@@ -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 {
......@@ -2416,6 +2420,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;
......@@ -2686,6 +2698,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;
......
......@@ -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);
......
......@@ -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
......
......@@ -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 {
......
......@@ -412,12 +412,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);
......
This diff is collapsed.
......@@ -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_
......@@ -86,9 +86,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_;
......@@ -2962,7 +2962,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 =
......@@ -2970,7 +2970,7 @@ void Isolate::Deinit() {
void* start = reinterpret_cast<void*>(code_range.begin());
win64_unwindinfo::UnregisterNonABICompliantCodeRange(start);
}
#endif
#endif // V8_OS_WIN64
debug()->Unload();
......@@ -3554,7 +3554,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();
......@@ -3562,7 +3562,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();
......@@ -4411,7 +4411,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) {
......@@ -4419,7 +4419,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;
......
......@@ -1502,11 +1502,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>,
......
......@@ -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();
}
......
......@@ -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_;
......
......@@ -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)
......
......@@ -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 { \
......@@ -643,7 +643,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
......@@ -653,7 +653,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) {
......@@ -1187,21 +1187,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.
......@@ -1369,12 +1369,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())));
......@@ -1487,12 +1487,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());
......
......@@ -610,9 +610,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;
......@@ -622,11 +622,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);
......@@ -654,9 +654,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
......
......@@ -319,6 +319,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",
......@@ -357,7 +360,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) ###
......
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment