forked from JuliaLang/julia
-
Notifications
You must be signed in to change notification settings - Fork 0
/
interpreter-stacktrace.c
430 lines (383 loc) · 14.6 KB
/
interpreter-stacktrace.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
// This file is a part of Julia. License is MIT: https://julialang.org/license
// #include'd from interpreter.c
// Backtrace support
#if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) || defined(_OS_WINDOWS_)
extern uintptr_t __start_jl_interpreter_frame_val;
uintptr_t __start_jl_interpreter_frame = (uintptr_t)&__start_jl_interpreter_frame_val;
extern uintptr_t __stop_jl_interpreter_frame_val;
uintptr_t __stop_jl_interpreter_frame = (uintptr_t)&__stop_jl_interpreter_frame_val;
#define SECT_INTERP JL_SECTION("jl_interpreter_frame_val")
#if defined(_CPU_X86_) && defined(_OS_WINDOWS_)
#define MANGLE(x) "@" x "@8"
#else
#define MANGLE(x) x
#endif
#if defined(_OS_LINUX_) || defined(_OS_FREEBSD_)
#if defined(_CPU_ARM_)
# define ASM_FUNCTION_TYPE "%function"
#else
# define ASM_FUNCTION_TYPE "@function"
#endif
#define ASM_ENTRY \
".text\n" \
".p2align 4,0x90\n" \
".global enter_interpreter_frame\n" \
".type enter_interpreter_frame," ASM_FUNCTION_TYPE "\n"
#if defined(_OS_LINUX_)
#define ASM_END ".previous\n"
#else
#define ASM_END
#endif
#else
#define ASM_ENTRY \
".text\n" \
".globl enter_interpreter_frame\n"
#define ASM_END
#endif
#elif defined(_OS_DARWIN_)
extern uintptr_t __start_jl_interpreter_frame_val __asm("section$start$__TEXT$__jif");
uintptr_t __start_jl_interpreter_frame = (uintptr_t)&__start_jl_interpreter_frame_val;
extern uintptr_t __stop_jl_interpreter_frame_val __asm("section$end$__TEXT$__jif");
uintptr_t __stop_jl_interpreter_frame = (uintptr_t)&__stop_jl_interpreter_frame_val;
#define SECT_INTERP JL_SECTION("__TEXT,__jif")
#define MANGLE(x) "_" x
#define ASM_ENTRY \
".section __TEXT,__text,regular,pure_instructions\n" \
".globl _enter_interpreter_frame\n"
#define ASM_END ".previous"
#else
#define SECT_INTERP
#define NO_INTERP_BT
#warning "Interpreter backtraces not implemented for this platform"
#endif
#define STR(x) #x
#define XSTR(x) STR(x)
// This function is special. The unwinder looks for this function to find interpreter
// stack frames.
#ifdef _CPU_X86_64_
// Instructions: Make sure that MAX_INTERP_STATE_SIZE is a multiple of
// alignof(struct interpreter_state) and larger than
// sizeof(struct interpreter_state). Additionally, make sure that
// MAX_INTERP_STATE_SIZE+STACK_PADDING+8 is a multiple of 16 to
// ensure the proper stack alignment.
#define MAX_INTERP_STATE_SIZE 72
#define STACK_PADDING 0
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE, "Stack layout invariants violated.");
static_assert(MAX_INTERP_STATE_SIZE % alignof(interpreter_state) == 0, "Stack layout invariants violated");
static_assert(((MAX_INTERP_STATE_SIZE + STACK_PADDING + 8) % 16) == 0, "Stack layout invariants violated");
#ifdef _OS_WINDOWS_
size_t TOTAL_STACK_PADDING = STACK_PADDING + 32;
#else
size_t TOTAL_STACK_PADDING = STACK_PADDING;
#endif
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
".cfi_startproc\n"
"\tsubq $" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING)", %rsp\n"
".cfi_def_cfa_offset " XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING)" + 8\n"
#ifdef _OS_WINDOWS_
#define ARG1_REG "rcx"
#else
#define ARG1_REG "rdi"
#endif
"\tmovq %" ARG1_REG ", %rax\n"
"\tleaq " XSTR(STACK_PADDING) "(%rsp), %" ARG1_REG "\n"
// Zero out the src and mi fields
"\tmovq $0, 0(%" ARG1_REG ")\n"
"\tmovq $0, 8(%" ARG1_REG ")\n"
#ifdef _OS_WINDOWS_
// Make space for the register parameter area
"\tsubq $32, %rsp\n"
#endif
// The L here conviences the OS X linker not to terminate the unwind info early
"Lenter_interpreter_frame_start_val:\n"
"\tcallq *%rax\n"
"Lenter_interpreter_frame_end_val:\n"
#ifdef _OS_WINDOWS_
"\taddq $32, %rsp\n"
#endif
"\taddq $" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING)", %rsp\n"
#ifndef _OS_DARWIN_
// Somehow this throws off compact unwind info on OS X
".cfi_def_cfa_offset 8\n"
#endif
"\tretq\n"
".cfi_endproc\n"
ASM_END
);
#define CALLBACK_ABI
#elif defined(_CPU_X86_)
#define MAX_INTERP_STATE_SIZE 36
#ifdef _OS_WINDOWS_
#define STACK_PADDING 4
#else
#define STACK_PADDING 8
#endif
size_t TOTAL_STACK_PADDING = STACK_PADDING;
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE, "Stack layout invariants violated");
static_assert(MAX_INTERP_STATE_SIZE % alignof(interpreter_state) == 0, "Stack layout invariants violated");
#ifndef _OS_WINDOWS_
static_assert((MAX_INTERP_STATE_SIZE + STACK_PADDING + 4) % 16 == 0, "Stack layout invariants violated");
#endif
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
".cfi_startproc\n"
#ifdef _OS_WINDOWS_
/*
* On win32, we set -mincoming-stack-boundary=2. This causes GCC to emit stack
* realignment gadgets into the prologue of every function. Unfortunately for
* us there are two different kinds of such gadgets and since we don't know
* which one the target function is going to use, we can't use the same trick
* as everywhere else. From https://gcc.gnu.org/ml/gcc/2007-12/msg00503.html,
* the two prologues are:
*
* pushl %ebp
* movl %esp, %ebp
* andl $-16, %esp
*
* and
*
* pushl %edi // Save callee save reg edi
* leal 8(%esp), %edi // Save address of parameter frame
* andl $-16, %esp // Align local stack
* pushl $4(%edi) // save return address
* pushl %ebp // save old ebp
* movl %esp, %ebp // point ebp to pseudo frame
*
* From the perspective of the unwinder, the first case looks like a regular
* (without the realignment gadget) stack frame. However, for the second one,
* the compiler deliberately constructs a "fake stack frame" that has an
* incorrect stack address for the previous frame. To work around all of this,
* use ebp based addressing on win32
*/
#define FP_CAPTURE_OFFSET MAX_INTERP_STATE_SIZE
#define ENTRY_OFFSET 8
"\tpushl %ebp\n"
".cfi_def_cfa_offset " XSTR(ENTRY_OFFSET)"\n"
"\tmovl %esp, %ebp\n"
#else
#define ENTRY_OFFSET 4
#endif
"\tsubl $" XSTR(MAX_INTERP_STATE_SIZE) ", %esp\n"
".cfi_def_cfa_offset " XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(ENTRY_OFFSET) "\n"
"\tmovl %ecx, %eax\n"
"\tmovl %esp, %ecx\n"
// Zero out the src and mi fields
"\tmovl $0, (%esp)\n"
"\tmovl $0, 4(%esp)\n"
// Restore 16 byte stack alignment
// Technically not necessary on windows, because we don't assume this
// alignment, but let's be nice if we ever start doing that.
"\tsubl $" XSTR(STACK_PADDING) ", %esp\n"
".cfi_def_cfa_offset " XSTR(STACK_PADDING) " + " XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(ENTRY_OFFSET) "\n"
"Lenter_interpreter_frame_start_val:\n"
"\tcalll *%eax\n"
"Lenter_interpreter_frame_end_val:\n"
"\taddl $" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ", %esp\n"
#ifdef _OS_WINDOWS_
".cfi_def_cfa_offset 8\n"
"\tpopl %ebp\n"
#endif
".cfi_def_cfa_offset 4\n"
"\tret\n"
".cfi_endproc\n"
ASM_END
);
#define CALLBACK_ABI __attribute__((fastcall))
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE, "Update assembly code above");
#elif defined(_CPU_AARCH64_)
#define MAX_INTERP_STATE_SIZE 64
#define STACK_PADDING 16
// Check that the interpreter state can fit
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE,
"Stack layout invariants violated.");
// Check that the alignment of the type is satisfied
// (16 is stack alignment at function boundary)
static_assert(alignof(interpreter_state) <= 16, "Stack layout invariants violated");
static_assert(STACK_PADDING % alignof(interpreter_state) == 0,
"Stack layout invariants violated");
// Check that ABI stack alignment requirement is maintained.
static_assert(((MAX_INTERP_STATE_SIZE + STACK_PADDING) % 16) == 0,
"Stack layout invariants violated");
// Check that the padding is large enough for lr.
static_assert(STACK_PADDING >= sizeof(void*), "Stack layout invariants violated");
size_t TOTAL_STACK_PADDING = STACK_PADDING;
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
".cfi_startproc\n"
// Save lr
"\tstr x30, [sp, #-(" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ")]!\n"
"\t.cfi_def_cfa_offset (" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ")\n"
"\t.cfi_offset 30, -(" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ")\n"
"\tmov x2, x0\n"
// Zero out the src and mi fields
"\tstp xzr, xzr, [sp, " XSTR(STACK_PADDING) "]\n"
"\tadd x0, sp, " XSTR(STACK_PADDING) "\n"
"Lenter_interpreter_frame_start_val:\n"
"\tblr x2\n"
"Lenter_interpreter_frame_end_val:\n"
"\tldr x30, [sp], (" XSTR(MAX_INTERP_STATE_SIZE) " + " XSTR(STACK_PADDING) ")\n"
"\t.cfi_restore 30\n"
"\t.cfi_def_cfa_offset 0\n"
"\tret\n"
".cfi_endproc\n"
ASM_END
);
#define CALLBACK_ABI
#elif defined(_CPU_ARM_)
#define MAX_INTERP_STATE_SIZE 48
// Check that the interpreter state can fit
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE,
"Stack layout invariants violated.");
// Check that the alignment of the type is satisfied
// (16 is what we realign the stack to)
static_assert(alignof(interpreter_state) <= 16, "Stack layout invariants violated");
size_t TOTAL_STACK_PADDING = 0;
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
".fnstart\n"
"\tpush {fp, lr}\n"
"\t.save {fp, lr}\n"
"\t.setfp fp, sp, #4\n"
"\tadd fp, sp, #4\n"
"\tmov r2, r0\n"
// Reserve enough space and realign stack to 16bytes
// The realignment is for consistency with every other architectures.
// It isn't strictly necessary since we currently do not rely on it.
"\tsub sp, sp, #" XSTR(MAX_INTERP_STATE_SIZE) "\n"
"\tbic sp, sp, #15\n"
// Zero out the src and mi field
"\tmov ip, #0\n"
"\tstr ip, [sp]\n"
"\tstr ip, [sp, #4]\n"
"\tmov r0, sp\n"
"Lenter_interpreter_frame_start_val:\n"
"\tblx r2\n"
"Lenter_interpreter_frame_end_val:\n"
"\tsub sp, fp, #4\n"
"\tpop {fp, pc}\n"
"\t.fnend\n"
ASM_END
);
#define CALLBACK_ABI
#elif defined(_CPU_PPC64_)
/**
* Implementation notes:
*
* This needs to follow the PPC ELFv2 ABI. Which means that there is a localentry
* and a global entry. The local entry expects r2/TOC to be set correctly, while
* the global entry expects r12 to be set to the function address, and from there
* restores r2/TOC. The function pointer we are getting passed point to the global
* entry and thus we need to set r12 correctly.
*
* - LR is stored in the caller
* - r1/SP is a back-chain that needs to be atomically updated
*/
#define MAX_INTERP_STATE_SIZE 64
#define MIN_STACK 32
#define STACK_PADDING 0
#define STACK_SIZE (MIN_STACK + MAX_INTERP_STATE_SIZE + STACK_PADDING)
size_t TOTAL_STACK_PADDING = MIN_STACK;
// Check that the interpreter state can fit
static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE,
"Stack layout invariants violated.");
// Check that the alignment of the type is satisfied
static_assert(alignof(interpreter_state) <= 16, "Stack layout invariants violated");
// Check that ABI stack alignment requirement is maintained.
static_assert(STACK_SIZE % 16 == 0, "Stack layout invariants violated");
static_assert(MIN_STACK % 16 == 0, "Stack layout invariants violated");
asm(
ASM_ENTRY
MANGLE("enter_interpreter_frame") ":\n"
"\taddis 2, 12, .TOC.-enter_interpreter_frame@ha\n"
"\taddi 2, 2, .TOC.-enter_interpreter_frame@l\n"
"\t.localentry enter_interpreter_frame, .-enter_interpreter_frame\n"
".cfi_startproc\n"
// store LR
"\tmflr 0\n"
"\tstd 0, 16(1)\n"
".cfi_offset lr, 16\n"
// set up stack frame
"\tstdu 1, -" XSTR(STACK_SIZE) "(1)\n"
".cfi_adjust_cfa_offset " XSTR(STACK_SIZE) "\n"
"\tmtctr 3\n" // move arg1 (func pointer) to ctr
"\tmr 12, 3\n" // move func pointer to r12 if we jump to global entry point
"\tcal 3, " XSTR(MIN_STACK) "(1)\n" // move pointer to INTERP_STATE to arg1
// zero out src and mi field
"\tli 6, 0\n"
"\tstd 6, 0(3)\n"
"\tstd 6, 8(3)\n"
// store TOC
"\tstd 2, 24(1)\n"
"Lenter_interpreter_frame_start_val:\n"
"\tbctrl\n"
"Lenter_interpreter_frame_end_val:\n"
// restore TOC
"\tld 2, 24(1)\n"
// restore stack frame
"\tld 1, 0(1)\n"
// restore LR
"\tld 0, 16(1)\n"
"\tmtlr 0\n"
".cfi_same_value lr\n"
"\tblr\n"
".cfi_endproc\n"
ASM_END
);
#define CALLBACK_ABI
#else
#warning "Interpreter backtraces not implemented for this platform"
#define NO_INTERP_BT
#endif
#ifndef NO_INTERP_BT
extern uintptr_t enter_interpreter_frame_start_val asm("Lenter_interpreter_frame_start_val");
extern uintptr_t enter_interpreter_frame_end_val asm("Lenter_interpreter_frame_end_val");
uintptr_t enter_interpreter_frame_start = (uintptr_t)&enter_interpreter_frame_start_val;
uintptr_t enter_interpreter_frame_end = (uintptr_t)&enter_interpreter_frame_end_val;
JL_DLLEXPORT int jl_is_interpreter_frame(uintptr_t ip)
{
return __start_jl_interpreter_frame <= ip && ip <= __stop_jl_interpreter_frame;
}
JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip)
{
return enter_interpreter_frame_start <= ip && ip <= enter_interpreter_frame_end;
}
JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining)
{
#ifdef FP_CAPTURE_OFFSET
interpreter_state *s = (interpreter_state *)(fp-FP_CAPTURE_OFFSET);
#else
interpreter_state *s = (interpreter_state *)(sp+TOTAL_STACK_PADDING);
#endif
if (space_remaining <= 1)
return 0;
// Sentinel value to indicate an interpreter frame
data[0] = (uintptr_t)-1;
data[1] = s->mi ? (uintptr_t)s->mi : s->src ? (uintptr_t)s->src : (uintptr_t)jl_nothing;
data[2] = (uintptr_t)s->ip;
return 2;
}
extern void * CALLBACK_ABI enter_interpreter_frame(void * CALLBACK_ABI (*callback)(interpreter_state *, void *), void *arg);
#else
JL_DLLEXPORT int jl_is_interpreter_frame(uintptr_t ip)
{
return 0;
}
JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip)
{
return 0;
}
JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining)
{
return 0;
}
#define CALLBACK_ABI
void *NOINLINE enter_interpreter_frame(void *(*callback)(interpreter_state *, void *), void *arg) {
interpreter_state state = {};
return callback(&state, arg);
}
#endif