blob: 361935bd8be09dceeec8be0159d7fcbd11ae686a [file] [log] [blame]
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/diagnostics/unwinding-info-win64.h"
#include "src/codegen/macro-assembler.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 {
bool CanEmitUnwindInfoForBuiltins() { return FLAG_win64_unwinding_info; }
bool CanRegisterUnwindInfoForNonABICompliantCodeRange() {
return !FLAG_jitless;
}
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)
/*
* From Windows SDK ehdata.h, which does not compile with Clang.
* See https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx.
*/
union UNWIND_CODE {
struct {
unsigned char CodeOffset;
unsigned char UnwindOp : 4;
unsigned char OpInfo : 4;
};
uint16_t FrameOffset;
};
struct UNWIND_INFO {
unsigned char Version : 3;
unsigned char Flags : 5;
unsigned char SizeOfProlog;
unsigned char CountOfCodes;
unsigned char FrameRegister : 4;
unsigned char FrameOffset : 4;
};
static constexpr int kNumberOfUnwindCodes = 2;
static constexpr int kMaxExceptionThunkSize = 12;
struct V8UnwindData {
UNWIND_INFO unwind_info;
UNWIND_CODE unwind_codes[kNumberOfUnwindCodes];
V8UnwindData() {
static constexpr int kOpPushNonvol = 0;
static constexpr int kOpSetFPReg = 3;
unwind_info.Version = 1;
unwind_info.Flags = UNW_FLAG_EHANDLER;
unwind_info.SizeOfProlog = kRbpPrefixLength;
unwind_info.CountOfCodes = kRbpPrefixCodes;
unwind_info.FrameRegister = rbp.code();
unwind_info.FrameOffset = 0;
unwind_codes[0].CodeOffset = kRbpPrefixLength; // movq rbp, rsp
unwind_codes[0].UnwindOp = kOpSetFPReg;
unwind_codes[0].OpInfo = 0;
unwind_codes[1].CodeOffset = kPushRbpInstructionLength; // push rbp
unwind_codes[1].UnwindOp = kOpPushNonvol;
unwind_codes[1].OpInfo = rbp.code();
}
};
struct ExceptionHandlerUnwindData {
UNWIND_INFO unwind_info;
ExceptionHandlerUnwindData() {
unwind_info.Version = 1;
unwind_info.Flags = UNW_FLAG_EHANDLER;
unwind_info.SizeOfProlog = 0;
unwind_info.CountOfCodes = 0;
unwind_info.FrameRegister = 0;
unwind_info.FrameOffset = 0;
}
};
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)
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[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());
}
#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,
OpAllocS = 0x00,
OpSaveFpLr = 0x40,
OpSaveFpLrX = 0x80,
OpSetFp = 0xE1,
OpAddFp = 0xE2,
OpEnd = 0xE4,
};
typedef uint32_t UNWIND_CODE;
constexpr UNWIND_CODE Combine8BitUnwindCodes(uint8_t code0 = OpNop,
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);
}
// 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 kDefaultNumberOfUnwindCodeWords = 1;
static constexpr int kMaxExceptionThunkSize = 16;
static constexpr int kFunctionLengthShiftSize = 2;
static constexpr int kFunctionLengthMask = (1 << kFunctionLengthShiftSize) - 1;
static constexpr int kAllocStackShiftSize = 4;
static constexpr int kAllocStackShiftMask = (1 << kAllocStackShiftSize) - 1;
// Generate an unwind code for "stp fp, lr, [sp, #pre_index_offset]!".
uint8_t MakeOpSaveFpLrX(int pre_index_offset) {
// See unwind code save_fplr_x in
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
DCHECK_LE(pre_index_offset, -8);
DCHECK_GE(pre_index_offset, -512);
constexpr int kShiftSize = 3;
constexpr int kShiftMask = (1 << kShiftSize) - 1;
DCHECK_EQ(pre_index_offset & kShiftMask, 0);
USE(kShiftMask);
// Solve for Z where -(Z+1)*8 = pre_index_offset.
int encoded_value = (-pre_index_offset >> kShiftSize) - 1;
return OpSaveFpLrX | encoded_value;
}
// Generate an unwind code for "sub sp, sp, #stack_space".
uint8_t MakeOpAllocS(int stack_space) {
// See unwind code alloc_s in
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
DCHECK_GE(stack_space, 0);
DCHECK_LT(stack_space, 512);
DCHECK_EQ(stack_space & kAllocStackShiftMask, 0);
return OpAllocS | (stack_space >> kAllocStackShiftSize);
}
// Generate the second byte of the unwind code for "add fp, sp, #offset".
uint8_t MakeOpAddFpArgument(int offset) {
// See unwind code add_fp in
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
DCHECK_GE(offset, 0);
constexpr int kShiftSize = 3;
constexpr int kShiftMask = (1 << kShiftSize) - 1;
DCHECK_EQ(offset & kShiftMask, 0);
USE(kShiftMask);
int encoded_value = offset >> kShiftSize;
// Encoded value must fit in 8 bits.
DCHECK_LE(encoded_value, 0xff);
return encoded_value;
}
template <int kNumberOfUnwindCodeWords = kDefaultNumberOfUnwindCodeWords>
struct V8UnwindData {
UNWIND_INFO unwind_info;
UNWIND_CODE unwind_codes[kNumberOfUnwindCodeWords];
V8UnwindData() {
memset(&unwind_info, 0, sizeof(UNWIND_INFO));
unwind_info.X = 1; // has exception handler after unwind-codes.
unwind_info.CodeWords = kNumberOfUnwindCodeWords;
// Generate unwind codes for the following prolog:
//
// stp fp, lr, [sp, #-kCallerSPOffset]!
// mov fp, sp
//
// This is a very rough approximation of the actual function prologs used in
// V8. In particular, we often push other data before the (fp, lr) pair,
// meaning the stack pointer computed for the caller frame is wrong. That
// error is acceptable when the unwinding info for the caller frame also
// depends on fp rather than sp, as is the case for V8 builtins and runtime-
// generated code.
STATIC_ASSERT(kNumberOfUnwindCodeWords >= 1);
unwind_codes[0] = Combine8BitUnwindCodes(
OpSetFp, MakeOpSaveFpLrX(-CommonFrameConstants::kCallerSPOffset),
OpEnd);
// Fill the rest with nops.
for (int i = 1; i < kNumberOfUnwindCodeWords; ++i) {
unwind_codes[i] = Combine8BitUnwindCodes();
}
}
};
struct CodeRangeUnwindingRecord {
void* dynamic_table;
uint32_t runtime_function_count;
V8UnwindData<> 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)
FrameOffsets::FrameOffsets()
: fp_to_saved_caller_fp(CommonFrameConstants::kCallerFPOffset),
fp_to_caller_sp(CommonFrameConstants::kCallerSPOffset) {}
bool FrameOffsets::IsDefault() const {
FrameOffsets other;
return fp_to_saved_caller_fp == other.fp_to_saved_caller_fp &&
fp_to_caller_sp == other.fp_to_caller_sp;
}
std::vector<uint8_t> GetUnwindInfoForBuiltinFunction(
uint32_t func_len, FrameOffsets fp_adjustment) {
DCHECK_LE(func_len, kMaxFunctionLength);
DCHECK_EQ((func_len & kFunctionLengthMask), 0);
USE(kFunctionLengthMask);
// The largest size of unwind data required for all options below.
constexpr int kMaxNumberOfUnwindCodeWords = 2;
V8UnwindData<kMaxNumberOfUnwindCodeWords> 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;
if (fp_adjustment.IsDefault()) {
// One code word is plenty.
STATIC_ASSERT(kDefaultNumberOfUnwindCodeWords <
kMaxNumberOfUnwindCodeWords);
xdata.unwind_info.CodeWords = kDefaultNumberOfUnwindCodeWords;
} else {
// We want to convey the following facts:
// 1. The caller's fp is found at [fp + fp_to_saved_caller_fp].
// 2. The caller's pc is found at [fp + fp_to_saved_caller_fp + 8].
// 3. The caller's sp is equal to fp + fp_to_caller_sp.
//
// An imaginary prolog that would establish those relationships might look
// like the following, with appropriate values for the various constants:
//
// stp fp, lr, [sp, #pre_index_amount]!
// sub sp, sp, #stack_space
// add fp, sp, offset_from_stack_top
//
// Why do we need offset_from_stack_top? The unwinding encoding for
// allocating stack space has 16-byte granularity, and the frame pointer has
// only 8-byte alignment.
int pre_index_amount =
fp_adjustment.fp_to_saved_caller_fp - fp_adjustment.fp_to_caller_sp;
int stack_space = fp_adjustment.fp_to_saved_caller_fp;
int offset_from_stack_top = stack_space & kAllocStackShiftMask;
stack_space += offset_from_stack_top;
xdata.unwind_codes[0] = Combine8BitUnwindCodes(
OpAddFp, MakeOpAddFpArgument(offset_from_stack_top),
MakeOpAllocS(stack_space), MakeOpSaveFpLrX(pre_index_amount));
xdata.unwind_codes[1] = Combine8BitUnwindCodes(OpEnd);
}
return std::vector<uint8_t>(
reinterpret_cast<uint8_t*>(&xdata),
reinterpret_cast<uint8_t*>(
&xdata.unwind_codes[xdata.unwind_info.CodeWords]));
}
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 {
// Many codes here are disabled because Cobalt's Windows host build does not
// support RtlAddGrowableFunctionTable and RtlDeleteGrowableFunctionTable yet
// and Cobalt doesn't need them unwinding data.
#if !defined(DISABLE_UNWIND_STARBOARD)
V8_DECLARE_ONCE(load_ntdll_unwinding_functions_once);
static decltype(
&::RtlAddGrowableFunctionTable) add_growable_function_table_func = nullptr;
static decltype(
&::RtlDeleteGrowableFunctionTable) delete_growable_function_table_func =
nullptr;
#endif
void LoadNtdllUnwindingFunctions() {
#if !defined(DISABLE_UNWIND_STARBOARD)
base::CallOnce(&load_ntdll_unwinding_functions_once, []() {
// Load functions from the ntdll.dll module.
HMODULE ntdll_module =
LoadLibraryEx(L"ntdll.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
DCHECK_NOT_NULL(ntdll_module);
// This fails on Windows 7.
add_growable_function_table_func =
reinterpret_cast<decltype(&::RtlAddGrowableFunctionTable)>(
::GetProcAddress(ntdll_module, "RtlAddGrowableFunctionTable"));
DCHECK_IMPLIES(IsWindows8OrGreater(), add_growable_function_table_func);
delete_growable_function_table_func =
reinterpret_cast<decltype(&::RtlDeleteGrowableFunctionTable)>(
::GetProcAddress(ntdll_module, "RtlDeleteGrowableFunctionTable"));
DCHECK_IMPLIES(IsWindows8OrGreater(), delete_growable_function_table_func);
});
#endif
}
bool AddGrowableFunctionTable(PVOID* DynamicTable,
PRUNTIME_FUNCTION FunctionTable, DWORD EntryCount,
DWORD MaximumEntryCount, ULONG_PTR RangeBase,
ULONG_PTR RangeEnd) {
#if !defined(DISABLE_UNWIND_STARBOARD)
DCHECK(::IsWindows8OrGreater());
LoadNtdllUnwindingFunctions();
DCHECK_NOT_NULL(add_growable_function_table_func);
*DynamicTable = nullptr;
DWORD status =
add_growable_function_table_func(DynamicTable, FunctionTable, EntryCount,
MaximumEntryCount, RangeBase, RangeEnd);
DCHECK((status == 0 && *DynamicTable != nullptr) ||
status == 0xC000009A); // STATUS_INSUFFICIENT_RESOURCES
return (status == 0);
#else
return false;
#endif
}
void DeleteGrowableFunctionTable(PVOID dynamic_table) {
#if !defined(DISABLE_UNWIND_STARBOARD)
DCHECK(::IsWindows8OrGreater());
LoadNtdllUnwindingFunctions();
DCHECK_NOT_NULL(delete_growable_function_table_func);
delete_growable_function_table_func(dynamic_table);
#endif
}
} // namespace
void RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) {
#if !defined(DISABLE_UNWIND_STARBOARD)
DCHECK(CanRegisterUnwindInfoForNonABICompliantCodeRange());
// When the --win64-unwinding-info flag is set, we call
// RtlAddGrowableFunctionTable to register unwinding info for the whole code
// range of an isolate or Wasm module. This enables the Windows OS stack
// unwinder to work correctly with V8-generated code, enabling stack walking
// in Windows debuggers and performance tools. However, the
// RtlAddGrowableFunctionTable API is only supported on Windows 8 and above.
//
// On Windows 7, or when --win64-unwinding-info is not set, we may still need
// to call RtlAddFunctionTable to register a custom exception handler passed
// 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,
kDefaultRuntimeFunctionCount,
reinterpret_cast<DWORD64>(start)));
// Protect reserved page against modifications.
DWORD old_protect;
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,
record->runtime_function_count, record->runtime_function_count,
reinterpret_cast<DWORD64>(start),
reinterpret_cast<DWORD64>(reinterpret_cast<uint8_t*>(start) +
size_in_bytes)));
// Protect reserved page against modifications.
DWORD old_protect;
CHECK(VirtualProtect(start, sizeof(CodeRangeUnwindingRecord),
PAGE_EXECUTE_READ, &old_protect));
}
#endif
}
void UnregisterNonABICompliantCodeRange(void* start) {
#if !defined(DISABLE_UNWIND_STARBOARD)
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));
}
#endif // V8_OS_WIN_X64
} else {
CodeRangeUnwindingRecord* record =
reinterpret_cast<CodeRangeUnwindingRecord*>(start);
if (record->dynamic_table) {
DeleteGrowableFunctionTable(record->dynamic_table);
}
}
#endif
}
#if defined(V8_OS_WIN_X64)
void XdataEncoder::onPushRbp() {
current_frame_code_offset_ =
assembler_.pc_offset() - kPushRbpInstructionLength;
}
void XdataEncoder::onMovRbpRsp() {
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_);
current_frame_adjustment_ = FrameOffsets();
}
void XdataEncoder::onFramePointerAdjustment(int fp_to_saved_caller_fp,
int fp_to_caller_sp) {
current_frame_adjustment_.fp_to_saved_caller_fp = fp_to_saved_caller_fp;
current_frame_adjustment_.fp_to_caller_sp = fp_to_caller_sp;
}
#endif // V8_OS_WIN_X64
} // namespace win64_unwindinfo
} // namespace internal
} // namespace v8