| // Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | #include "vm/unwinding_records.h" | 
 |  | 
 | #include "vm/globals.h" | 
 |  | 
 | #include "platform/unwinding_records.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | #if (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS)) &&      \ | 
 |     (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64)) | 
 |  | 
 | static void InitUnwindingRecord(intptr_t offset, | 
 |                                 CodeRangeUnwindingRecord* record, | 
 |                                 size_t code_size_in_bytes) { | 
 | #if defined(TARGET_ARCH_X64) | 
 |   // All addresses are 32bit relative offsets to start. | 
 |   record->runtime_function[0].BeginAddress = 0; | 
 |   record->runtime_function[0].EndAddress = code_size_in_bytes; | 
 |   record->runtime_function[0].UnwindData = | 
 |       offset + offsetof(CodeRangeUnwindingRecord, unwind_info); | 
 |   record->runtime_function_count = 1; | 
 | #elif defined(TARGET_ARCH_ARM64) | 
 |  | 
 |   const intptr_t kInstrSize = 4; | 
 |  | 
 |   // 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. | 
 |   const uint32_t max_runtime_function_count = | 
 |       static_cast<uint32_t>((UnwindingRecordsPlatform::SizeInBytes() - | 
 |                              sizeof(CodeRangeUnwindingRecord)) / | 
 |                                 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 = | 
 |         offset + | 
 |         static_cast<DWORD>(offsetof(CodeRangeUnwindingRecord, 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) { | 
 |     ASSERT((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 = | 
 |         offset + | 
 |         static_cast<DWORD>(offsetof(CodeRangeUnwindingRecord, unwind_info1)); | 
 |  | 
 |     remaining_size_in_bytes -= kMaxFunctionLength; | 
 |     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. | 
 |   ASSERT(remaining_size_in_bytes <= 0); | 
 | #else | 
 | #error What architecture? | 
 | #endif | 
 |   record->magic = kUnwindingRecordMagic; | 
 | } | 
 |  | 
 | const void* UnwindingRecords::GenerateRecordsInto(intptr_t offset, | 
 |                                                   uint8_t* target_buffer) { | 
 |   CodeRangeUnwindingRecord* record = | 
 |       new (target_buffer) CodeRangeUnwindingRecord(); | 
 |   InitUnwindingRecord(offset, record, offset); | 
 |   return target_buffer; | 
 | } | 
 |  | 
 | #endif  // (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS)) | 
 |  | 
 | #if defined(DART_HOST_OS_WINDOWS) &&                                           \ | 
 |     (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64)) | 
 |  | 
 | // Special exception-unwinding records are put at the end of executable | 
 | // page on Windows for 64-bit applications. | 
 | void UnwindingRecords::RegisterExecutablePage(Page* page) { | 
 |   ASSERT(page->is_executable()); | 
 |   ASSERT(sizeof(CodeRangeUnwindingRecord) <= | 
 |          UnwindingRecordsPlatform::SizeInBytes()); | 
 |   page->top_ -= UnwindingRecordsPlatform::SizeInBytes(); | 
 |   intptr_t unwinding_record_offset = | 
 |       page->memory_->size() - UnwindingRecordsPlatform::SizeInBytes(); | 
 |   CodeRangeUnwindingRecord* record = | 
 |       new (reinterpret_cast<uint8_t*>(page->memory_->start()) + | 
 |            unwinding_record_offset) CodeRangeUnwindingRecord(); | 
 |   InitUnwindingRecord(unwinding_record_offset, record, page->memory_->size()); | 
 |   RELEASE_ASSERT(record->magic == kUnwindingRecordMagic); | 
 |   DWORD status = RtlAddGrowableFunctionTable( | 
 |       /*DynamicTable=*/&record->dynamic_table, | 
 |       /*FunctionTable=*/record->runtime_function, | 
 |       /*EntryCount=*/record->runtime_function_count, | 
 |       /*MaximumEntryCount=*/record->runtime_function_count, | 
 |       /*RangeBase=*/page->memory_->start(), | 
 |       /*RangeEnd=*/page->memory_->end()); | 
 |   if (status != 0) { | 
 |     FATAL("Failed to add growable function table: 0x%x\n", status); | 
 |   } | 
 | } | 
 |  | 
 | void UnwindingRecords::UnregisterExecutablePage(Page* page) { | 
 |   ASSERT(page->is_executable() && !page->is_image()); | 
 |   intptr_t unwinding_record_offset = | 
 |       page->memory_->size() - UnwindingRecordsPlatform::SizeInBytes(); | 
 |   CodeRangeUnwindingRecord* record = | 
 |       reinterpret_cast<CodeRangeUnwindingRecord*>( | 
 |           reinterpret_cast<uint8_t*>(page->memory_->start()) + | 
 |           unwinding_record_offset); | 
 |   RELEASE_ASSERT(record->magic == kUnwindingRecordMagic); | 
 |   RtlDeleteGrowableFunctionTable(record->dynamic_table); | 
 | } | 
 |  | 
 | #endif  // defined(DART_HOST_OS_WINDOWS) | 
 |  | 
 | }  // namespace dart |