blob: 01e5410b9bd226174383e5cb605bf5856c85e083 [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 {
// 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