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*)&regs, &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(&regs);
          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(&regs, sizeof *stk_siginfo);
          183                 stk_mcontext = alloc(&regs, 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(&regs, 3*4);
          200                 sp[0] = (uint)stk_siginfo;
          201                 sp[1] = (uint)stk_mcontext;
          202                 sp[2] = (uint)handler;
          203                 
          204                 push(&regs, 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*)&regs, 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(&regs);
          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