blob: a92c2931df5fba99ccebe50a1397a0323846eade [file] [log] [blame]
// 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