[vm/win/aot] Provide unwinding information for Windows AOT snapshots.

Fixes https://github.com/dart-lang/sdk/issues/52045
TEST=ffi_induce_a_crash_test

Change-Id: Ic047df10732d6ff8cf695ce098d80ec3a098dbf9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/302380
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
Alexander Aprelev
2023-05-11 03:14:27 +00:00
committed by Commit Queue
parent 838c926f64
commit 3a668dcf03
12 changed files with 295 additions and 112 deletions
+26
View File
@@ -16,6 +16,8 @@
#include "bin/virtual_memory.h"
#include "platform/elf.h"
#include "platform/unwinding_records.h"
namespace dart {
namespace bin {
@@ -239,6 +241,12 @@ class LoadedElf {
const dart::elf::Symbol* dynamic_symbol_table_ = nullptr;
uword dynamic_symbol_count_ = 0;
#if defined(DART_HOST_OS_WINDOWS) && defined(HOST_ARCH_X64)
// Dynamic table for looking up unwinding exceptions info.
// Initialized by LoadSegments as we load executable segment.
MallocGrowableArray<void*> dynamic_runtime_function_tables_;
#endif
DISALLOW_COPY_AND_ASSIGN(LoadedElf);
};
@@ -261,6 +269,7 @@ class LoadedElf {
}
bool LoadedElf::Load() {
UnwindingRecordsPlatform::Init();
VirtualMemory::Init();
if (error_ != nullptr) {
@@ -291,6 +300,13 @@ LoadedElf::~LoadedElf() {
program_table_mapping_.reset();
section_table_mapping_.reset();
section_string_table_mapping_.reset();
#if defined(DART_HOST_OS_WINDOWS) && defined(HOST_ARCH_X64)
for (intptr_t i = 0; i < dynamic_runtime_function_tables_.length(); i++) {
UnwindingRecordsPlatform::UnregisterDynamicTable(
dynamic_runtime_function_tables_[i]);
}
UnwindingRecordsPlatform::Cleanup();
#endif
}
bool LoadedElf::ReadHeader() {
@@ -446,6 +462,16 @@ bool LoadedElf::LoadSegments() {
CHECK_ERROR(memory != nullptr, "Could not map segment.");
CHECK_ERROR(memory->address() == memory_start,
"Mapping not at requested address.");
#if defined(DART_HOST_OS_WINDOWS) && defined(HOST_ARCH_X64)
// For executable pages register unwinding information that should be
// present on the page.
if (map_type == File::kReadExecute) {
void* ptable = nullptr;
UnwindingRecordsPlatform::RegisterExecutableMemory(
memory->address(), memory->size(), &ptable);
dynamic_runtime_function_tables_.Add(ptable);
}
#endif
}
return true;
+3
View File
@@ -32,6 +32,9 @@ platform_sources = [
"thread_sanitizer.h",
"unicode.cc",
"unicode.h",
"unwinding_records.cc",
"unwinding_records.h",
"unwinding_records_win.cc",
"utils.cc",
"utils.h",
"utils_android.cc",
+25
View File
@@ -0,0 +1,25 @@
// 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 "platform/unwinding_records.h"
#include "vm/globals.h"
#if !defined(DART_HOST_OS_WINDOWS) || !defined(TARGET_ARCH_X64)
namespace dart {
void UnwindingRecordsPlatform::Init() {}
void UnwindingRecordsPlatform::Cleanup() {}
void UnwindingRecordsPlatform::RegisterExecutableMemory(
void* start,
intptr_t size,
void** pp_dynamic_table) {}
void UnwindingRecordsPlatform::UnregisterDynamicTable(void* p_dynamic_table) {}
intptr_t UnwindingRecordsPlatform::SizeInBytes() {
return 0;
}
} // namespace dart
#endif // !defined(DART_HOST_OS_WINDOWS) || !defined(TARGET_ARCH_X64)
+94
View File
@@ -0,0 +1,94 @@
// 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.
// Class for patching compiled code.
#ifndef RUNTIME_PLATFORM_UNWINDING_RECORDS_H_
#define RUNTIME_PLATFORM_UNWINDING_RECORDS_H_
#include "platform/allocation.h"
namespace dart {
class UnwindingRecordsPlatform : public AllStatic {
public:
static void Init();
static void Cleanup();
static intptr_t SizeInBytes();
static void RegisterExecutableMemory(void* start,
intptr_t size,
void** pp_dynamic_table);
static void UnregisterDynamicTable(void* p_dynamic_table);
static void* GetAddGrowableFunctionTableFunc();
static void* GetDeleteGrowableFunctionTableFunc();
};
#if defined(DART_HOST_OS_WINDOWS) && defined(TARGET_ARCH_X64)
#pragma pack(push, 1)
//
// Refer to https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64
//
typedef unsigned char UBYTE;
typedef union _UNWIND_CODE {
struct {
UBYTE CodeOffset;
UBYTE UnwindOp : 4;
UBYTE OpInfo : 4;
};
USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;
typedef struct _UNWIND_INFO {
UBYTE Version : 3;
UBYTE Flags : 5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes;
UBYTE FrameRegister : 4;
UBYTE FrameOffset : 4;
UNWIND_CODE UnwindCode[2];
} UNWIND_INFO, *PUNWIND_INFO;
static constexpr int kPushRbpInstructionLength = 1;
static const int kMovRbpRspInstructionLength = 3;
static constexpr int kRbpPrefixLength =
kPushRbpInstructionLength + kMovRbpRspInstructionLength;
static constexpr int kRBP = 5;
struct GeneratedCodeUnwindInfo {
UNWIND_INFO unwind_info;
GeneratedCodeUnwindInfo() {
unwind_info.Version = 1;
unwind_info.Flags = UNW_FLAG_NHANDLER;
unwind_info.SizeOfProlog = kRbpPrefixLength;
unwind_info.CountOfCodes = 2;
unwind_info.FrameRegister = kRBP;
unwind_info.FrameOffset = 0;
unwind_info.UnwindCode[0].CodeOffset = kRbpPrefixLength;
unwind_info.UnwindCode[0].UnwindOp = 3; // UWOP_SET_FPREG
unwind_info.UnwindCode[0].OpInfo = 0;
unwind_info.UnwindCode[1].CodeOffset = kPushRbpInstructionLength;
unwind_info.UnwindCode[1].UnwindOp = 0; // UWOP_PUSH_NONVOL
unwind_info.UnwindCode[1].OpInfo = kRBP;
}
};
struct CodeRangeUnwindingRecord {
void* dynamic_table;
uint32_t runtime_function_count;
GeneratedCodeUnwindInfo unwind_info;
intptr_t exception_handler;
RUNTIME_FUNCTION runtime_function[1];
};
#pragma pack(pop)
#endif // defined(DART_HOST_OS_WINDOWS) && defined(TARGET_ARCH_X64)
} // namespace dart
#endif // RUNTIME_PLATFORM_UNWINDING_RECORDS_H_
+86
View File
@@ -0,0 +1,86 @@
// 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 "platform/unwinding_records.h"
#include "platform/assert.h"
#include "platform/globals.h"
#if defined(DART_HOST_OS_WINDOWS) && defined(TARGET_ARCH_X64)
namespace dart {
static HMODULE ntdll_module;
static decltype(
&::RtlAddGrowableFunctionTable) add_growable_function_table_func_ = nullptr;
static decltype(
&::RtlDeleteGrowableFunctionTable) delete_growable_function_table_func_ =
nullptr;
const intptr_t kReservedUnwindingRecordsSizeBytes = 64;
intptr_t UnwindingRecordsPlatform::SizeInBytes() {
return kReservedUnwindingRecordsSizeBytes;
}
void* UnwindingRecordsPlatform::GetAddGrowableFunctionTableFunc() {
return add_growable_function_table_func_;
}
void* UnwindingRecordsPlatform::GetDeleteGrowableFunctionTableFunc() {
return delete_growable_function_table_func_;
}
void UnwindingRecordsPlatform::Init() {
ntdll_module =
LoadLibraryEx(L"ntdll.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
ASSERT(ntdll_module != nullptr);
// This pair of functions is not available on Windows 7.
add_growable_function_table_func_ =
reinterpret_cast<decltype(&::RtlAddGrowableFunctionTable)>(
::GetProcAddress(ntdll_module, "RtlAddGrowableFunctionTable"));
delete_growable_function_table_func_ =
reinterpret_cast<decltype(&::RtlDeleteGrowableFunctionTable)>(
::GetProcAddress(ntdll_module, "RtlDeleteGrowableFunctionTable"));
// Either both available, or both not available.
ASSERT((add_growable_function_table_func_ == nullptr) ==
(delete_growable_function_table_func_ == nullptr));
}
void UnwindingRecordsPlatform::Cleanup() {
FreeLibrary(ntdll_module);
}
void UnwindingRecordsPlatform::RegisterExecutableMemory(
void* start,
intptr_t size,
void** pp_dynamic_table) {
auto func = add_growable_function_table_func_;
if (func == nullptr) {
return;
}
intptr_t unwinding_record_offset = size - kReservedUnwindingRecordsSizeBytes;
uint8_t* record_ptr = static_cast<uint8_t*>(start) + unwinding_record_offset;
CodeRangeUnwindingRecord* record =
reinterpret_cast<CodeRangeUnwindingRecord*>(record_ptr);
uword start_num = reinterpret_cast<intptr_t>(start);
uword end_num = start_num + size;
if (func(pp_dynamic_table,
/*FunctionTable=*/record->runtime_function,
/*EntryCount=*/record->runtime_function_count,
/*MaximumEntryCount=*/record->runtime_function_count,
/*RangeBase=*/start_num,
/*RangeEnd=*/end_num) != 0) {
FATAL("Failed to add growable function table: %d\n", GetLastError());
}
}
void UnwindingRecordsPlatform::UnregisterDynamicTable(void* p_dynamic_table) {
auto func = delete_growable_function_table_func_;
if (func == nullptr) return;
func(p_dynamic_table);
}
} // namespace dart
#endif // defined(DART_HOST_OS_WINDOWS)
+4 -2
View File
@@ -7,6 +7,8 @@
#include "vm/dart.h"
#include "platform/unwinding_records.h"
#include "vm/app_snapshot.h"
#include "vm/code_observers.h"
#include "vm/compiler/runtime_offsets_extracted.h"
@@ -334,7 +336,7 @@ char* Dart::DartInit(const Dart_InitializeParams* params) {
Api::Init();
NativeSymbolResolver::Init();
NOT_IN_PRODUCT(Profiler::Init());
UnwindingRecords::Init();
UnwindingRecordsPlatform::Init();
Page::Init();
StoreBuffer::Init();
MarkingStack::Init();
@@ -767,7 +769,7 @@ char* Dart::Cleanup() {
StoreBuffer::Cleanup();
Object::Cleanup();
Page::Cleanup();
UnwindingRecords::Cleanup();
UnwindingRecordsPlatform::Cleanup();
StubCode::Cleanup();
#if defined(SUPPORT_TIMELINE)
if (FLAG_trace_shutdown) {
+30
View File
@@ -5,12 +5,14 @@
#include "vm/elf.h"
#include "platform/elf.h"
#include "platform/unwinding_records.h"
#include "vm/cpu.h"
#include "vm/dwarf.h"
#include "vm/hash_map.h"
#include "vm/image_snapshot.h"
#include "vm/stack_frame.h"
#include "vm/thread.h"
#include "vm/unwinding_records.h"
#include "vm/zone_text_buffer.h"
namespace dart {
@@ -1409,6 +1411,28 @@ void Elf::FinalizeEhFrame() {
// No text section added means no .eh_frame.
if (text_section == nullptr) return;
#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_X64)
// Append Windows unwinding instructions to the end of .text section.
{
auto* const unwinding_instructions_frame = new (zone_) TextSection(type_);
ZoneWriteStream stream(
zone(),
/*initial_size=*/UnwindingRecordsPlatform::SizeInBytes());
uint8_t* unwinding_instructions =
zone()->Alloc<uint8_t>(UnwindingRecordsPlatform::SizeInBytes());
intptr_t start_offset =
Utils::RoundUp(text_section->FileSize(), text_section->alignment);
stream.WriteBytes(UnwindingRecords::GenerateRecordsInto(
start_offset, unwinding_instructions),
UnwindingRecordsPlatform::SizeInBytes());
unwinding_instructions_frame->AddPortion(stream.buffer(),
stream.bytes_written());
section_table_->Add(unwinding_instructions_frame, kTextName);
}
#endif
// Multiplier which will be used to scale operands of DW_CFA_offset and
// DW_CFA_val_offset.
const intptr_t kDataAlignment = -compiler::target::kWordSize;
@@ -1449,6 +1473,12 @@ void Elf::FinalizeEhFrame() {
// Emit an FDE covering each .text section.
for (const auto& portion : text_section->portions()) {
#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_X64)
if (portion.label == 0) {
// Unwinding instructions sections doesn't have label, doesn't dwarf
continue;
}
#endif
ASSERT(portion.label != 0); // Needed for relocations.
dwarf_stream.WritePrefixedLength([&]() {
// Offset to CIE. Note that unlike pcrel this offset is encoded
+2 -1
View File
@@ -6,6 +6,7 @@
#include "platform/assert.h"
#include "platform/leak_sanitizer.h"
#include "platform/unwinding_records.h"
#include "vm/dart.h"
#include "vm/heap/become.h"
#include "vm/heap/compactor.h"
@@ -217,7 +218,7 @@ Page* PageSpace::AllocatePage(bool is_exec, bool link) {
Page* PageSpace::AllocateLargePage(intptr_t size, bool is_exec) {
const intptr_t page_size_in_words = LargePageSizeInWordsFor(
size + (is_exec ? UnwindingRecords::SizeInBytes() : 0));
size + (is_exec ? UnwindingRecordsPlatform::SizeInBytes() : 0));
{
MutexLocker ml(&pages_lock_);
if (!CanIncreaseCapacityInWordsLocked(page_size_in_words)) {
-5
View File
@@ -9,11 +9,6 @@
namespace dart {
void UnwindingRecords::Init() {}
void UnwindingRecords::Cleanup() {}
intptr_t UnwindingRecords::SizeInBytes() {
return 0;
}
void UnwindingRecords::RegisterExecutablePage(Page* page) {}
void UnwindingRecords::UnregisterExecutablePage(Page* page) {}
+2 -3
View File
@@ -13,9 +13,8 @@ namespace dart {
class UnwindingRecords : public AllStatic {
public:
static void Init();
static void Cleanup();
static intptr_t SizeInBytes();
static const void* GenerateRecordsInto(intptr_t offset,
uint8_t* target_buffer);
static void RegisterExecutablePage(Page* page);
static void UnregisterExecutablePage(Page* page);
};
+23 -99
View File
@@ -2,99 +2,16 @@
// 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/globals.h"
#include "vm/unwinding_records.h"
#include "vm/globals.h"
#include "platform/unwinding_records.h"
#if defined(DART_HOST_OS_WINDOWS) && defined(TARGET_ARCH_X64)
namespace dart {
static HMODULE ntdll_module;
static decltype(
&::RtlAddGrowableFunctionTable) add_growable_function_table_func = nullptr;
static decltype(
&::RtlDeleteGrowableFunctionTable) delete_growable_function_table_func =
nullptr;
void UnwindingRecords::Init() {
ntdll_module =
LoadLibraryEx(L"ntdll.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
ASSERT(ntdll_module != nullptr);
// This pair of functions is not available on Windows 7.
add_growable_function_table_func =
reinterpret_cast<decltype(&::RtlAddGrowableFunctionTable)>(
::GetProcAddress(ntdll_module, "RtlAddGrowableFunctionTable"));
delete_growable_function_table_func =
reinterpret_cast<decltype(&::RtlDeleteGrowableFunctionTable)>(
::GetProcAddress(ntdll_module, "RtlDeleteGrowableFunctionTable"));
// Either both available, or both not available.
ASSERT((add_growable_function_table_func == nullptr) ==
(delete_growable_function_table_func == nullptr));
}
void UnwindingRecords::Cleanup() {
FreeLibrary(ntdll_module);
}
#pragma pack(push, 1)
//
// Refer to https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64
//
typedef unsigned char UBYTE;
typedef union _UNWIND_CODE {
struct {
UBYTE CodeOffset;
UBYTE UnwindOp : 4;
UBYTE OpInfo : 4;
};
USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;
typedef struct _UNWIND_INFO {
UBYTE Version : 3;
UBYTE Flags : 5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes;
UBYTE FrameRegister : 4;
UBYTE FrameOffset : 4;
UNWIND_CODE UnwindCode[2];
} UNWIND_INFO, *PUNWIND_INFO;
static constexpr int kPushRbpInstructionLength = 1;
static const int kMovRbpRspInstructionLength = 3;
static constexpr int kRbpPrefixLength =
kPushRbpInstructionLength + kMovRbpRspInstructionLength;
static constexpr int kRBP = 5;
struct GeneratedCodeUnwindInfo {
UNWIND_INFO unwind_info;
GeneratedCodeUnwindInfo() {
unwind_info.Version = 1;
unwind_info.Flags = UNW_FLAG_NHANDLER;
unwind_info.SizeOfProlog = kRbpPrefixLength;
unwind_info.CountOfCodes = 2;
unwind_info.FrameRegister = kRBP;
unwind_info.FrameOffset = 0;
unwind_info.UnwindCode[0].CodeOffset = kRbpPrefixLength;
unwind_info.UnwindCode[0].UnwindOp = 3; // UWOP_SET_FPREG
unwind_info.UnwindCode[0].OpInfo = 0;
unwind_info.UnwindCode[1].CodeOffset = kPushRbpInstructionLength;
unwind_info.UnwindCode[1].UnwindOp = 0; // UWOP_PUSH_NONVOL
unwind_info.UnwindCode[1].OpInfo = kRBP;
}
};
struct CodeRangeUnwindingRecord {
void* dynamic_table;
uint32_t runtime_function_count;
GeneratedCodeUnwindInfo unwind_info;
intptr_t exception_handler;
RUNTIME_FUNCTION runtime_function[1];
};
#pragma pack(pop)
static void InitUnwindingRecord(intptr_t offset,
CodeRangeUnwindingRecord* record,
size_t code_size_in_bytes) {
@@ -106,11 +23,12 @@ static void InitUnwindingRecord(intptr_t offset,
record->runtime_function_count = 1;
}
const intptr_t kReservedUnwindingRecordsSizeBytes = 64;
COMPILE_ASSERT(kReservedUnwindingRecordsSizeBytes >
sizeof(CodeRangeUnwindingRecord));
intptr_t UnwindingRecords::SizeInBytes() {
return kReservedUnwindingRecordsSizeBytes;
const void* UnwindingRecords::GenerateRecordsInto(intptr_t offset,
uint8_t* target_buffer) {
CodeRangeUnwindingRecord* record =
new (target_buffer) CodeRangeUnwindingRecord();
InitUnwindingRecord(offset, record, offset);
return target_buffer;
}
// Special exception-unwinding records are put at the end of executable
@@ -118,16 +36,20 @@ intptr_t UnwindingRecords::SizeInBytes() {
void UnwindingRecords::RegisterExecutablePage(Page* page) {
// Won't set up unwinding records on Windows 7, so users won't be able
// to benefit from proper unhandled exceptions filtering.
if (add_growable_function_table_func == nullptr) return;
auto function = static_cast<decltype(&::RtlAddGrowableFunctionTable)>(
UnwindingRecordsPlatform::GetAddGrowableFunctionTableFunc());
if (function == nullptr) return;
ASSERT(page->is_executable());
page->top_ -= kReservedUnwindingRecordsSizeBytes;
ASSERT(sizeof(CodeRangeUnwindingRecord) <=
UnwindingRecordsPlatform::SizeInBytes());
page->top_ -= UnwindingRecordsPlatform::SizeInBytes();
intptr_t unwinding_record_offset =
page->memory_->size() - kReservedUnwindingRecordsSizeBytes;
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());
if (add_growable_function_table_func(
if (function(
/*DynamicTable=*/&record->dynamic_table,
/*FunctionTable=*/record->runtime_function,
/*EntryCount=*/record->runtime_function_count,
@@ -139,15 +61,17 @@ void UnwindingRecords::RegisterExecutablePage(Page* page) {
}
void UnwindingRecords::UnregisterExecutablePage(Page* page) {
if (delete_growable_function_table_func == nullptr) return;
auto function = static_cast<decltype(&::RtlDeleteGrowableFunctionTable)>(
UnwindingRecordsPlatform::GetDeleteGrowableFunctionTableFunc());
if (function == nullptr) return;
ASSERT(page->is_executable() && !page->is_image());
intptr_t unwinding_record_offset =
page->memory_->size() - kReservedUnwindingRecordsSizeBytes;
page->memory_->size() - UnwindingRecordsPlatform::SizeInBytes();
CodeRangeUnwindingRecord* record =
reinterpret_cast<CodeRangeUnwindingRecord*>(
reinterpret_cast<uint8_t*>(page->memory_->start()) +
unwinding_record_offset);
delete_growable_function_table_func(record->dynamic_table);
function(record->dynamic_table);
}
} // namespace dart
-2
View File
@@ -19,8 +19,6 @@ import 'dylib_utils.dart';
void main(List<String> args) async {
// Test exercises JIT, Windows-only functionality.
if (!Platform.isWindows) return;
if (path.basenameWithoutExtension(Platform.executable) ==
'dart_precompiled_runtime') return;
if (args.length == 0) {
asyncStart();
final results = await Process.run(