mirror of
https://github.com/dart-lang/sdk
synced 2024-09-04 16:03:44 +00:00
![Alexander Markov](/assets/img/avatar_default.png)
TEST=ci Fixes https://github.com/flutter/flutter/issues/141786 Change-Id: I9c6620b8ee25e36631da2b1213002e8bf30b6473 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/347065 Reviewed-by: Alexander Aprelev <aam@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
247 lines
7.7 KiB
C++
247 lines
7.7 KiB
C++
// 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_TARGET_OS_WINDOWS) || 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 uint16_t USHORT;
|
|
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;
|
|
|
|
#if !defined(DART_HOST_OS_WINDOWS)
|
|
typedef uint32_t ULONG;
|
|
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 =
|
|
kPushRbpInstructionLength + kMovRbpRspInstructionLength;
|
|
static constexpr int kRBP = 5;
|
|
|
|
#ifndef UNW_FLAG_NHANDLER
|
|
#define UNW_FLAG_NHANDLER 0
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
static constexpr uint32_t kUnwindingRecordMagic = 0xAABBCCDD;
|
|
|
|
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];
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
#elif (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS)) && \
|
|
defined(TARGET_ARCH_ARM64)
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
// ARM64 unwind codes are defined in below doc.
|
|
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
|
|
enum UnwindOp8Bit {
|
|
OpNop = 0xE3,
|
|
OpAllocS = 0x00,
|
|
OpSaveFpLr = 0x40,
|
|
OpSaveFpLrX = 0x80,
|
|
OpSetFp = 0xE1,
|
|
OpAddFp = 0xE2,
|
|
OpEnd = 0xE4,
|
|
};
|
|
|
|
typedef uint32_t UNWIND_CODE;
|
|
|
|
constexpr UNWIND_CODE Combine8BitUnwindCodes(uint8_t code0 = OpNop,
|
|
uint8_t code1 = OpNop,
|
|
uint8_t code2 = OpNop,
|
|
uint8_t code3 = OpNop) {
|
|
return static_cast<uint32_t>(code0) | (static_cast<uint32_t>(code1) << 8) |
|
|
(static_cast<uint32_t>(code2) << 16) |
|
|
(static_cast<uint32_t>(code3) << 24);
|
|
}
|
|
|
|
// UNWIND_INFO defines the static part (first 32-bit) of the .xdata record in
|
|
// below doc.
|
|
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#xdata-records
|
|
struct UNWIND_INFO {
|
|
uint32_t FunctionLength : 18;
|
|
uint32_t Version : 2;
|
|
uint32_t X : 1;
|
|
uint32_t E : 1;
|
|
uint32_t EpilogCount : 5;
|
|
uint32_t CodeWords : 5;
|
|
};
|
|
|
|
#if !defined(DART_HOST_OS_WINDOWS)
|
|
typedef uint32_t ULONG;
|
|
typedef uint32_t DWORD;
|
|
typedef struct _RUNTIME_FUNCTION {
|
|
ULONG BeginAddress;
|
|
ULONG EndAddress;
|
|
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.
|
|
* https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#xdata-records
|
|
*/
|
|
static const int kMaxFunctionLength = ((1 << 18) - 1) << 2;
|
|
|
|
static constexpr int kDefaultNumberOfUnwindCodeWords = 1;
|
|
static constexpr int kMaxExceptionThunkSize = 16;
|
|
static constexpr int kFunctionLengthShiftSize = 2;
|
|
static constexpr int kFunctionLengthMask = (1 << kFunctionLengthShiftSize) - 1;
|
|
|
|
// Generate an unwind code for "stp fp, lr, [sp, #pre_index_offset]!".
|
|
inline uint8_t MakeOpSaveFpLrX(int pre_index_offset) {
|
|
// See unwind code save_fplr_x in
|
|
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
|
|
ASSERT(pre_index_offset <= -8);
|
|
ASSERT(pre_index_offset >= -512);
|
|
constexpr int kShiftSize = 3;
|
|
constexpr int kShiftMask = (1 << kShiftSize) - 1;
|
|
ASSERT((pre_index_offset & kShiftMask) == 0);
|
|
USE(kShiftMask);
|
|
// Solve for Z where -(Z+1)*8 = pre_index_offset.
|
|
int encoded_value = (-pre_index_offset >> kShiftSize) - 1;
|
|
return OpSaveFpLrX | encoded_value;
|
|
}
|
|
|
|
template <int kNumberOfUnwindCodeWords = kDefaultNumberOfUnwindCodeWords>
|
|
struct UnwindData {
|
|
UNWIND_INFO unwind_info;
|
|
UNWIND_CODE unwind_codes[kNumberOfUnwindCodeWords];
|
|
|
|
UnwindData() {
|
|
memset(&unwind_info, 0, sizeof(UNWIND_INFO));
|
|
unwind_info.X = 0; // no exception handler
|
|
unwind_info.CodeWords = kNumberOfUnwindCodeWords;
|
|
|
|
// Generate unwind codes for the following prolog:
|
|
//
|
|
// stp fp, lr, [sp, #-kCallerSPOffset]!
|
|
// mov fp, sp
|
|
//
|
|
// This is a very rough approximation of the actual function prologs used in
|
|
// V8. In particular, we often push other data before the (fp, lr) pair,
|
|
// meaning the stack pointer computed for the caller frame is wrong. That
|
|
// error is acceptable when the unwinding info for the caller frame also
|
|
// depends on fp rather than sp, as is the case for V8 builtins and runtime-
|
|
// generated code.
|
|
static_assert(kNumberOfUnwindCodeWords >= 1);
|
|
uword kCallerSPOffset = -16;
|
|
unwind_codes[0] = Combine8BitUnwindCodes(
|
|
OpSetFp, MakeOpSaveFpLrX(kCallerSPOffset), OpEnd);
|
|
|
|
// Fill the rest with nops.
|
|
for (int i = 1; i < kNumberOfUnwindCodeWords; ++i) {
|
|
unwind_codes[i] = Combine8BitUnwindCodes();
|
|
}
|
|
}
|
|
};
|
|
|
|
static const uint32_t kDefaultRuntimeFunctionCount = 1;
|
|
static constexpr uint32_t kUnwindingRecordMagic = 0xAABBCCEE;
|
|
|
|
struct CodeRangeUnwindingRecord {
|
|
void* dynamic_table;
|
|
uint32_t magic;
|
|
uint32_t runtime_function_count;
|
|
UnwindData<> unwind_info;
|
|
uint32_t exception_handler;
|
|
|
|
// For Windows ARM64 unwinding, register 2 unwind_info for each code range,
|
|
// unwind_info for all full size ranges (1MB - 4 bytes) and unwind_info1 for
|
|
// the remaining non full size range. There is at most 1 range which is less
|
|
// 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];
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
#endif // (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS))
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_PLATFORM_UNWINDING_RECORDS_H_
|