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