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