Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/CodeBlockUnwind.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "Luau/CodeBlockUnwind.h"
3
4
#include "Luau/CodeAllocator.h"
5
#include "Luau/CodeGenCommon.h"
6
#include "Luau/UnwindBuilder.h"
7
8
#include <string.h>
9
#include <stdlib.h>
10
11
#if defined(_WIN32) && defined(CODEGEN_TARGET_X64)
12
13
#ifndef WIN32_LEAN_AND_MEAN
14
#define WIN32_LEAN_AND_MEAN
15
#endif
16
#ifndef NOMINMAX
17
#define NOMINMAX
18
#endif
19
#include <windows.h>
20
21
#elif (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
22
23
// __register_frame and __deregister_frame are defined in libgcc or libc++
24
// (depending on how it's built). We want to declare them as weak symbols
25
// so that if they're provided by a shared library, we'll use them, and if
26
// not, we'll disable some c++ exception handling support. However, if they're
27
// declared as weak and the definitions are linked in a static library
28
// that's not linked with whole-archive, then the symbols will technically be defined here,
29
// and the linker won't look for the strong ones in the library.
30
#ifndef LUAU_ENABLE_REGISTER_FRAME
31
#define REGISTER_FRAME_WEAK __attribute__((weak))
32
#else
33
#define REGISTER_FRAME_WEAK
34
#endif
35
36
extern "C" void __register_frame(const void*) REGISTER_FRAME_WEAK;
37
extern "C" void __deregister_frame(const void*) REGISTER_FRAME_WEAK;
38
39
extern "C" void __unw_add_dynamic_fde() __attribute__((weak));
40
#endif
41
42
#if defined(__APPLE__) && defined(CODEGEN_TARGET_A64)
43
#include <sys/sysctl.h>
44
#include <mach-o/loader.h>
45
#include <dlfcn.h>
46
47
struct unw_dynamic_unwind_sections_t
48
{
49
uintptr_t dso_base;
50
uintptr_t dwarf_section;
51
size_t dwarf_section_length;
52
uintptr_t compact_unwind_section;
53
size_t compact_unwind_section_length;
54
};
55
56
typedef int (*unw_add_find_dynamic_unwind_sections_t)(int (*)(uintptr_t addr, unw_dynamic_unwind_sections_t* info));
57
#endif
58
59
namespace Luau
60
{
61
namespace CodeGen
62
{
63
64
#if defined(__APPLE__) && defined(CODEGEN_TARGET_A64)
65
static int findDynamicUnwindSections(uintptr_t addr, unw_dynamic_unwind_sections_t* info)
66
{
67
// Define a minimal mach header for JIT'd code.
68
static const mach_header_64 kFakeMachHeader = {
69
MH_MAGIC_64,
70
CPU_TYPE_ARM64,
71
CPU_SUBTYPE_ARM64_ALL,
72
MH_DYLIB,
73
};
74
75
info->dso_base = (uintptr_t)&kFakeMachHeader;
76
info->dwarf_section = 0;
77
info->dwarf_section_length = 0;
78
info->compact_unwind_section = 0;
79
info->compact_unwind_section_length = 0;
80
return 1;
81
}
82
#endif
83
84
#if (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
85
static void visitFdeEntries(char* pos, void (*cb)(const void*))
86
{
87
// When using glibc++ unwinder, we need to call __register_frame/__deregister_frame on the entire .eh_frame data
88
// When using libc++ unwinder (libunwind), each FDE has to be handled separately
89
// libc++ unwinder is the macOS unwinder, but on Linux the unwinder depends on the library the executable is linked with
90
// __unw_add_dynamic_fde is specific to libc++ unwinder, as such we determine the library based on its existence
91
if (__unw_add_dynamic_fde == nullptr)
92
return cb(pos);
93
94
for (;;)
95
{
96
unsigned partLength;
97
memcpy(&partLength, pos, sizeof(partLength));
98
99
if (partLength == 0) // Zero-length section signals completion
100
break;
101
102
unsigned partId;
103
memcpy(&partId, pos + 4, sizeof(partId));
104
105
if (partId != 0) // Skip CIE part
106
cb(pos); // CIE is found using an offset in FDE
107
108
pos += partLength + 4;
109
}
110
}
111
#endif
112
113
void* createBlockUnwindInfo(void* context, uint8_t* block, size_t blockSize, size_t& beginOffset)
114
{
115
UnwindBuilder* unwind = (UnwindBuilder*)context;
116
117
// All unwinding related data is placed together at the start of the block
118
size_t unwindSize = unwind->getUnwindInfoSize(blockSize);
119
unwindSize = (unwindSize + (kCodeAlignment - 1)) & ~(kCodeAlignment - 1); // Match code allocator alignment
120
CODEGEN_ASSERT(blockSize >= unwindSize);
121
122
char* unwindData = (char*)block;
123
[[maybe_unused]] size_t functionCount = unwind->finalize(unwindData, unwindSize, block, blockSize);
124
125
#if defined(_WIN32) && defined(CODEGEN_TARGET_X64)
126
127
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
128
if (!RtlAddFunctionTable((RUNTIME_FUNCTION*)block, uint32_t(functionCount), uintptr_t(block)))
129
{
130
CODEGEN_ASSERT(!"Failed to allocate function table");
131
return nullptr;
132
}
133
#endif
134
135
#elif (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
136
if (!&__register_frame)
137
return nullptr;
138
139
visitFdeEntries(unwindData, __register_frame);
140
#endif
141
142
#if defined(__APPLE__) && defined(CODEGEN_TARGET_A64)
143
// Starting from macOS 14, we need to register unwind section callback to state that our ABI doesn't require pointer authentication
144
// This might conflict with other JITs that do the same; unfortunately this is the best we can do for now.
145
static unw_add_find_dynamic_unwind_sections_t unw_add_find_dynamic_unwind_sections =
146
unw_add_find_dynamic_unwind_sections_t(dlsym(RTLD_DEFAULT, "__unw_add_find_dynamic_unwind_sections"));
147
static int regonce = unw_add_find_dynamic_unwind_sections ? unw_add_find_dynamic_unwind_sections(findDynamicUnwindSections) : 0;
148
CODEGEN_ASSERT(regonce == 0);
149
#endif
150
151
beginOffset = unwindSize + unwind->getBeginOffset();
152
return block;
153
}
154
155
void destroyBlockUnwindInfo(void* context, void* unwindData)
156
{
157
#if defined(_WIN32) && defined(CODEGEN_TARGET_X64)
158
159
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
160
if (!RtlDeleteFunctionTable((RUNTIME_FUNCTION*)unwindData))
161
CODEGEN_ASSERT(!"Failed to deallocate function table");
162
#endif
163
164
#elif (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
165
if (!&__deregister_frame)
166
{
167
CODEGEN_ASSERT(!"Cannot deregister unwind information");
168
return;
169
}
170
171
visitFdeEntries((char*)unwindData, __deregister_frame);
172
#endif
173
}
174
175
bool isUnwindSupported()
176
{
177
#if defined(_WIN32) && defined(CODEGEN_TARGET_X64)
178
return true;
179
#elif defined(__APPLE__) && defined(CODEGEN_TARGET_A64)
180
char ver[256];
181
size_t verLength = sizeof(ver);
182
// libunwind on macOS 12 and earlier (which maps to osrelease 21) assumes JIT frames use pointer authentication without a way to override that
183
return sysctlbyname("kern.osrelease", ver, &verLength, NULL, 0) == 0 && atoi(ver) >= 22;
184
#elif (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
185
return true;
186
#else
187
return false;
188
#endif
189
}
190
191
} // namespace CodeGen
192
} // namespace Luau
193
194