• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/diagnostics/unwinding-info-win64.h"
6 
7 #include "src/codegen/macro-assembler.h"
8 #include "src/utils/allocation.h"
9 
10 #if defined(V8_OS_WIN_X64)
11 #include "src/codegen/x64/assembler-x64.h"
12 #elif defined(V8_OS_WIN_ARM64)
13 #include "src/codegen/arm64/assembler-arm64-inl.h"
14 #include "src/codegen/arm64/macro-assembler-arm64-inl.h"
15 #else
16 #error "Unsupported OS"
17 #endif  // V8_OS_WIN_X64
18 
19 namespace v8 {
20 namespace internal {
21 namespace win64_unwindinfo {
22 
CanEmitUnwindInfoForBuiltins()23 bool CanEmitUnwindInfoForBuiltins() { return FLAG_win64_unwinding_info; }
24 
CanRegisterUnwindInfoForNonABICompliantCodeRange()25 bool CanRegisterUnwindInfoForNonABICompliantCodeRange() {
26   return !FLAG_jitless;
27 }
28 
RegisterUnwindInfoForExceptionHandlingOnly()29 bool RegisterUnwindInfoForExceptionHandlingOnly() {
30   DCHECK(CanRegisterUnwindInfoForNonABICompliantCodeRange());
31 #if defined(V8_OS_WIN_ARM64)
32   return !FLAG_win64_unwinding_info;
33 #else
34   return !IsWindows8OrGreater() || !FLAG_win64_unwinding_info;
35 #endif
36 }
37 
38 v8::UnhandledExceptionCallback unhandled_exception_callback_g = nullptr;
39 
SetUnhandledExceptionCallback(v8::UnhandledExceptionCallback unhandled_exception_callback)40 void SetUnhandledExceptionCallback(
41     v8::UnhandledExceptionCallback unhandled_exception_callback) {
42   unhandled_exception_callback_g = unhandled_exception_callback;
43 }
44 
45 // This function is registered as exception handler for V8-generated code as
46 // part of the registration of unwinding info. It is referenced by
47 // RegisterNonABICompliantCodeRange(), below, and by the unwinding info for
48 // builtins declared in the embedded blob.
CRASH_HANDLER_FUNCTION_NAME(PEXCEPTION_RECORD ExceptionRecord,ULONG64 EstablisherFrame,PCONTEXT ContextRecord,PDISPATCHER_CONTEXT DispatcherContext)49 extern "C" __declspec(dllexport) int CRASH_HANDLER_FUNCTION_NAME(
50     PEXCEPTION_RECORD ExceptionRecord, ULONG64 EstablisherFrame,
51     PCONTEXT ContextRecord, PDISPATCHER_CONTEXT DispatcherContext) {
52   if (unhandled_exception_callback_g != nullptr) {
53     EXCEPTION_POINTERS info = {ExceptionRecord, ContextRecord};
54     return unhandled_exception_callback_g(&info);
55   }
56   return ExceptionContinueSearch;
57 }
58 
59 #if defined(V8_OS_WIN_X64)
60 
61 #pragma pack(push, 1)
62 
63 /*
64  * From Windows SDK ehdata.h, which does not compile with Clang.
65  * See https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx.
66  */
67 union UNWIND_CODE {
68   struct {
69     unsigned char CodeOffset;
70     unsigned char UnwindOp : 4;
71     unsigned char OpInfo : 4;
72   };
73   uint16_t FrameOffset;
74 };
75 
76 struct UNWIND_INFO {
77   unsigned char Version : 3;
78   unsigned char Flags : 5;
79   unsigned char SizeOfProlog;
80   unsigned char CountOfCodes;
81   unsigned char FrameRegister : 4;
82   unsigned char FrameOffset : 4;
83 };
84 
85 static constexpr int kNumberOfUnwindCodes = 2;
86 static constexpr int kMaxExceptionThunkSize = 12;
87 
88 struct V8UnwindData {
89   UNWIND_INFO unwind_info;
90   UNWIND_CODE unwind_codes[kNumberOfUnwindCodes];
91 
V8UnwindDatav8::internal::win64_unwindinfo::V8UnwindData92   V8UnwindData() {
93     static constexpr int kOpPushNonvol = 0;
94     static constexpr int kOpSetFPReg = 3;
95 
96     unwind_info.Version = 1;
97     unwind_info.Flags = UNW_FLAG_EHANDLER;
98     unwind_info.SizeOfProlog = kRbpPrefixLength;
99     unwind_info.CountOfCodes = kRbpPrefixCodes;
100     unwind_info.FrameRegister = rbp.code();
101     unwind_info.FrameOffset = 0;
102 
103     unwind_codes[0].CodeOffset = kRbpPrefixLength;  // movq rbp, rsp
104     unwind_codes[0].UnwindOp = kOpSetFPReg;
105     unwind_codes[0].OpInfo = 0;
106 
107     unwind_codes[1].CodeOffset = kPushRbpInstructionLength;  // push rbp
108     unwind_codes[1].UnwindOp = kOpPushNonvol;
109     unwind_codes[1].OpInfo = rbp.code();
110   }
111 };
112 
113 struct ExceptionHandlerUnwindData {
114   UNWIND_INFO unwind_info;
115 
ExceptionHandlerUnwindDatav8::internal::win64_unwindinfo::ExceptionHandlerUnwindData116   ExceptionHandlerUnwindData() {
117     unwind_info.Version = 1;
118     unwind_info.Flags = UNW_FLAG_EHANDLER;
119     unwind_info.SizeOfProlog = 0;
120     unwind_info.CountOfCodes = 0;
121     unwind_info.FrameRegister = 0;
122     unwind_info.FrameOffset = 0;
123   }
124 };
125 
126 struct CodeRangeUnwindingRecord {
127   void* dynamic_table;
128   uint32_t runtime_function_count;
129   V8UnwindData unwind_info;
130   uint32_t exception_handler;
131   uint8_t exception_thunk[kMaxExceptionThunkSize];
132   RUNTIME_FUNCTION runtime_function[kDefaultRuntimeFunctionCount];
133 };
134 
135 struct ExceptionHandlerRecord {
136   uint32_t runtime_function_count;
137   RUNTIME_FUNCTION runtime_function[kDefaultRuntimeFunctionCount];
138   ExceptionHandlerUnwindData unwind_info;
139   uint32_t exception_handler;
140   uint8_t exception_thunk[kMaxExceptionThunkSize];
141 };
142 
143 #pragma pack(pop)
144 
GetUnwindInfoForBuiltinFunctions()145 std::vector<uint8_t> GetUnwindInfoForBuiltinFunctions() {
146   V8UnwindData xdata;
147   return std::vector<uint8_t>(
148       reinterpret_cast<uint8_t*>(&xdata),
149       reinterpret_cast<uint8_t*>(&xdata) + sizeof(xdata));
150 }
151 
152 template <typename Record>
InitUnwindingRecord(Record * record,size_t code_size_in_bytes)153 void InitUnwindingRecord(Record* record, size_t code_size_in_bytes) {
154   // We assume that the first page of the code range is executable and
155   // committed and reserved to contain PDATA/XDATA.
156 
157   // All addresses are 32bit relative offsets to start.
158   record->runtime_function[0].BeginAddress = 0;
159   record->runtime_function[0].EndAddress =
160       static_cast<DWORD>(code_size_in_bytes);
161   record->runtime_function[0].UnwindData = offsetof(Record, unwind_info);
162   record->runtime_function_count = 1;
163   record->exception_handler = offsetof(Record, exception_thunk);
164 
165   // Hardcoded thunk.
166   AssemblerOptions options;
167   options.record_reloc_info_for_serialization = false;
168   MacroAssembler masm(nullptr, options, CodeObjectRequired::kNo,
169                       NewAssemblerBuffer(64));
170   masm.movq(rax, reinterpret_cast<uint64_t>(&CRASH_HANDLER_FUNCTION_NAME));
171   masm.jmp(rax);
172   DCHECK_LE(masm.instruction_size(), sizeof(record->exception_thunk));
173   memcpy(&record->exception_thunk[0], masm.buffer_start(),
174          masm.instruction_size());
175 }
176 
177 #elif defined(V8_OS_WIN_ARM64)
178 
179 #pragma pack(push, 1)
180 
181 // ARM64 unwind codes are defined in below doc.
182 // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
183 enum UnwindOp8Bit {
184   OpNop = 0xE3,
185   OpAllocS = 0x00,
186   OpSaveFpLr = 0x40,
187   OpSaveFpLrX = 0x80,
188   OpSetFp = 0xE1,
189   OpAddFp = 0xE2,
190   OpEnd = 0xE4,
191 };
192 
193 typedef uint32_t UNWIND_CODE;
194 
Combine8BitUnwindCodes(uint8_t code0=OpNop,uint8_t code1=OpNop,uint8_t code2=OpNop,uint8_t code3=OpNop)195 constexpr UNWIND_CODE Combine8BitUnwindCodes(uint8_t code0 = OpNop,
196                                              uint8_t code1 = OpNop,
197                                              uint8_t code2 = OpNop,
198                                              uint8_t code3 = OpNop) {
199   return static_cast<uint32_t>(code0) | (static_cast<uint32_t>(code1) << 8) |
200          (static_cast<uint32_t>(code2) << 16) |
201          (static_cast<uint32_t>(code3) << 24);
202 }
203 
204 // UNWIND_INFO defines the static part (first 32-bit) of the .xdata record in
205 // below doc.
206 // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#xdata-records
207 struct UNWIND_INFO {
208   uint32_t FunctionLength : 18;
209   uint32_t Version : 2;
210   uint32_t X : 1;
211   uint32_t E : 1;
212   uint32_t EpilogCount : 5;
213   uint32_t CodeWords : 5;
214 };
215 
216 static constexpr int kDefaultNumberOfUnwindCodeWords = 1;
217 static constexpr int kMaxExceptionThunkSize = 16;
218 static constexpr int kFunctionLengthShiftSize = 2;
219 static constexpr int kFunctionLengthMask = (1 << kFunctionLengthShiftSize) - 1;
220 static constexpr int kAllocStackShiftSize = 4;
221 static constexpr int kAllocStackShiftMask = (1 << kAllocStackShiftSize) - 1;
222 
223 // Generate an unwind code for "stp fp, lr, [sp, #pre_index_offset]!".
MakeOpSaveFpLrX(int pre_index_offset)224 uint8_t MakeOpSaveFpLrX(int pre_index_offset) {
225   // See unwind code save_fplr_x in
226   // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
227   DCHECK_LE(pre_index_offset, -8);
228   DCHECK_GE(pre_index_offset, -512);
229   constexpr int kShiftSize = 3;
230   constexpr int kShiftMask = (1 << kShiftSize) - 1;
231   DCHECK_EQ(pre_index_offset & kShiftMask, 0);
232   USE(kShiftMask);
233   // Solve for Z where -(Z+1)*8 = pre_index_offset.
234   int encoded_value = (-pre_index_offset >> kShiftSize) - 1;
235   return OpSaveFpLrX | encoded_value;
236 }
237 
238 // Generate an unwind code for "sub sp, sp, #stack_space".
MakeOpAllocS(int stack_space)239 uint8_t MakeOpAllocS(int stack_space) {
240   // See unwind code alloc_s in
241   // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
242   DCHECK_GE(stack_space, 0);
243   DCHECK_LT(stack_space, 512);
244   DCHECK_EQ(stack_space & kAllocStackShiftMask, 0);
245   return OpAllocS | (stack_space >> kAllocStackShiftSize);
246 }
247 
248 // Generate the second byte of the unwind code for "add fp, sp, #offset".
MakeOpAddFpArgument(int offset)249 uint8_t MakeOpAddFpArgument(int offset) {
250   // See unwind code add_fp in
251   // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
252   DCHECK_GE(offset, 0);
253   constexpr int kShiftSize = 3;
254   constexpr int kShiftMask = (1 << kShiftSize) - 1;
255   DCHECK_EQ(offset & kShiftMask, 0);
256   USE(kShiftMask);
257   int encoded_value = offset >> kShiftSize;
258   // Encoded value must fit in 8 bits.
259   DCHECK_LE(encoded_value, 0xff);
260   return encoded_value;
261 }
262 
263 template <int kNumberOfUnwindCodeWords = kDefaultNumberOfUnwindCodeWords>
264 struct V8UnwindData {
265   UNWIND_INFO unwind_info;
266   UNWIND_CODE unwind_codes[kNumberOfUnwindCodeWords];
267 
V8UnwindDatav8::internal::win64_unwindinfo::V8UnwindData268   V8UnwindData() {
269     memset(&unwind_info, 0, sizeof(UNWIND_INFO));
270     unwind_info.X = 1;  // has exception handler after unwind-codes.
271     unwind_info.CodeWords = kNumberOfUnwindCodeWords;
272 
273     // Generate unwind codes for the following prolog:
274     //
275     // stp fp, lr, [sp, #-kCallerSPOffset]!
276     // mov fp, sp
277     //
278     // This is a very rough approximation of the actual function prologs used in
279     // V8. In particular, we often push other data before the (fp, lr) pair,
280     // meaning the stack pointer computed for the caller frame is wrong. That
281     // error is acceptable when the unwinding info for the caller frame also
282     // depends on fp rather than sp, as is the case for V8 builtins and runtime-
283     // generated code.
284     STATIC_ASSERT(kNumberOfUnwindCodeWords >= 1);
285     unwind_codes[0] = Combine8BitUnwindCodes(
286         OpSetFp, MakeOpSaveFpLrX(-CommonFrameConstants::kCallerSPOffset),
287         OpEnd);
288 
289     // Fill the rest with nops.
290     for (int i = 1; i < kNumberOfUnwindCodeWords; ++i) {
291       unwind_codes[i] = Combine8BitUnwindCodes();
292     }
293   }
294 };
295 
296 struct CodeRangeUnwindingRecord {
297   void* dynamic_table;
298   uint32_t runtime_function_count;
299   V8UnwindData<> unwind_info;
300   uint32_t exception_handler;
301 
302   // For Windows ARM64 unwinding, register 2 unwind_info for each code range,
303   // unwind_info for all full size ranges (1MB - 4 bytes) and unwind_info1 for
304   // the remaining non full size range. There is at most 1 range which is less
305   // than full size.
306   V8UnwindData<> unwind_info1;
307   uint32_t exception_handler1;
308   uint8_t exception_thunk[kMaxExceptionThunkSize];
309 
310   // More RUNTIME_FUNCTION structs could follow below array because the number
311   // of RUNTIME_FUNCTION needed to cover given code range is computed at
312   // runtime.
313   RUNTIME_FUNCTION runtime_function[kDefaultRuntimeFunctionCount];
314 };
315 
316 #pragma pack(pop)
317 
FrameOffsets()318 FrameOffsets::FrameOffsets()
319     : fp_to_saved_caller_fp(CommonFrameConstants::kCallerFPOffset),
320       fp_to_caller_sp(CommonFrameConstants::kCallerSPOffset) {}
IsDefault() const321 bool FrameOffsets::IsDefault() const {
322   FrameOffsets other;
323   return fp_to_saved_caller_fp == other.fp_to_saved_caller_fp &&
324          fp_to_caller_sp == other.fp_to_caller_sp;
325 }
326 
GetUnwindInfoForBuiltinFunction(uint32_t func_len,FrameOffsets fp_adjustment)327 std::vector<uint8_t> GetUnwindInfoForBuiltinFunction(
328     uint32_t func_len, FrameOffsets fp_adjustment) {
329   DCHECK_LE(func_len, kMaxFunctionLength);
330   DCHECK_EQ((func_len & kFunctionLengthMask), 0);
331   USE(kFunctionLengthMask);
332 
333   // The largest size of unwind data required for all options below.
334   constexpr int kMaxNumberOfUnwindCodeWords = 2;
335 
336   V8UnwindData<kMaxNumberOfUnwindCodeWords> xdata;
337   // FunctionLength is ensured to be aligned at instruction size and Windows
338   // ARM64 doesn't encoding its 2 LSB.
339   xdata.unwind_info.FunctionLength = func_len >> kFunctionLengthShiftSize;
340 
341   if (fp_adjustment.IsDefault()) {
342     // One code word is plenty.
343     STATIC_ASSERT(kDefaultNumberOfUnwindCodeWords <
344                   kMaxNumberOfUnwindCodeWords);
345     xdata.unwind_info.CodeWords = kDefaultNumberOfUnwindCodeWords;
346   } else {
347     // We want to convey the following facts:
348     // 1. The caller's fp is found at [fp + fp_to_saved_caller_fp].
349     // 2. The caller's pc is found at [fp + fp_to_saved_caller_fp + 8].
350     // 3. The caller's sp is equal to fp + fp_to_caller_sp.
351     //
352     // An imaginary prolog that would establish those relationships might look
353     // like the following, with appropriate values for the various constants:
354     //
355     // stp fp, lr, [sp, #pre_index_amount]!
356     // sub sp, sp, #stack_space
357     // add fp, sp, offset_from_stack_top
358     //
359     // Why do we need offset_from_stack_top? The unwinding encoding for
360     // allocating stack space has 16-byte granularity, and the frame pointer has
361     // only 8-byte alignment.
362     int pre_index_amount =
363         fp_adjustment.fp_to_saved_caller_fp - fp_adjustment.fp_to_caller_sp;
364     int stack_space = fp_adjustment.fp_to_saved_caller_fp;
365     int offset_from_stack_top = stack_space & kAllocStackShiftMask;
366     stack_space += offset_from_stack_top;
367 
368     xdata.unwind_codes[0] = Combine8BitUnwindCodes(
369         OpAddFp, MakeOpAddFpArgument(offset_from_stack_top),
370         MakeOpAllocS(stack_space), MakeOpSaveFpLrX(pre_index_amount));
371     xdata.unwind_codes[1] = Combine8BitUnwindCodes(OpEnd);
372   }
373 
374   return std::vector<uint8_t>(
375       reinterpret_cast<uint8_t*>(&xdata),
376       reinterpret_cast<uint8_t*>(
377           &xdata.unwind_codes[xdata.unwind_info.CodeWords]));
378 }
379 
380 template <typename Record>
InitUnwindingRecord(Record * record,size_t code_size_in_bytes)381 void InitUnwindingRecord(Record* record, size_t code_size_in_bytes) {
382   // We assume that the first page of the code range is executable and
383   // committed and reserved to contain multiple PDATA/XDATA to cover the whole
384   // range. All addresses are 32bit relative offsets to start.
385 
386   // Maximum RUNTIME_FUNCTION count available in reserved memory, this includes
387   // static part in Record as kDefaultRuntimeFunctionCount plus dynamic part in
388   // the remaining reserved memory.
389   constexpr uint32_t max_runtime_function_count = static_cast<uint32_t>(
390       (kOSPageSize - sizeof(Record)) / sizeof(RUNTIME_FUNCTION) +
391       kDefaultRuntimeFunctionCount);
392 
393   uint32_t runtime_function_index = 0;
394   uint32_t current_unwind_start_address = 0;
395   int64_t remaining_size_in_bytes = static_cast<int64_t>(code_size_in_bytes);
396 
397   // Divide the code range into chunks in size kMaxFunctionLength and create a
398   // RUNTIME_FUNCTION for each of them. All the chunks in the same size can
399   // share 1 unwind_info struct, but a separate unwind_info is needed for the
400   // last chunk if it is smaller than kMaxFunctionLength, because unlike X64,
401   // unwind_info encodes the function/chunk length.
402   while (remaining_size_in_bytes >= kMaxFunctionLength &&
403          runtime_function_index < max_runtime_function_count) {
404     record->runtime_function[runtime_function_index].BeginAddress =
405         current_unwind_start_address;
406     record->runtime_function[runtime_function_index].UnwindData =
407         static_cast<DWORD>(offsetof(Record, unwind_info));
408 
409     runtime_function_index++;
410     current_unwind_start_address += kMaxFunctionLength;
411     remaining_size_in_bytes -= kMaxFunctionLength;
412   }
413   // FunctionLength is ensured to be aligned at instruction size and Windows
414   // ARM64 doesn't encoding 2 LSB.
415   record->unwind_info.unwind_info.FunctionLength = kMaxFunctionLength >> 2;
416 
417   if (remaining_size_in_bytes > 0 &&
418       runtime_function_index < max_runtime_function_count) {
419     DCHECK_EQ(remaining_size_in_bytes % kInstrSize, 0);
420 
421     record->unwind_info1.unwind_info.FunctionLength = static_cast<uint32_t>(
422         remaining_size_in_bytes >> kFunctionLengthShiftSize);
423     record->runtime_function[runtime_function_index].BeginAddress =
424         current_unwind_start_address;
425     record->runtime_function[runtime_function_index].UnwindData =
426         static_cast<DWORD>(offsetof(Record, unwind_info1));
427 
428     remaining_size_in_bytes -= kMaxFunctionLength;
429     record->exception_handler1 = offsetof(Record, exception_thunk);
430     record->runtime_function_count = runtime_function_index + 1;
431   } else {
432     record->runtime_function_count = runtime_function_index;
433   }
434 
435   // 1 page can cover kMaximalCodeRangeSize for ARM64 (128MB). If
436   // kMaximalCodeRangeSize is changed for ARM64 and makes 1 page insufficient to
437   // cover it, more pages will need to reserved for unwind data.
438   DCHECK_LE(remaining_size_in_bytes, 0);
439 
440   record->exception_handler = offsetof(Record, exception_thunk);
441 
442   // Hardcoded thunk.
443   AssemblerOptions options;
444   options.record_reloc_info_for_serialization = false;
445   TurboAssembler masm(nullptr, options, CodeObjectRequired::kNo,
446                       NewAssemblerBuffer(64));
447   masm.Mov(x16,
448            Operand(reinterpret_cast<uint64_t>(&CRASH_HANDLER_FUNCTION_NAME)));
449   masm.Br(x16);
450   DCHECK_LE(masm.instruction_size(), sizeof(record->exception_thunk));
451   memcpy(&record->exception_thunk[0], masm.buffer_start(),
452          masm.instruction_size());
453 }
454 
455 #endif  // V8_OS_WIN_X64
456 
457 namespace {
458 
459 V8_DECLARE_ONCE(load_ntdll_unwinding_functions_once);
460 static decltype(
461     &::RtlAddGrowableFunctionTable) add_growable_function_table_func = nullptr;
462 static decltype(
463     &::RtlDeleteGrowableFunctionTable) delete_growable_function_table_func =
464     nullptr;
465 
LoadNtdllUnwindingFunctions()466 void LoadNtdllUnwindingFunctions() {
467   base::CallOnce(&load_ntdll_unwinding_functions_once, []() {
468     // Load functions from the ntdll.dll module.
469     HMODULE ntdll_module =
470         LoadLibraryEx(L"ntdll.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
471     DCHECK_NOT_NULL(ntdll_module);
472 
473     // This fails on Windows 7.
474     add_growable_function_table_func =
475         reinterpret_cast<decltype(&::RtlAddGrowableFunctionTable)>(
476             ::GetProcAddress(ntdll_module, "RtlAddGrowableFunctionTable"));
477     DCHECK_IMPLIES(IsWindows8OrGreater(), add_growable_function_table_func);
478 
479     delete_growable_function_table_func =
480         reinterpret_cast<decltype(&::RtlDeleteGrowableFunctionTable)>(
481             ::GetProcAddress(ntdll_module, "RtlDeleteGrowableFunctionTable"));
482     DCHECK_IMPLIES(IsWindows8OrGreater(), delete_growable_function_table_func);
483   });
484 }
485 
AddGrowableFunctionTable(PVOID * DynamicTable,PRUNTIME_FUNCTION FunctionTable,DWORD EntryCount,DWORD MaximumEntryCount,ULONG_PTR RangeBase,ULONG_PTR RangeEnd)486 bool AddGrowableFunctionTable(PVOID* DynamicTable,
487                               PRUNTIME_FUNCTION FunctionTable, DWORD EntryCount,
488                               DWORD MaximumEntryCount, ULONG_PTR RangeBase,
489                               ULONG_PTR RangeEnd) {
490   DCHECK(::IsWindows8OrGreater());
491 
492   LoadNtdllUnwindingFunctions();
493   DCHECK_NOT_NULL(add_growable_function_table_func);
494 
495   *DynamicTable = nullptr;
496   DWORD status =
497       add_growable_function_table_func(DynamicTable, FunctionTable, EntryCount,
498                                        MaximumEntryCount, RangeBase, RangeEnd);
499   DCHECK((status == 0 && *DynamicTable != nullptr) ||
500          status == 0xC000009A);  // STATUS_INSUFFICIENT_RESOURCES
501   return (status == 0);
502 }
503 
DeleteGrowableFunctionTable(PVOID dynamic_table)504 void DeleteGrowableFunctionTable(PVOID dynamic_table) {
505   DCHECK(::IsWindows8OrGreater());
506 
507   LoadNtdllUnwindingFunctions();
508   DCHECK_NOT_NULL(delete_growable_function_table_func);
509 
510   delete_growable_function_table_func(dynamic_table);
511 }
512 
513 }  // namespace
514 
RegisterNonABICompliantCodeRange(void * start,size_t size_in_bytes)515 void RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) {
516   DCHECK(CanRegisterUnwindInfoForNonABICompliantCodeRange());
517 
518   // When the --win64-unwinding-info flag is set, we call
519   // RtlAddGrowableFunctionTable to register unwinding info for the whole code
520   // range of an isolate or Wasm module. This enables the Windows OS stack
521   // unwinder to work correctly with V8-generated code, enabling stack walking
522   // in Windows debuggers and performance tools. However, the
523   // RtlAddGrowableFunctionTable API is only supported on Windows 8 and above.
524   //
525   // On Windows 7, or when --win64-unwinding-info is not set, we may still need
526   // to call RtlAddFunctionTable to register a custom exception handler passed
527   // by the embedder (like Crashpad).
528 
529   if (RegisterUnwindInfoForExceptionHandlingOnly()) {
530 #if defined(V8_OS_WIN_X64)
531     // Windows ARM64 starts since 1709 Windows build, no need to have exception
532     // handling only unwind info for compatibility.
533     if (unhandled_exception_callback_g) {
534       ExceptionHandlerRecord* record = new (start) ExceptionHandlerRecord();
535       InitUnwindingRecord(record, size_in_bytes);
536 
537       CHECK(::RtlAddFunctionTable(record->runtime_function,
538                                   kDefaultRuntimeFunctionCount,
539                                   reinterpret_cast<DWORD64>(start)));
540 
541       // Protect reserved page against modifications.
542       DWORD old_protect;
543       CHECK(VirtualProtect(start, sizeof(ExceptionHandlerRecord),
544                            PAGE_EXECUTE_READ, &old_protect));
545     }
546 #endif  // V8_OS_WIN_X64
547   } else {
548     CodeRangeUnwindingRecord* record = new (start) CodeRangeUnwindingRecord();
549     InitUnwindingRecord(record, size_in_bytes);
550 
551     CHECK(AddGrowableFunctionTable(
552         &record->dynamic_table, record->runtime_function,
553         record->runtime_function_count, record->runtime_function_count,
554         reinterpret_cast<DWORD64>(start),
555         reinterpret_cast<DWORD64>(reinterpret_cast<uint8_t*>(start) +
556                                   size_in_bytes)));
557 
558     // Protect reserved page against modifications.
559     DWORD old_protect;
560     CHECK(VirtualProtect(start, sizeof(CodeRangeUnwindingRecord),
561                          PAGE_EXECUTE_READ, &old_protect));
562   }
563 }
564 
UnregisterNonABICompliantCodeRange(void * start)565 void UnregisterNonABICompliantCodeRange(void* start) {
566   DCHECK(CanRegisterUnwindInfoForNonABICompliantCodeRange());
567 
568   if (RegisterUnwindInfoForExceptionHandlingOnly()) {
569 #if defined(V8_OS_WIN_X64)
570     // Windows ARM64 starts since 1709 Windows build, no need to have exception
571     // handling only unwind info for compatibility.
572     if (unhandled_exception_callback_g) {
573       ExceptionHandlerRecord* record =
574           reinterpret_cast<ExceptionHandlerRecord*>(start);
575       CHECK(::RtlDeleteFunctionTable(record->runtime_function));
576     }
577 #endif  // V8_OS_WIN_X64
578   } else {
579     CodeRangeUnwindingRecord* record =
580         reinterpret_cast<CodeRangeUnwindingRecord*>(start);
581     if (record->dynamic_table) {
582       DeleteGrowableFunctionTable(record->dynamic_table);
583     }
584   }
585 }
586 
587 #if defined(V8_OS_WIN_X64)
588 
onPushRbp()589 void XdataEncoder::onPushRbp() {
590   current_frame_code_offset_ =
591       assembler_.pc_offset() - kPushRbpInstructionLength;
592 }
593 
onMovRbpRsp()594 void XdataEncoder::onMovRbpRsp() {
595   if (current_frame_code_offset_ >= 0 &&
596       current_frame_code_offset_ == assembler_.pc_offset() - kRbpPrefixLength) {
597     fp_offsets_.push_back(current_frame_code_offset_);
598   }
599 }
600 
601 #elif defined(V8_OS_WIN_ARM64)
602 
onSaveFpLr()603 void XdataEncoder::onSaveFpLr() {
604   current_frame_code_offset_ = assembler_.pc_offset() - 4;
605   fp_offsets_.push_back(current_frame_code_offset_);
606   fp_adjustments_.push_back(current_frame_adjustment_);
607   current_frame_adjustment_ = FrameOffsets();
608 }
609 
onFramePointerAdjustment(int fp_to_saved_caller_fp,int fp_to_caller_sp)610 void XdataEncoder::onFramePointerAdjustment(int fp_to_saved_caller_fp,
611                                             int fp_to_caller_sp) {
612   current_frame_adjustment_.fp_to_saved_caller_fp = fp_to_saved_caller_fp;
613   current_frame_adjustment_.fp_to_caller_sp = fp_to_caller_sp;
614 }
615 
616 #endif  // V8_OS_WIN_X64
617 
618 }  // namespace win64_unwindinfo
619 }  // namespace internal
620 }  // namespace v8
621