|  | // 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 { | 
|  |  | 
|  | // When the host is 64-bit Windows but the target is not, only define this | 
|  | // when the ELF loader may be used, so _not_ in gen_snapshot. | 
|  | #if (defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)) ||     \ | 
|  | (defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT) &&               \ | 
|  | (!defined(DART_PRECOMPILER) || defined(TESTING))) | 
|  |  | 
|  | 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 Unhandled Windows architecture. | 
|  | #endif | 
|  | record->magic = kUnwindingRecordMagic; | 
|  | } | 
|  |  | 
|  | #endif  // defined(DART_TARGET_OS_WINDOWS) || ... | 
|  |  | 
|  | #if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT) | 
|  |  | 
|  | 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(TARGET_ARCH_IS_64_BIT) | 
|  |  | 
|  | // Only use these definitions when the ELF loader may be used on 64-bit | 
|  | // Windows, as it is the only client of these methods (e.g., _not_ in | 
|  | // gen_snapshot). | 
|  | #if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT) &&                \ | 
|  | (!defined(DART_PRECOMPILER) || defined(TESTING)) | 
|  |  | 
|  | // 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 |