@@ -69,6 +69,43 @@ static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size)
6969int jl_simulate_longjmp (jl_jmp_buf mctx , bt_context_t * c ) JL_NOTSAFEPOINT ;
7070static void jl_longjmp_in_ctx (int sig , void * _ctx , jl_jmp_buf jmpbuf );
7171
72+ // create a fake function that describes the variable manipulations in jl_call_in_ctx/jl_call_in_state
73+ __attribute__((naked )) static void fake_stack_pop (void )
74+ {
75+ #if (defined(_OS_LINUX_ ) || defined(_OS_DARWIN_ )) && defined(_CPU_X86_64_ )
76+ __asm__ volatile (
77+ " .cfi_signal_frame\n"
78+ " .cfi_def_cfa %rsp, 0\n" // CFA here uses %rsp directly
79+ " .cfi_offset %rip, 0\n" // previous value of %rip at CFA
80+ " .cfi_offset %rsp, 8\n" // previous value of %rsp at CFA
81+ " nop\n"
82+ );
83+ #elif defined(_OS_LINUX_ ) && defined(_CPU_X86_ )
84+ __asm__ volatile (
85+ " .cfi_signal_frame\n"
86+ " .cfi_def_cfa %esp, 0\n" // CFA here uses %esp directly
87+ " .cfi_offset %eip, 0\n" // previous value of %eip at CFA
88+ " .cfi_offset %esp, 4\n" // previous value of %esp at CFA
89+ " nop\n"
90+ );
91+ #elif (defined(_OS_LINUX_ ) || defined(_OS_DARWIN_ )) && defined(_CPU_AARCH64_ )
92+ __asm__ volatile (
93+ " .cfi_signal_frame\n"
94+ " .cfi_def_cfa sp, 0\n" // use sp as fp here
95+ " .cfi_offset lr, 0\n"
96+ " .cfi_offset sp, 8\n"
97+ // Anything else got smashed, since we didn't explicitly copy all of the
98+ // state object to the stack (to build a real sigreturn frame).
99+ // This is also not quite valid, since the AArch64 DWARF spec lacks the ability to define how to restore the LR register correctly,
100+ // so normally libunwind implementations on linux detect this function specially and hack around the invalid info:
101+ // https://github.com/llvm/llvm-project/commit/c82deed6764cbc63966374baf9721331901ca958
102+ " nop\n"
103+ );
104+ #else
105+ CFI_NORETURN
106+ #endif
107+ }
108+
72109#if !defined(_OS_DARWIN_ )
73110static inline uintptr_t jl_get_rsp_from_ctx (const void * _ctx )
74111{
@@ -123,12 +160,25 @@ JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int si
123160 // will not be part of the validation...
124161 uintptr_t rsp = jl_get_rsp_from_ctx (_ctx );
125162 rsp = (rsp - 256 ) & ~(uintptr_t )15 ; // redzone and re-alignment
163+ assert (rsp % 16 == 0 );
126164#if defined(_OS_LINUX_ ) && defined(_CPU_X86_64_ )
127165 ucontext_t * ctx = (ucontext_t * )_ctx ;
166+ // set return address to NULL
128167 rsp -= sizeof (void * );
129168 * (uintptr_t * )rsp = 0 ;
130- ctx -> uc_mcontext .gregs [REG_RSP ] = rsp ;
131- ctx -> uc_mcontext .gregs [REG_RIP ] = (uintptr_t )fptr ;
169+ rsp -= sizeof (void * );
170+ * (uintptr_t * )rsp = 0 ;
171+ // pushq %rsp
172+ rsp -= sizeof (void * );
173+ * (uintptr_t * )rsp = ctx -> uc_mcontext .gregs [REG_RSP ];
174+ // pushq %rip
175+ rsp -= sizeof (void * );
176+ * (uintptr_t * )rsp = ctx -> uc_mcontext .gregs [REG_RIP ];
177+ // pushq .fake_stack_pop + 1; aka call from fake_stack_pop
178+ rsp -= sizeof (void * );
179+ * (uintptr_t * )rsp = (uintptr_t )& fake_stack_pop + 1 ;
180+ ctx -> uc_mcontext .gregs [REG_RSP ] = rsp ; // set stack pointer
181+ ctx -> uc_mcontext .gregs [REG_RIP ] = (uintptr_t )fptr ; // "call" the function
132182#elif defined(_OS_FREEBSD_ ) && defined(_CPU_X86_64_ )
133183 ucontext_t * ctx = (ucontext_t * )_ctx ;
134184 rsp -= sizeof (void * );
@@ -137,10 +187,22 @@ JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int si
137187 ctx -> uc_mcontext .mc_rip = (uintptr_t )fptr ;
138188#elif defined(_OS_LINUX_ ) && defined(_CPU_X86_ )
139189 ucontext_t * ctx = (ucontext_t * )_ctx ;
190+ // set return address to NULL
140191 rsp -= sizeof (void * );
141192 * (uintptr_t * )rsp = 0 ;
142- ctx -> uc_mcontext .gregs [REG_ESP ] = rsp ;
143- ctx -> uc_mcontext .gregs [REG_EIP ] = (uintptr_t )fptr ;
193+ rsp -= sizeof (void * );
194+ * (uintptr_t * )rsp = 0 ;
195+ // pushl %esp
196+ rsp -= sizeof (void * );
197+ * (uintptr_t * )rsp = ctx -> uc_mcontext .gregs [REG_ESP ];
198+ // pushl %eip
199+ rsp -= sizeof (void * );
200+ * (uintptr_t * )rsp = ctx -> uc_mcontext .gregs [REG_EIP ];
201+ // pushl .fake_stack_pop + 1; aka call from fake_stack_pop
202+ rsp -= sizeof (void * );
203+ * (uintptr_t * )rsp = (uintptr_t )& fake_stack_pop + 1 ;
204+ ctx -> uc_mcontext .gregs [REG_ESP ] = rsp ; // set stack pointer
205+ ctx -> uc_mcontext .gregs [REG_EIP ] = (uintptr_t )fptr ; // "call" the function
144206#elif defined(_OS_FREEBSD_ ) && defined(_CPU_X86_ )
145207 ucontext_t * ctx = (ucontext_t * )_ctx ;
146208 rsp -= sizeof (void * );
@@ -155,13 +217,19 @@ JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int si
155217 ctx -> sc_rip = fptr ;
156218#elif defined(_OS_LINUX_ ) && defined(_CPU_AARCH64_ )
157219 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 ;
220+ // push {%sp, %pc}
221+ rsp -= sizeof (void * );
222+ * (uintptr_t * )rsp = ctx -> uc_mcontext .sp ;
223+ rsp -= sizeof (void * );
224+ * (uintptr_t * )rsp = (uintptr_t )ctx -> uc_mcontext .pc ;
225+ ctx -> uc_mcontext .sp = rsp ; // sp
226+ ctx -> uc_mcontext .pc = (uint64_t )fptr ; // pc
227+ ctx -> uc_mcontext .regs [30 ] = (uintptr_t )& fake_stack_pop + 4 ; // lr (x30)
161228#elif defined(_OS_FREEBSD_ ) && defined(_CPU_AARCH64_ )
162229 ucontext_t * ctx = (ucontext_t * )_ctx ;
163230 ctx -> uc_mcontext .mc_gpregs .gp_sp = rsp ;
164- ctx -> uc_mcontext .mc_gpregs .gp_x [29 ] = 0 ; // Clear link register (x29)
231+ ctx -> uc_mcontext .mc_gpregs .gp_x [29 ] = 0 ; // Clear frame pointer (x29)
232+ ctx -> uc_mcontext .mc_gpregs .gp_lr = 0 ; // Clear link register (x30)
165233 ctx -> uc_mcontext .mc_gpregs .gp_elr = (uintptr_t )fptr ;
166234#elif defined(_OS_LINUX_ ) && defined(_CPU_ARM_ )
167235 ucontext_t * ctx = (ucontext_t * )_ctx ;
0 commit comments