signal.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
signal.c (8116B)
---
1 /*
2 * Truly awful code to simulate Unix signal handler dispatch
3 * using Mach signal handler dispatch. The BSD support routines
4 * can't deal with our SIGSEGVs properly. Among other things,
5 * they keep waking up other threads and they cause a popup
6 * about the application quitting when it hasn't.
7 *
8 * This code is inspired by similar code in SBCL.
9 * See also http://paste.lisp.org/display/19593.
10 * See also http://lists.apple.com/archives/darwin-dev/2006/Oct/msg00122.html
11 */
12
13 #define __DARWIN_UNIX03 0
14
15 #include <mach/mach.h>
16 #include <sys/ucontext.h>
17 #include <pthread.h>
18 #include <signal.h>
19 #include <sys/signal.h>
20
21 #include "u.h"
22 #include "lib.h"
23 #include "mem.h"
24 #include "dat.h"
25 #include "fns.h"
26 #include "error.h"
27
28 #define EFLAGS_TF 0x100
29
30 extern int invx32;
31
32 static x86_thread_state32_t normal; /* normal segment registers */
33 static void *altstack;
34
35 static void (*sigbus)(int, siginfo_t*, void*);
36 static void (*sigtrap)(int, siginfo_t*, void*);
37 static void (*sigfpe)(int, siginfo_t*, void*);
38
39 /*
40 * Manipulate stack in regs.
41 */
42 static void
43 push(x86_thread_state32_t *regs, ulong data)
44 {
45 uint *sp;
46
47 sp = (uint*)regs->esp;
48 *--sp = data;
49 regs->esp = (uint)sp;
50 }
51
52 static void
53 align(x86_thread_state32_t *regs)
54 {
55 uint *sp;
56
57 sp = (uint*)regs->esp;
58 while((ulong)sp & 15)
59 sp--;
60 regs->esp = (uint)sp;
61 }
62
63 static void*
64 alloc(x86_thread_state32_t *regs, int n)
65 {
66 n = (n+15) & ~15;
67 regs->esp -= n;
68 return (void*)regs->esp;
69 }
70
71 /*
72 * Signal handler wrapper. Calls handler and then
73 * causes an illegal instruction exception to jump
74 * back to us.
75 */
76 static void
77 wrapper(siginfo_t *siginfo,
78 mcontext_t mcontext,
79 void (*handler)(int, siginfo_t*, void*))
80 {
81 ucontext_t ucontext;
82
83 memset(&ucontext, 0, sizeof ucontext);
84 ucontext.uc_mcontext = mcontext;
85 handler(siginfo->si_signo, siginfo, &ucontext);
86
87 /* Cause EXC_BAD_INSTRUCTION to "exit" signal handler */
88 asm volatile(
89 "movl %0, %%eax\n"
90 "movl %1, %%ebx\n"
91 "movl $0xdeadbeef, %%esp\n"
92 ".globl _wrapper_bad\n"
93 "_wrapper_bad:\n"
94 ".long 0xffff0b0f\n"
95 : : "r" (mcontext), "r" (siginfo));
96 }
97
98 void
99 dumpmcontext(mcontext_t m)
100 {
101 x86_thread_state32_t *ureg;
102
103 ureg = &m->ss;
104 iprint("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX CR2=%luX\n",
105 ureg->eflags, m->es.trapno, m->es.err, ureg->eip, m->es.faultvaddr);
106 iprint(" AX %8.8luX BX %8.8luX CX %8.8luX DX %8.8luX\n",
107 ureg->eax, ureg->ebx, ureg->ecx, ureg->edx);
108 iprint(" SI %8.8luX DI %8.8luX BP %8.8luX SP %8.8luX\n",
109 ureg->esi, ureg->edi, ureg->ebp, ureg->esp);
110 }
111
112 void
113 dumpregs1(x86_thread_state32_t *ureg)
114 {
115 iprint("FLAGS=%luX PC=%luX\n",
116 ureg->eflags, ureg->eip);
117 iprint(" AX %8.8luX BX %8.8luX CX %8.8luX DX %8.8luX\n",
118 ureg->eax, ureg->ebx, ureg->ecx, ureg->edx);
119 iprint(" SI %8.8luX DI %8.8luX BP %8.8luX SP %8.8luX\n",
120 ureg->esi, ureg->edi, ureg->ebp, ureg->esp);
121 }
122
123
124 /*
125 * Called by mach loop in exception handling thread.
126 */
127 kern_return_t
128 catch_exception_raise(mach_port_t exception_port,
129 mach_port_t thread,
130 mach_port_t task,
131 exception_type_t exception,
132 exception_data_t code_vector,
133 mach_msg_type_number_t code_count)
134 {
135 mach_msg_type_number_t n;
136 x86_thread_state32_t regs, save_regs;
137 siginfo_t *stk_siginfo;
138 kern_return_t ret;
139 uint *sp;
140 mcontext_t stk_mcontext;
141 void (*handler)(int, siginfo_t*, void*);
142 ulong addr;
143 int signo;
144
145 n = x86_THREAD_STATE32_COUNT;
146 ret = thread_get_state(thread, x86_THREAD_STATE32, (void*)®s, &n);
147 if(ret != KERN_SUCCESS)
148 panic("mach get regs failed: %d", ret);
149
150 addr = 0;
151 save_regs = regs;
152
153 switch(exception){
154 case EXC_BAD_ACCESS:
155 signo = SIGBUS;
156 addr = code_vector[1];
157 handler = sigbus;
158 goto Trigger;
159
160 case EXC_BREAKPOINT:
161 signo = SIGTRAP;
162 handler = sigtrap;
163 regs.eflags &= ~EFLAGS_TF;
164 goto Trigger;
165
166 case EXC_ARITHMETIC:
167 signo = SIGFPE;
168 handler = sigfpe;
169 goto Trigger;
170
171 Trigger:
172 if(invx32)
173 regs.esp = (uint)altstack;
174 else if(regs.ss != normal.ss)
175 panic("not in vx32 but bogus ss");
176 align(®s);
177 regs.cs = normal.cs;
178 regs.ds = normal.ds;
179 regs.es = normal.es;
180 regs.ss = normal.ss;
181
182 stk_siginfo = alloc(®s, sizeof *stk_siginfo);
183 stk_mcontext = alloc(®s, sizeof *stk_mcontext);
184
185 memset(stk_siginfo, 0, sizeof *stk_siginfo);
186 stk_siginfo->si_signo = signo;
187 stk_siginfo->si_addr = (void*)addr;
188
189 stk_mcontext->ss = save_regs;
190 n = x86_FLOAT_STATE32_COUNT;
191 ret = thread_get_state(thread, x86_FLOAT_STATE32, (void*)&stk_mcontext->fs, &n);
192 if(ret != KERN_SUCCESS)
193 panic("mach get fpregs failed: %d", ret);
194 n = x86_EXCEPTION_STATE32_COUNT;
195 ret = thread_get_state(thread, x86_EXCEPTION_STATE32, (void*)&stk_mcontext->es, &n);
196 if(ret != KERN_SUCCESS)
197 panic("mach get eregs: %d", ret);
198
199 sp = alloc(®s, 3*4);
200 sp[0] = (uint)stk_siginfo;
201 sp[1] = (uint)stk_mcontext;
202 sp[2] = (uint)handler;
203
204 push(®s, regs.eip); /* for debugger; wrapper won't return */
205 regs.eip = (uint)wrapper;
206
207 ret = thread_set_state(thread, x86_THREAD_STATE32,
208 (void*)®s, x86_THREAD_STATE32_COUNT);
209 if(ret != KERN_SUCCESS)
210 panic("mach set regs failed: %d", ret);
211 if(0 && stk_siginfo->si_signo != SIGBUS){
212 iprint("call sig %d\n", stk_siginfo->si_signo);
213 dumpmcontext(stk_mcontext);
214 }
215 return KERN_SUCCESS;
216
217 case EXC_BAD_INSTRUCTION:;
218 /*
219 * We use an invalid instruction in wrapper to note
220 * that we're done with the signal handler, but
221 * Mach sends the same exception (different code_vector[0])
222 * when it gets the GP fault triggered by an address
223 * greater than the segment limit. Catch both.
224 */
225 extern char wrapper_bad[];
226 if(regs.eip == (ulong)wrapper_bad && regs.esp == 0xdeadbeef){
227 stk_mcontext = (mcontext_t)regs.eax;
228 stk_siginfo = (siginfo_t*)regs.ebx;
229 if(0 && stk_siginfo->si_signo != SIGBUS){
230 iprint("return sig %d\n", stk_siginfo->si_signo);
231 dumpmcontext(stk_mcontext);
232 }
233 ret = thread_set_state(thread, x86_THREAD_STATE32,
234 (void*)&stk_mcontext->ss, x86_THREAD_STATE32_COUNT);
235 if(ret != KERN_SUCCESS)
236 panic("mach set regs1 failed: %d", ret);
237 ret = thread_set_state(thread, x86_FLOAT_STATE32,
238 (void*)&stk_mcontext->fs, x86_FLOAT_STATE32_COUNT);
239 if(ret != KERN_SUCCESS)
240 panic("mach set fpregs failed: %d", ret);
241 return KERN_SUCCESS;
242 }
243
244 /*
245 * Other things can cause GP faults too, but let's assume it was this.
246 * Linux sends si_addr == 0; so will we. The app isn't going to try
247 * to recover anyway, so it's not a big deal if we send other GP
248 * faults that way too.
249 */
250 if(code_vector[0] == EXC_I386_GPFLT){
251 signo = SIGBUS;
252 handler = sigbus;
253 addr = 0;
254 goto Trigger;
255 }
256
257 iprint("Unexpected bad instruction at eip=%p: code %p %p\n",
258 regs.eip, code_vector[0], code_vector[1]);
259 dumpregs1(®s);
260 return KERN_INVALID_RIGHT;
261 }
262 return KERN_INVALID_RIGHT;
263 }
264
265 static void*
266 handler(void *v)
267 {
268 extern boolean_t exc_server();
269 mach_port_t port;
270
271 setmach(machp[0]);
272 port = (mach_port_t)v;
273 mach_msg_server(exc_server, 2048, port, 0);
274 return 0; /* not reached */
275 }
276
277 void
278 machsiginit(void)
279 {
280 mach_port_t port;
281 pthread_t pid;
282 stack_t ss;
283 struct sigaction act;
284 int ret;
285
286 extern int vx32_getcontext(x86_thread_state32_t*);
287 vx32_getcontext(&normal);
288
289 if(sigaltstack(nil, &ss) < 0 || (ss.ss_flags & SS_DISABLE))
290 panic("machsiginit: no alt stack");
291 altstack = ss.ss_sp + ss.ss_size;
292
293 if(sigaction(SIGBUS, nil, &act) < 0)
294 panic("sigaction bus");
295 sigbus = (void*)act.sa_handler;
296
297 if(sigaction(SIGTRAP, nil, &act) < 0)
298 panic("sigaction trap");
299 sigtrap = (void*)act.sa_handler;
300
301 if(sigaction(SIGFPE, nil, &act) < 0)
302 panic("sigaction fpe");
303 sigfpe = (void*)act.sa_handler;
304
305 mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
306 mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
307 ret = thread_set_exception_ports(mach_thread_self(),
308 EXC_MASK_BAD_ACCESS|EXC_MASK_BAD_INSTRUCTION|
309 EXC_MASK_BREAKPOINT|EXC_MASK_ARITHMETIC,
310 port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
311 pthread_create(&pid, nil, handler, (void*)port);
312 }
313