vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | 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