Skip to content

Commit 8fd607a

Browse files
authored
signals: Implement fake_stack_pop for Linux on x86_64 and i686 (JuliaLang#60013)
Add fake_stack_pop implementation for Linux platforms to improve stack unwinding in debuggers when analyzing core dumps from signals like SIGQUIT. This provides proper DWARF Call Frame Information (CFI) directives that help unwinders locate saved register values on the manipulated stack. The implementation follows the same pattern as the existing macOS version, with fake_stack_pop now unified in signals-unix.c to support both platforms: - x86_64: Uses .cfi_def_cfa %rsp with offsets for %rip and %rsp - i686: Uses .cfi_def_cfa %esp with offsets for %eip and %esp - aarch64: Uses .cfi_def_cfa sp with offsets for lr and sp The jl_call_in_ctx function on Linux now sets up the stack similarly to jl_call_in_state on macOS, pushing saved register state and a return address pointing to fake_stack_pop to enable proper unwinding. 🤖 Generated with Claude Code
1 parent 18aa237 commit 8fd607a

File tree

3 files changed

+207
-79
lines changed

3 files changed

+207
-79
lines changed

src/julia_threads.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,13 @@ typedef struct _jl_tls_states_t {
186186
#else
187187
void *signal_stack;
188188
size_t signal_stack_size;
189+
#endif
190+
#if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) || defined(_OS_OPENBSD_)
191+
// Saved context from jl_call_in_ctx for stack unwinding
192+
uintptr_t signal_ctx_pc;
193+
uintptr_t signal_ctx_sp;
194+
void (*signal_ctx_fptr)(void);
195+
uintptr_t signal_ctx_arg;
189196
#endif
190197
jl_thread_t system_id;
191198
_Atomic(int16_t) suspend_count;

src/signals-mach.c

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -220,36 +220,36 @@ typedef arm_exception_state64_t host_exception_state_t;
220220
#define HOST_EXCEPTION_STATE_COUNT ARM_EXCEPTION_STATE64_COUNT
221221
#endif
222222

223-
// create a fake function that describes the variable manipulations in jl_call_in_state
224-
__attribute__((naked)) static void fake_stack_pop(void)
223+
// Create a fake function that describes the register manipulations in jl_call_in_state
224+
// The callee-saved registers still may get smashed (by the cdecl fptr), since we didn't explicitly copy all of the
225+
// state to the stack (to build a real sigreturn frame).
226+
__attribute__((naked)) static void jl_fake_signal_return(void)
225227
{
226-
#ifdef _CPU_X86_64_
227-
__asm__ volatile (
228-
" .cfi_signal_frame\n"
229-
" .cfi_def_cfa %rsp, 0\n" // CFA here uses %rsp directly
230-
" .cfi_offset %rip, 0\n" // previous value of %rip at CFA
231-
" .cfi_offset %rsp, 8\n" // previous value of %rsp at CFA
232-
" nop\n"
233-
);
228+
#if defined(_CPU_X86_64_)
229+
__asm__(
230+
" .cfi_signal_frame\n"
231+
" .cfi_def_cfa %rsp, 0\n" // CFA here uses %rsp directly
232+
" .cfi_offset %rip, 0\n" // previous value of %rip at CFA
233+
" .cfi_offset %rsp, 8\n" // previous value of %rsp at CFA
234+
" ud2\n"
235+
" ud2\n"
236+
);
234237
#elif defined(_CPU_AARCH64_)
235-
__asm__ volatile (
236-
" .cfi_signal_frame\n"
237-
" .cfi_def_cfa sp, 0\n" // use sp as fp here
238-
" .cfi_offset lr, 0\n"
239-
" .cfi_offset sp, 8\n"
240-
// Anything else got smashed, since we didn't explicitly copy all of the
241-
// state object to the stack (to build a real sigreturn frame).
242-
// This is also not quite valid, since the AArch64 DWARF spec lacks the ability to define how to restore the LR register correctly,
243-
// so normally libunwind implementations on linux detect this function specially and hack around the invalid info:
244-
// https://github.com/llvm/llvm-project/commit/c82deed6764cbc63966374baf9721331901ca958
245-
" nop\n"
246-
);
247-
#else
248-
CFI_NORETURN
238+
__asm__(
239+
" .cfi_signal_frame\n"
240+
" .cfi_def_cfa sp, 0\n" // use sp as fp here
241+
// This is not quite valid, since the AArch64 DWARF spec lacks the ability to define how to restore the LR register correctly,
242+
// so normally libunwind implementations on linux detect this function specially and hack around the invalid info:
243+
// https://github.com/llvm/llvm-project/commit/c82deed6764cbc63966374baf9721331901ca958
244+
" .cfi_offset lr, 0\n"
245+
" .cfi_offset sp, 8\n"
246+
" brk #1\n"
247+
" brk #1\n"
248+
);
249249
#endif
250250
}
251251

252-
static void jl_call_in_state(host_thread_state_t *state, void (*fptr)(void))
252+
static void jl_call_in_state1(host_thread_state_t *state, void (*fptr)(void), uintptr_t arg0)
253253
{
254254
#ifdef _CPU_X86_64_
255255
uintptr_t sp = state->__rsp;
@@ -258,51 +258,59 @@ static void jl_call_in_state(host_thread_state_t *state, void (*fptr)(void))
258258
#endif
259259
sp = (sp - 256) & ~(uintptr_t)15; // redzone and re-alignment
260260
assert(sp % 16 == 0);
261-
sp -= 16;
262261
#ifdef _CPU_X86_64_
263-
// set return address to NULL
264-
*(uintptr_t*)sp = 0;
265-
// pushq %sp
262+
// push {%rsp, %rip}
266263
sp -= sizeof(void*);
267264
*(uintptr_t*)sp = state->__rsp;
268-
// pushq %rip
269265
sp -= sizeof(void*);
270266
*(uintptr_t*)sp = state->__rip;
271-
// pushq .fake_stack_pop + 1; aka call from fake_stack_pop
267+
// pushq .jl_fake_signal_return + 1; aka call from jl_fake_signal_return
272268
sp -= sizeof(void*);
273-
*(uintptr_t*)sp = (uintptr_t)&fake_stack_pop + 1;
269+
*(uintptr_t*)sp = (uintptr_t)&jl_fake_signal_return + 1;
274270
state->__rsp = sp; // set stack pointer
275271
state->__rip = (uint64_t)fptr; // "call" the function
272+
state->__rdi = arg0;
276273
#elif defined(_CPU_AARCH64_)
277-
// push {%sp, %pc + 4}
274+
// push {%sp, %pc}
278275
sp -= sizeof(void*);
279276
*(uintptr_t*)sp = state->__sp;
280277
sp -= sizeof(void*);
281278
*(uintptr_t*)sp = (uintptr_t)state->__pc;
282279
state->__sp = sp; // x31
283280
state->__pc = (uint64_t)fptr; // pc
284-
state->__lr = (uintptr_t)&fake_stack_pop + 4; // x30
281+
state->__lr = (uintptr_t)&jl_fake_signal_return + 4; // x30
282+
state->__x[0] = arg0;
285283
#else
286284
#error "julia: throw-in-context not supported on this platform"
287285
#endif
288286
}
289287

290288
static void jl_longjmp_in_state(host_thread_state_t *state, jl_jmp_buf jmpbuf)
291289
{
292-
293290
if (!jl_simulate_longjmp(jmpbuf, (bt_context_t*)state)) {
294291
// for sanitizer builds, fallback to calling longjmp on the original stack
295292
// (this will fail for stack overflow, but that is hardly sanitizer-legal anyways)
296293
#ifdef _CPU_X86_64_
297-
state->__rdi = (uintptr_t)jmpbuf;
298-
state->__rsi = 1;
294+
uintptr_t sp = state->__rsp;
295+
#elif defined(_CPU_AARCH64_)
296+
uintptr_t sp = state->__sp;
297+
#endif
298+
sp = (sp - 256) & ~(uintptr_t)15; // redzone and re-alignment
299+
assert(sp % 16 == 0);
300+
#ifdef _CPU_X86_64_
301+
state->__rdi = (uintptr_t)jmpbuf;
302+
state->__rsi = 1;
303+
state->__rsp = sp; // set stack pointer
304+
state->__rip = (uint64_t)longjmp; // "call" the function
299305
#elif defined(_CPU_AARCH64_)
300-
state->__x[0] = (uintptr_t)jmpbuf;
301-
state->__x[1] = 1;
306+
state->__x[0] = (uintptr_t)jmpbuf;
307+
state->__x[1] = 1;
308+
state->__sp = sp; // x31
309+
state->__pc = (uint64_t)longjmp; // pc
310+
state->__lr = (uintptr_t)0; // x30
302311
#else
303-
#error "julia: jl_longjmp_in_state not supported on this platform"
312+
#error "julia: throw-in-context not supported on this platform"
304313
#endif
305-
jl_call_in_state(state, (void (*)(void))longjmp);
306314
}
307315
}
308316

@@ -577,7 +585,7 @@ static void jl_try_deliver_sigint(void)
577585
HANDLE_MACH_ERROR("thread_resume", ret);
578586
}
579587

580-
static void JL_NORETURN jl_exit_thread0_cb(int signo)
588+
static void jl_exit_thread0_cb(int signo)
581589
{
582590
jl_fprint_critical_error(ios_safe_stderr, signo, 0, NULL, jl_current_task);
583591
jl_atexit_hook(128);
@@ -602,15 +610,7 @@ static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size)
602610
ptls2->bt_size = bt_size; // <= JL_MAX_BT_SIZE
603611
memcpy(ptls2->bt_data, bt_data, ptls2->bt_size * sizeof(bt_data[0]));
604612

605-
#ifdef _CPU_X86_64_
606-
// First integer argument. Not portable but good enough =)
607-
state.__rdi = signo;
608-
#elif defined(_CPU_AARCH64_)
609-
state.__x[0] = signo;
610-
#else
611-
#error Fill in first integer argument here
612-
#endif
613-
jl_call_in_state(&state, (void (*)(void))&jl_exit_thread0_cb);
613+
jl_call_in_state1(&state, (void (*)(void))&jl_exit_thread0_cb, signo);
614614
unsigned int count = MACH_THREAD_STATE_COUNT;
615615
ret = thread_set_state(thread, MACH_THREAD_STATE, (thread_state_t)&state, count);
616616
HANDLE_MACH_ERROR("thread_set_state", ret);

src/signals-unix.c

Lines changed: 149 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,95 @@ int jl_simulate_longjmp(jl_jmp_buf mctx, bt_context_t *c) JL_NOTSAFEPOINT;
7070
static void jl_longjmp_in_ctx(int sig, void *_ctx, jl_jmp_buf jmpbuf);
7171

7272
#if !defined(_OS_DARWIN_)
73+
extern void jl_fake_signal_return(void);
74+
// Create a trampoline function that does the stack manipulations for jl_call_in_ctx/jl_call_in_state
75+
// The callee-saved registers still may get smashed (by the cdecl fptr), since we didn't explicitly copy all of the
76+
// state to the stack (to build a real sigreturn frame).
77+
#if (defined(_OS_LINUX_) || defined(_OS_FREEBSD_) || defined(_OS_OPENBSD_)) && defined(_CPU_X86_64_)
78+
__asm__(
79+
" .type jl_fake_signal_return, @function\n"
80+
"jl_fake_signal_return:\n"
81+
" .cfi_startproc\n"
82+
" .cfi_signal_frame\n"
83+
// Mark as end of stack until frame is set up
84+
" .cfi_undefined %rip\n"
85+
" .cfi_undefined %rsp\n"
86+
// rdi points to signal_ctx_pc in ptls (followed by signal_ctx_sp, signal_ctx_fptr, signal_ctx_arg)
87+
" pushq (%rdi)\n" // push pc (signal_ctx_pc)
88+
" pushq 8(%rdi)\n" // push sp (signal_ctx_sp)
89+
// stack layout: [sp, pc] (pc at higher address, like return address after call)
90+
" .cfi_def_cfa %rsp, 8\n"
91+
" .cfi_offset %rip, 0\n" // previous %rip at CFA+0 (pc slot at rsp+8)
92+
" .cfi_offset %rsp, -8\n" // previous %rsp at CFA-8 (sp slot at rsp+0)
93+
" pushq 16(%rdi)\n" // push fptr (signal_ctx_fptr)
94+
" .cfi_def_cfa %rsp, 16\n"
95+
" movq 24(%rdi), %rdi\n" // restore original rdi from signal_ctx_arg
96+
" subq $8, %rsp\n" // align stack to 16 bytes
97+
" .cfi_def_cfa %rsp, 24\n"
98+
" callq *8(%rsp)\n" // call fptr
99+
" ud2\n" // unreachable
100+
" .cfi_endproc\n"
101+
" .size jl_fake_signal_return, .-jl_fake_signal_return\n"
102+
);
103+
104+
#elif (defined(_OS_LINUX_) || defined(_OS_FREEBSD_)) && defined(_CPU_X86_)
105+
__asm__(
106+
" .type jl_fake_signal_return, @function\n"
107+
"jl_fake_signal_return:\n"
108+
" .cfi_startproc\n"
109+
" .cfi_signal_frame\n"
110+
// Mark as end of stack until frame is set up
111+
" .cfi_undefined 1\n"
112+
// eax points to signal_ctx_pc in ptls (followed by signal_ctx_sp, signal_ctx_fptr, signal_ctx_arg)
113+
" pushl (%eax)\n" // push pc (signal_ctx_pc)
114+
" pushl 4(%eax)\n" // push sp (signal_ctx_sp)
115+
// stack layout: [sp, pc] (pc at higher address, like return address after call)
116+
" .cfi_def_cfa %esp, 4\n"
117+
" .cfi_offset %eip, 0\n" // previous %eip at CFA+0 (pc slot at esp+4)
118+
" .cfi_offset %esp, -4\n" // previous %esp at CFA-4 (sp slot at esp+0)
119+
" pushl 8(%eax)\n" // push fptr (signal_ctx_fptr)
120+
" .cfi_def_cfa %esp, 8\n"
121+
" movl 12(%eax), %eax\n" // restore original eax from signal_ctx_arg
122+
" subl $4, %esp\n" // align stack to 16 bytes
123+
" .cfi_def_cfa %esp, 12\n"
124+
" calll *4(%esp)\n" // call fptr
125+
" ud2\n" // unreachable
126+
" .cfi_endproc\n"
127+
" .size jl_fake_signal_return, .-jl_fake_signal_return\n"
128+
);
129+
#elif (defined(_OS_LINUX_) || defined(_OS_FREEBSD_)) && defined(_CPU_AARCH64_)
130+
__asm__(
131+
" .type jl_fake_signal_return, @function\n"
132+
"jl_fake_signal_return:\n"
133+
" .cfi_startproc\n"
134+
" .cfi_signal_frame\n"
135+
// Mark as end of stack until frame is set up
136+
" .cfi_undefined 1\n"
137+
// x0 points to signal_ctx_pc in ptls (followed by signal_ctx_sp, signal_ctx_fptr, signal_ctx_arg)
138+
" ldp x1, x2, [x0]\n" // load pc (x1) and sp (x2)
139+
" stp x2, x1, [sp, #-16]!\n" // push sp and pc (sp at lower addr, pc at higher addr)
140+
// stack layout: [sp, pc] (pc at higher address, like return address after call)
141+
" .cfi_def_cfa sp, 16\n"
142+
" .cfi_offset lr, -8\n" // previous lr (pc) at CFA-8 (pc slot at sp+8)
143+
" .cfi_offset sp, -16\n" // previous sp at CFA-16 (sp slot at sp+0)
144+
// This is not quite valid, since the AArch64 DWARF spec lacks the ability to define how to restore the LR register correctly,
145+
// so normally libunwind implementations on linux detect this function specially and hack around the invalid info:
146+
// https://github.com/llvm/llvm-project/commit/c82deed6764cbc63966374baf9721331901ca958
147+
" ldp x1, x2, [x0, #16]\n" // load fptr (x1) and saved x0 (x2)
148+
" mov x0, x2\n" // restore original x0
149+
" blr x1\n" // call fptr
150+
" brk #1\n" // unreachable
151+
" .cfi_endproc\n"
152+
" .size jl_fake_signal_return, .-jl_fake_signal_return\n"
153+
);
154+
#else
155+
extern void JL_NORETURN jl_fake_signal_return(void)
156+
{
157+
CFI_NORETURN
158+
abort();
159+
}
160+
#endif
161+
73162
static inline uintptr_t jl_get_rsp_from_ctx(const void *_ctx)
74163
{
75164
#if defined(_OS_LINUX_) && defined(_CPU_X86_64_)
@@ -123,46 +212,79 @@ JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int si
123212
// will not be part of the validation...
124213
uintptr_t rsp = jl_get_rsp_from_ctx(_ctx);
125214
rsp = (rsp - 256) & ~(uintptr_t)15; // redzone and re-alignment
215+
assert(rsp % 16 == 0);
126216
#if defined(_OS_LINUX_) && defined(_CPU_X86_64_)
127217
ucontext_t *ctx = (ucontext_t*)_ctx;
128-
rsp -= sizeof(void*);
129-
*(uintptr_t*)rsp = 0;
130-
ctx->uc_mcontext.gregs[REG_RSP] = rsp;
131-
ctx->uc_mcontext.gregs[REG_RIP] = (uintptr_t)fptr;
218+
// Save context in ptls for stack unwinding
219+
ptls->signal_ctx_pc = ctx->uc_mcontext.gregs[REG_RIP];
220+
ptls->signal_ctx_sp = ctx->uc_mcontext.gregs[REG_RSP];
221+
ptls->signal_ctx_fptr = fptr;
222+
ptls->signal_ctx_arg = ctx->uc_mcontext.gregs[REG_RDI];
223+
ctx->uc_mcontext.gregs[REG_RSP] = rsp; // set stack pointer
224+
ctx->uc_mcontext.gregs[REG_RDI] = (uintptr_t)&ptls->signal_ctx_pc; // first arg points to signal_ctx
225+
ctx->uc_mcontext.gregs[REG_RIP] = (uintptr_t)&jl_fake_signal_return; // "call" jl_fake_signal_return
132226
#elif defined(_OS_FREEBSD_) && defined(_CPU_X86_64_)
133227
ucontext_t *ctx = (ucontext_t*)_ctx;
134-
rsp -= sizeof(void*);
135-
*(uintptr_t*)rsp = 0;
136-
ctx->uc_mcontext.mc_rsp = rsp;
137-
ctx->uc_mcontext.mc_rip = (uintptr_t)fptr;
228+
// Save context in ptls for stack unwinding
229+
ptls->signal_ctx_pc = ctx->uc_mcontext.mc_rip;
230+
ptls->signal_ctx_sp = ctx->uc_mcontext.mc_rsp;
231+
ptls->signal_ctx_fptr = fptr;
232+
ptls->signal_ctx_arg = ctx->uc_mcontext.mc_rdi;
233+
ctx->uc_mcontext.mc_rsp = rsp; // set stack pointer
234+
ctx->uc_mcontext.mc_rdi = (uintptr_t)&ptls->signal_ctx_pc; // first arg points to signal_ctx
235+
ctx->uc_mcontext.mc_rip = (uintptr_t)&jl_fake_signal_return; // "call" jl_fake_signal_return
138236
#elif defined(_OS_LINUX_) && defined(_CPU_X86_)
139237
ucontext_t *ctx = (ucontext_t*)_ctx;
140-
rsp -= sizeof(void*);
141-
*(uintptr_t*)rsp = 0;
142-
ctx->uc_mcontext.gregs[REG_ESP] = rsp;
143-
ctx->uc_mcontext.gregs[REG_EIP] = (uintptr_t)fptr;
238+
// Save context in ptls for stack unwinding
239+
ptls->signal_ctx_pc = ctx->uc_mcontext.gregs[REG_EIP];
240+
ptls->signal_ctx_sp = ctx->uc_mcontext.gregs[REG_ESP];
241+
ptls->signal_ctx_fptr = fptr;
242+
ptls->signal_ctx_arg = ctx->uc_mcontext.gregs[REG_EAX];
243+
ctx->uc_mcontext.gregs[REG_ESP] = rsp; // set stack pointer
244+
ctx->uc_mcontext.gregs[REG_EAX] = (uintptr_t)&ptls->signal_ctx_pc; // set eax to point to signal_ctx
245+
ctx->uc_mcontext.gregs[REG_EIP] = (uintptr_t)&jl_fake_signal_return; // "call" jl_fake_signal_return
144246
#elif defined(_OS_FREEBSD_) && defined(_CPU_X86_)
145247
ucontext_t *ctx = (ucontext_t*)_ctx;
146-
rsp -= sizeof(void*);
147-
*(uintptr_t*)rsp = 0;
148-
ctx->uc_mcontext.mc_esp = rsp;
149-
ctx->uc_mcontext.mc_eip = (uintptr_t)fptr;
248+
// Save context in ptls for stack unwinding
249+
ptls->signal_ctx_pc = ctx->uc_mcontext.mc_eip;
250+
ptls->signal_ctx_sp = ctx->uc_mcontext.mc_esp;
251+
ptls->signal_ctx_fptr = fptr;
252+
ptls->signal_ctx_arg = ctx->uc_mcontext.mc_eax;
253+
ctx->uc_mcontext.mc_esp = rsp; // set stack pointer
254+
ctx->uc_mcontext.mc_eax = (uintptr_t)&ptls->signal_ctx_pc; // set eax to point to signal_ctx
255+
ctx->uc_mcontext.mc_eip = (uintptr_t)&jl_fake_signal_return; // "call" jl_fake_signal_return
150256
#elif defined(_OS_OPENBSD_) && defined(_CPU_X86_64_)
151257
struct sigcontext *ctx = (struct sigcontext *)_ctx;
152-
rsp -= sizeof(void*);
153-
*(uintptr_t*)rsp = 0;
154-
ctx->sc_rsp = rsp;
155-
ctx->sc_rip = fptr;
258+
// Save context in ptls for stack unwinding
259+
ptls->signal_ctx_pc = ctx->sc_rip;
260+
ptls->signal_ctx_sp = ctx->sc_rsp;
261+
ptls->signal_ctx_fptr = fptr;
262+
ptls->signal_ctx_arg = ctx->sc_rdi;
263+
ctx->sc_rsp = rsp; // set stack pointer
264+
ctx->sc_rdi = (uintptr_t)&ptls->signal_ctx_pc; // first arg points to signal_ctx
265+
ctx->sc_rip = (uintptr_t)&jl_fake_signal_return; // "call" jl_fake_signal_return
156266
#elif defined(_OS_LINUX_) && defined(_CPU_AARCH64_)
157267
ucontext_t *ctx = (ucontext_t*)_ctx;
158-
ctx->uc_mcontext.sp = rsp;
159-
ctx->uc_mcontext.regs[29] = 0; // Clear link register (x29)
160-
ctx->uc_mcontext.pc = (uintptr_t)fptr;
268+
// Save context in ptls for stack unwinding
269+
ptls->signal_ctx_pc = (uintptr_t)ctx->uc_mcontext.pc;
270+
ptls->signal_ctx_sp = ctx->uc_mcontext.sp;
271+
ptls->signal_ctx_fptr = fptr;
272+
ptls->signal_ctx_arg = ctx->uc_mcontext.regs[0];
273+
ctx->uc_mcontext.sp = rsp; // sp
274+
ctx->uc_mcontext.regs[0] = (uintptr_t)&ptls->signal_ctx_pc; // first arg points to signal_ctx
275+
ctx->uc_mcontext.pc = (uint64_t)&jl_fake_signal_return; // pc
276+
ctx->uc_mcontext.regs[30] = 0; // clear lr (x30)
161277
#elif defined(_OS_FREEBSD_) && defined(_CPU_AARCH64_)
162278
ucontext_t *ctx = (ucontext_t*)_ctx;
163-
ctx->uc_mcontext.mc_gpregs.gp_sp = rsp;
164-
ctx->uc_mcontext.mc_gpregs.gp_x[29] = 0; // Clear link register (x29)
165-
ctx->uc_mcontext.mc_gpregs.gp_elr = (uintptr_t)fptr;
279+
// Save context in ptls for stack unwinding
280+
ptls->signal_ctx_pc = ctx->uc_mcontext.mc_gpregs.gp_elr;
281+
ptls->signal_ctx_sp = ctx->uc_mcontext.mc_gpregs.gp_sp;
282+
ptls->signal_ctx_fptr = fptr;
283+
ptls->signal_ctx_arg = ctx->uc_mcontext.mc_gpregs.gp_x[0];
284+
ctx->uc_mcontext.mc_gpregs.gp_sp = rsp; // set stack pointer
285+
ctx->uc_mcontext.mc_gpregs.gp_x[0] = (uintptr_t)&ptls->signal_ctx_pc; // first arg points to signal_ctx
286+
ctx->uc_mcontext.mc_gpregs.gp_elr = (uintptr_t)&jl_fake_signal_return; // pc
287+
ctx->uc_mcontext.mc_gpregs.gp_lr = 0; // clear lr (x30)
166288
#elif defined(_OS_LINUX_) && defined(_CPU_ARM_)
167289
ucontext_t *ctx = (ucontext_t*)_ctx;
168290
uintptr_t target = (uintptr_t)fptr;
@@ -549,9 +671,8 @@ static void jl_try_deliver_sigint(void)
549671
// Write only by signal handling thread, read only by main thread
550672
// no sync necessary.
551673
static int thread0_exit_signo = 0;
552-
static void JL_NORETURN jl_exit_thread0_cb(void)
674+
static void jl_exit_thread0_cb(void)
553675
{
554-
CFI_NORETURN
555676
jl_atomic_fetch_add(&jl_gc_disable_counter, -1);
556677
jl_fprint_critical_error(ios_safe_stderr, thread0_exit_signo, 0, NULL, jl_current_task);
557678
jl_atexit_hook(128);

0 commit comments

Comments
 (0)