[vm] Use a non-native definition of the runtime function structure.

Attempting to use the native definition when available causes problems
when cross-compiling, and the original attempts to avoid this broke in
subtle ways. Instead, just make a non-native definition that mimics
the native definition and use that with reinterpret_casts when sending
uses of the non-native definition to Windows API calls that expect
a value of the native definition.

TEST=windows ci

Fixes: https://github.com/dart-lang/sdk/issues/60771
Change-Id: I347fcaf8cccd809a3d8e6f041cc7e360b0b8226e
Cq-Include-Trybots: luci.dart.try:vm-aot-win-release-arm64-try,vm-aot-win-release-x64-try,vm-win-release-arm64-try,vm-win-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64-try,pkg-win-release-try,pkg-win-release-arm64-try,dart-sdk-win-try,dart-sdk-win-arm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/430400
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Tess Strickland
2025-05-23 06:19:16 -07:00
parent 94a36d59ff
commit 9a2234461a
6 changed files with 75 additions and 80 deletions
+6 -6
View File
@@ -3,30 +3,30 @@
// BSD-style license that can be found in the LICENSE file.
#include "platform/unwinding_records.h"
#include "platform/globals.h"
namespace dart {
#if !(defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT) || \
defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT))
#if !defined(NEED_WINDOWS_UNWINDING_RECORDS)
intptr_t UnwindingRecordsPlatform::SizeInBytes() {
return 0;
}
#endif // !defined(DART_TARGET_OS_WINDOWS) && !defined(DART_HOST_OS_WINDOWS)
#endif // !defined(NEED_WINDOWS_UNWINDING_RECORDS)
// Also use empty definitions when running gen_snapshot on 64-bit Windows, as it
// does not use the ELF loader, which is the client of these methods.
#if !defined(DART_HOST_OS_WINDOWS) || !defined(ARCH_IS_64_BIT) || \
(defined(DART_PRECOMPILER) && !defined(TESTING))
#if !defined(UNWINDING_RECORDS_WINDOWS_HOST)
void UnwindingRecordsPlatform::RegisterExecutableMemory(
void* start,
intptr_t size,
void** pp_dynamic_table) {}
void UnwindingRecordsPlatform::UnregisterDynamicTable(void* p_dynamic_table) {}
#endif // !defined(DART_HOST_OS_WINDOWS) || ...
#endif // !defined(UNWINDING_RECORDS_WINDOWS_HOST)
} // namespace dart
+43 -32
View File
@@ -20,16 +20,20 @@ class UnwindingRecordsPlatform : public AllStatic {
static void UnregisterDynamicTable(void* p_dynamic_table);
};
// These definitions are only needed if targeting 64-bit Windows, or if the
// ELF loader may be used on 64-bit Windows.
//
// More specifically, a 64-bit Windows gen_snapshot that does not target
// 64-bit Windows does not need these definitions, as the generated snapshot
// should not contain Windows-specific unwinding records and gen_snapshot
// does not uses the ELF loader.
#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)))
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT) && \
(!defined(DART_PRECOMPILER) || defined(TESTING))
#define NEED_WINDOWS_UNWINDING_RECORDS 1
// Guard for code and definitions that are used when the 64-bit Windows runtime
// may make calls to the Windows APIs using the unwinding records information.
#define UNWINDING_RECORDS_WINDOWS_HOST 1
#elif defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
#define NEED_WINDOWS_UNWINDING_RECORDS 1
// Guard for code and definitions that are used when precompiling for
// a 64-bit Windows target without any runtime use (and thus, no API calls).
#define UNWINDING_RECORDS_WINDOWS_PRECOMPILER 1
#endif
#if defined(NEED_WINDOWS_UNWINDING_RECORDS)
#pragma pack(push, 1)
@@ -63,14 +67,6 @@ typedef struct _UNWIND_INFO {
UNWIND_CODE UnwindCode[2];
} UNWIND_INFO, *PUNWIND_INFO;
#if !defined(DART_HOST_OS_WINDOWS)
typedef struct _RUNTIME_FUNCTION {
ULONG BeginAddress;
ULONG EndAddress;
ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
#endif
static constexpr int kPushRbpInstructionLength = 1;
static const int kMovRbpRspInstructionLength = 3;
static constexpr int kRbpPrefixLength =
@@ -102,13 +98,20 @@ struct GeneratedCodeUnwindInfo {
static constexpr uint32_t kUnwindingRecordMagic = 0xAABBCCDD;
struct TargetRuntimeFunction {
ULONG BeginAddress;
ULONG EndAddress;
ULONG UnwindData;
};
struct CodeRangeUnwindingRecord {
void* dynamic_table;
uint32_t magic;
uint32_t runtime_function_count;
GeneratedCodeUnwindInfo unwind_info;
intptr_t exception_handler;
RUNTIME_FUNCTION runtime_function[1];
// Must be cast to a PRUNTIME_FUNCTION when passed to Windows APIs.
TargetRuntimeFunction runtime_function[1];
};
#elif defined(TARGET_ARCH_ARM64)
@@ -148,13 +151,6 @@ struct UNWIND_INFO {
uint32_t CodeWords : 5;
};
#if !defined(DART_HOST_OS_WINDOWS) || !defined(HOST_ARCH_ARM64)
typedef struct _RUNTIME_FUNCTION {
ULONG BeginAddress;
ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
#endif
/**
* Base on below doc, unwind record has 18 bits (unsigned) to encode function
* length, besides 2 LSB which are always 0.
@@ -218,6 +214,11 @@ struct UnwindData {
static const uint32_t kDefaultRuntimeFunctionCount = 1;
static constexpr uint32_t kUnwindingRecordMagic = 0xAABBCCEE;
struct TargetRuntimeFunction {
ULONG BeginAddress;
ULONG UnwindData;
};
struct CodeRangeUnwindingRecord {
void* dynamic_table;
uint32_t magic;
@@ -231,19 +232,29 @@ struct CodeRangeUnwindingRecord {
// than full size.
UnwindData<> unwind_info1;
// More RUNTIME_FUNCTION structs could follow below array because the number
// of RUNTIME_FUNCTION needed to cover given code range is computed at
// runtime.
RUNTIME_FUNCTION runtime_function[kDefaultRuntimeFunctionCount];
// An arbitrary number of runtime function structs follow the initial header
// as the number needed to cover the given code range is computed at runtime.
// Must be cast to a PRUNTIME_FUNCTION when passed to Windows APIs.
TargetRuntimeFunction runtime_function[kDefaultRuntimeFunctionCount];
};
#else
#error Unhandled Windows architecture.
#endif
// Since the definition of the RUNTIME_FUNCTION struct differs on X64
// and ARM64 Windows and the precompiler may be cross compiling between
// the two, TargetRuntimeFunction is defined above, which mimics the
// native RUNTIME_FUNCTION struct of the target.
//
// Make sure that the sizes match, so that TargetRuntimeFunction values
// can be used as RUNTIME_FUNCTION values and vice versa in the runtime.
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
static_assert(sizeof(TargetRuntimeFunction) == sizeof(RUNTIME_FUNCTION));
#endif
#pragma pack(pop)
#endif // (defined(DART_TARGET_OS_WINDOWS) || ...
#endif // defined(NEED_WINDOWS_UNWINDING_RECORDS)
} // namespace dart
+6 -7
View File
@@ -9,8 +9,7 @@
namespace dart {
#if (defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)) || \
(defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT))
#if defined(NEED_WINDOWS_UNWINDING_RECORDS)
#if defined(TARGET_ARCH_X64)
const intptr_t kReservedUnwindingRecordsSizeBytes = 64;
@@ -24,12 +23,11 @@ intptr_t UnwindingRecordsPlatform::SizeInBytes() {
return kReservedUnwindingRecordsSizeBytes;
}
#endif // defined(DART_TARGET_OS_WINDOWS) ...
#endif // defined(NEED_WINDOWS_UNWINDING_RECORDS)
// 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))
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
void UnwindingRecordsPlatform::RegisterExecutableMemory(
void* start,
@@ -44,7 +42,8 @@ void UnwindingRecordsPlatform::RegisterExecutableMemory(
uword end_num = start_num + size;
DWORD status = RtlAddGrowableFunctionTable(
pp_dynamic_table,
/*FunctionTable=*/record->runtime_function,
/*FunctionTable=*/
reinterpret_cast<PRUNTIME_FUNCTION>(record->runtime_function),
/*EntryCount=*/record->runtime_function_count,
/*MaximumEntryCount=*/record->runtime_function_count,
/*RangeBase=*/start_num,
@@ -58,6 +57,6 @@ void UnwindingRecordsPlatform::UnregisterDynamicTable(void* p_dynamic_table) {
RtlDeleteGrowableFunctionTable(p_dynamic_table);
}
#endif // defined(DART_HOST_OS_WINDOWS) ...
#endif // defined(UNWINDING_RECORDS_WINDOWS_HOST)
} // namespace dart
+7 -8
View File
@@ -3,27 +3,26 @@
// BSD-style license that can be found in the LICENSE file.
#include "vm/unwinding_records.h"
#include "vm/globals.h"
namespace dart {
#if !defined(DART_TARGET_OS_WINDOWS) || !defined(TARGET_ARCH_IS_64_BIT)
// The default definition when not precompiling for a Windows target.
#if !defined(UNWINDING_RECORDS_WINDOWS_PRECOMPILER)
const void* UnwindingRecords::GenerateRecordsInto(intptr_t offset,
uint8_t* target_buffer) {
return nullptr;
}
#endif // !defined(DART_TARGET_OS_WINDOWS)
#endif // !defined(UNWINDING_RECORDS_WINDOWS_PRECOMPILER)
// Also use empty definitions when running gen_snapshot on 64-bit Windows, as
// it does not use the ELF loader, which is the client of these methods.
#if !defined(DART_HOST_OS_WINDOWS) || !defined(ARCH_IS_64_BIT) || \
(defined(DART_PRECOMPILER) && !defined(TESTING))
// The default definition when the ElfLoader is not used or the VM is not
// running on a Windows system.
#if !defined(UNWINDING_RECORDS_WINDOWS_HOST)
void UnwindingRecords::RegisterExecutablePage(Page* page) {}
void UnwindingRecords::UnregisterExecutablePage(Page* page) {}
#endif // !defined(DART_HOST_OS_WINDOWS)
#endif // !defined(UNWINDING_RECORDS_WINDOWS_HOST)
} // namespace dart
+1
View File
@@ -6,6 +6,7 @@
#ifndef RUNTIME_VM_UNWINDING_RECORDS_H_
#define RUNTIME_VM_UNWINDING_RECORDS_H_
#include "platform/unwinding_records.h"
#include "vm/allocation.h"
#include "vm/heap/page.h"
+12 -27
View File
@@ -4,18 +4,11 @@
#include "vm/unwinding_records.h"
#include "vm/globals.h"
#include "platform/unwinding_records.h"
#include "platform/globals.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)))
#if defined(NEED_WINDOWS_UNWINDING_RECORDS)
static void InitUnwindingRecord(intptr_t offset,
CodeRangeUnwindingRecord* record,
size_t code_size_in_bytes) {
@@ -34,13 +27,13 @@ static void InitUnwindingRecord(intptr_t offset,
// 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
// 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) +
sizeof(TargetRuntimeFunction) +
kDefaultRuntimeFunctionCount);
uint32_t runtime_function_index = 0;
@@ -48,7 +41,7 @@ static void InitUnwindingRecord(intptr_t offset,
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
// 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.
@@ -95,11 +88,9 @@ static void InitUnwindingRecord(intptr_t offset,
#endif
record->magic = kUnwindingRecordMagic;
}
#endif // defined(NEED_WINDOWS_UNWINDING_RECORDS)
#endif // defined(DART_TARGET_OS_WINDOWS) || ...
#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
#if defined(UNWINDING_RECORDS_WINDOWS_PRECOMPILER)
const void* UnwindingRecords::GenerateRecordsInto(intptr_t offset,
uint8_t* target_buffer) {
CodeRangeUnwindingRecord* record =
@@ -107,15 +98,9 @@ const void* UnwindingRecords::GenerateRecordsInto(intptr_t offset,
InitUnwindingRecord(offset, record, offset);
return target_buffer;
}
#endif // defined(UNWINDING_RECORDS_WINDOWS_PRECOMPILER)
#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))
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
// Special exception-unwinding records are put at the end of executable
// page on Windows for 64-bit applications.
void UnwindingRecords::RegisterExecutablePage(Page* page) {
@@ -132,7 +117,8 @@ void UnwindingRecords::RegisterExecutablePage(Page* page) {
RELEASE_ASSERT(record->magic == kUnwindingRecordMagic);
DWORD status = RtlAddGrowableFunctionTable(
/*DynamicTable=*/&record->dynamic_table,
/*FunctionTable=*/record->runtime_function,
/*FunctionTable=*/
reinterpret_cast<PRUNTIME_FUNCTION>(record->runtime_function),
/*EntryCount=*/record->runtime_function_count,
/*MaximumEntryCount=*/record->runtime_function_count,
/*RangeBase=*/page->memory_->start(),
@@ -153,7 +139,6 @@ void UnwindingRecords::UnregisterExecutablePage(Page* page) {
RELEASE_ASSERT(record->magic == kUnwindingRecordMagic);
RtlDeleteGrowableFunctionTable(record->dynamic_table);
}
#endif // defined(DART_HOST_OS_WINDOWS) ...
#endif // defined(UNWINDING_RECORDS_WINDOWS_HOST)
} // namespace dart