vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

sig.c (10170B)


      1 #include <sys/signal.h>
      2 #include <ucontext.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <assert.h>
      6 #include <string.h>
      7 #include <signal.h>
      8 
      9 #include "vx32.h"
     10 #include "vx32impl.h"
     11 
     12 static void sighandler(int signo, siginfo_t *si, void *ucontext)
     13 {
     14 	if (vx32_sighandler(signo, si, ucontext)){
     15 		// Back into the fire.
     16 		return;
     17 	}
     18 	
     19 	// vx32_sighandler thought that vx32 wasn't
     20 	// running or otherwise didn't know what to do.
     21 	// TODO: If there was a handler registered before us, call it?
     22 	// If the signal is not important, ignore it.
     23 	if (signo == SIGVTALRM || signo == SIGALRM)
     24 		return;
     25 	
     26 	// Otherwise, deregister the handler and let the signal
     27 	// happen again -- this time we'll crash and get a core dump.
     28 	vxprint("Unregistering signal handler %d\n", signo);
     29 	signal(signo, SIG_DFL);
     30 }
     31 
     32 // vx32_sighandler saves execution context, finds emu,
     33 // and calls vxemu_sighandler.  If we're here, the fs segment
     34 // register pointed at a valid emu, but we still might not have
     35 // been executing actual vx32 translations.
     36 // Trapeip is 0xffffffff if the other segment registers 
     37 // suggest we weren't inside vxrun_setup ... vxrun_cleanup.
     38 int vxemu_sighandler(vxemu *emu, uint32_t trapeip)
     39 {
     40 	if (vx32_debugxlate)
     41 		vxprint("vxemu_sighandler %p %#x\n", emu, trapeip);
     42 
     43 	if (emu->cpu_trap == 0)
     44 		return VXSIG_ERROR;
     45 	
     46 	// Not in vx32 code: assume registers saved already and trap.
     47 	if (trapeip == 0xffffffff)
     48 		return VXSIG_TRAP;
     49 	
     50 	if (emu->ininst) {
     51 		// In the middle of translating an instruction,
     52 		// so the registers are already saved.
     53 		// The signal hander knows the exact fault address,
     54 		// which may be a bit after emu->ininst.
     55 		// In fact, the fault address may be one or two
     56 		// instructions past emu->cpu.eip, and a real 
     57 		// processor would not throw the trap until it
     58 		// actually got to the unreadable instruction,
     59 		// but there's not really any harm in trapping now instead.
     60 		return VXSIG_TRAP;
     61 	}
     62 
     63 	// Single-stepping?  Use the original trap code.
     64 	if (emu->cpu_trap == VXTRAP_SINGLESTEP && emu->saved_trap){
     65 		emu->cpu_trap = emu->saved_trap;
     66 		emu->saved_trap = 0;
     67 	}
     68 
     69 	// In vx32 runtime code (rts.S, run32/64.S), the registers are in flux.
     70 	// Single-step until we can get out; then they'll be safe to look at.
     71 	// This only makes sense if the trap is an external trap like a timer.
     72 	char *eip = (char*)(uintptr_t)trapeip;
     73 	if ((emu->cpu_trap&VXTRAP_CATEGORY) != VXTRAP_CPU){
     74 		// The check for run32/64.S doesn't look for the entire file.
     75 		// Instead it looks for anywhere in the file except
     76 		// vxrun_cleanup.  If we single-step out of vxrun_cleanup,
     77 		// then vx32_sighandler will get a single-step trap when
     78 		// the vx32 segment register doesn't point at emu and 
     79 		// won't know what to do.  If we're in vxrun_cleanup, then
     80 		// all the cpu registers are known to be saved.
     81 		extern char vx_rts_S_start[], vx_rts_S_end[];
     82 		extern char vx_run_S_start[];
     83 		if ((vx_rts_S_start <= eip && eip < vx_rts_S_end)
     84 		||  (vx_run_S_start <= eip && eip < (char*)vxrun_cleanup)){
     85 		SingleStep:
     86 			if(++emu->nsinglestep > 500){
     87 				// Give up: something is wrong.
     88 				vxprint("vx32: single-stepping but stuck\n");
     89 				return VXSIG_ERROR;
     90 			}
     91 			emu->saved_trap = emu->cpu_trap;
     92 			emu->cpu_trap = 0;
     93 			return VXSIG_SINGLESTEP;
     94 		}
     95 	}
     96 	emu->nsinglestep = 0;
     97 
     98 	// In translated code buffer?  Find original eip.
     99 	if ((char*)emu->codebuf <= eip && eip < (char*)emu->codefree){
    100 		// Binary search for the translated chunk in which the trap occurred.
    101 		// NB: frags appear in the opposite order as the code itself.
    102 		uint32_t *codetab = emu->codetab;
    103 		unsigned lofrag = 0;
    104 		unsigned hifrag = (uint32_t*)emu->codetop - codetab - 1;
    105 		while (hifrag > lofrag) {
    106 			unsigned midfrag = (lofrag + hifrag) / 2;
    107 			uint32_t mideip = codetab[midfrag];
    108 			if (trapeip >= mideip)
    109 				hifrag = midfrag;
    110 			else
    111 				lofrag = midfrag + 1;
    112 		}
    113 		struct vxfrag *frag = (vxfrag*)(intptr_t)codetab[lofrag];
    114 
    115 		// The eip is known to be in the translation buffer,
    116 		// but the buffer contains both fragment headers and
    117 		// fragment code.  Make sure it's not in a fragment header.
    118 		if (trapeip < (uint32_t)(intptr_t)FRAGCODE(frag))
    119 			return VXSIG_ERROR;
    120 		unsigned ofs = trapeip - (uint32_t)(intptr_t)FRAGCODE(frag);
    121 
    122 		// The very first instruction in each fragment is the one that
    123 		// restores ebx.  It is not included in the table, because it 
    124 		// doesn't correspond to any original source instruction.
    125 		// If we're there, pretend we're at the first instruction in the
    126 		// fragment but don't save ebx -- it's already saved.
    127 		if (ofs < frag->insn[0].dstofs) {
    128 			emu->cpu.eip = frag->eip + frag->insn[0].srcofs;
    129 			return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
    130 		}
    131 
    132 		// Binary search through this fragment's instruction table
    133 		// for the actual faulting translated instruction,
    134 		// and compute the corresponding EIP in the original vx32 code.
    135 		unsigned ninsn = frag->ninsn;
    136 		unsigned loinsn = 0;
    137 		unsigned hiinsn = ninsn - 1;
    138 		while (hiinsn > loinsn) {
    139 			unsigned midinsn = (loinsn + hiinsn + 1) / 2;
    140 			unsigned midofs = frag->insn[midinsn].dstofs;
    141 			if (ofs >= midofs)
    142 				loinsn = midinsn;
    143 			else
    144 				hiinsn = midinsn - 1;
    145 		}
    146 		struct vxinsn *insn = &frag->insn[loinsn];
    147 		
    148 		if (ofs < insn->dstofs) {
    149 			// How did that happen?
    150 			// We checked for ofs < frag->insn[0].dstofs above.
    151 			assert(0);
    152 		}
    153 		emu->cpu.eip = frag->eip + insn->srcofs;
    154 		
    155 		// At the beginning of a translated instruction (before the
    156 		// translation has begun to execute) all registers are valid.
    157 		if (ofs == insn->dstofs)
    158 			return VXSIG_TRAP | VXSIG_SAVE_ALL;
    159 		
    160 		// But some translations end up being more than one instruction,
    161 		// and we have to handle those specially, if they've executed 
    162 		// only some of the instructions.
    163 		int r;
    164 		switch (insn->itype) {
    165 		default:
    166 			assert(0);
    167 
    168 		case VXI_JUMP:
    169 		case VXI_ENDFRAG:
    170 			// Direct jumps don't trash any registers while
    171 			// making their way into vxrun_lookup_backpatch.
    172 			return VXSIG_TRAP | VXSIG_SAVE_ALL;
    173 
    174 		case VXI_CALL:
    175 			// Call pushes a return address onto the stack
    176 			// as the first instruction of the translation.
    177 			// We can't just pop it back off, because we would
    178 			// still need to restore the value that got overwritten.
    179 			// The target eip is stored in the word at byte 26 in
    180 			// the translation.
    181 			// Call does not trash any registers on the way 
    182 			// into vxrun_lookup_backpatch.
    183 			emu->cpu.eip = *(uint32_t*)(FRAGCODE(frag)+insn->dstofs+26);
    184 			return VXSIG_TRAP | VXSIG_SAVE_ALL;
    185 
    186 		case VXI_JUMPIND:
    187 			// Indirect jumps save ebx as their first instruction,
    188 			// and then they trash it.  Since we're not at the first
    189 			// instruction (see above), it might or might not be 
    190 			// trashed but is definitely already saved.  
    191 			return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
    192 
    193 		case VXI_CALLIND:
    194 			// Indirect call is like indirect jump except that it
    195 			// pushes a return address onto the stack in the
    196 			// instruction that ends at dstlen-5.  Until that point,
    197 			// only ebx has been trashed (but saved) and the stack
    198 			// is unmodified.  At that point, though, the stack is now
    199 			// modified and we must commit the instruction:
    200 			// ebx contains the target eip.
    201 			r = VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
    202 			if (ofs >= insn->dstofs + insn->dstlen - 5)
    203 				r |= VXSIG_SAVE_EBX_AS_EIP;
    204 			return r;
    205 			
    206 		case VXI_RETURN:
    207 		case VXI_RETURN_IMM:;
    208 			// Return is like indirect jump, but we have to treat it
    209 			// as committed once the pop ebx happens.
    210 			// Pop ebx happens at byte 7 of the translation.
    211 			r = VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
    212 			if (ofs - insn->dstofs > 7) {
    213 				r |= VXSIG_SAVE_EBX_AS_EIP;
    214 			
    215 				if (insn->itype == VXI_RETURN_IMM) {
    216 					// Return immediate is like return but if we've committed
    217 					// to popping ebx we also have to commit to popping
    218 					// the immediate number of bytes off the stack.
    219 					// The immediate is a 32-bit word at byte 10 of
    220 					// the translation, but it was originally only 16 bits
    221 					// in the original return instruction, so it's okay to
    222 					// truncate.
    223 					r |= VXSIG_ADD_COUNT_TO_ESP;
    224 					r |= *(uint16_t*)(FRAGCODE(frag)+insn->dstofs+10) << VXSIG_COUNT_SHIFT;
    225 				}
    226 			}
    227 			return r;
    228 
    229 		case VXI_TRAP:
    230 			// Traps save eax as their first instruction and then
    231 			// they trash it.  Since we're not at the first instruction
    232 			// (see above), it might or might not be trashed but
    233 			// is definitely already saved.
    234 			return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EAX);
    235 		
    236 		case VXI_LOOP:
    237 		case VXI_LOOPZ:
    238 		case VXI_LOOPNZ:
    239 			// The first instruction in the loop translation decrements ecx.
    240 			// The rest figure out whether to jump.
    241 			// We can back out by re-incrementing ecx.
    242 			// Untested (most loops translate into actual loop instructions).
    243 			return VXSIG_TRAP | VXSIG_SAVE_ALL | VXSIG_INC_ECX;
    244 		}
    245 	}
    246 
    247 	// Let's see.
    248 	// The fs segment register pointed at a vxemu, so we're in vxproc_run.
    249 	// The eip is not in rts.S, nor in run32/64.S.
    250 	// The eip is not in a fragment translation.
    251 	// We're not translating an instruction (emu->ininst == nil).
    252 	// That pretty much means we're in some interstitial instruction.
    253 	// Assume the registers are already saved.
    254 	return VXSIG_TRAP;
    255 }
    256 
    257 int vx32_siginit(void)
    258 {
    259 	stack_t ss;
    260 	void *stk;
    261 	struct sigaction sa;
    262 	
    263 	// See if there's already an alternate signal stack.
    264 	if (sigaltstack(NULL, &ss) < 0)
    265 		return -1;
    266 
    267 	assert(!(ss.ss_flags & SS_ONSTACK));
    268 	if (ss.ss_flags & SS_DISABLE) {
    269 		// Allocate an alternate signal stack.
    270 		ss.ss_size = 64*1024;
    271 		stk = malloc(ss.ss_size);
    272 		if (stk == NULL)
    273 			return -1;
    274 		ss.ss_flags = 0;
    275 		ss.ss_sp = stk;
    276 		if (sigaltstack(&ss, NULL) < 0) {
    277 			free(stk);
    278 			return -1;
    279 		}
    280 	}
    281 	
    282 	// Register our signal handler.
    283 	memset(&sa, 0, sizeof sa);
    284 	sa.sa_handler = (void*)sighandler;
    285 	sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
    286 	if (sigemptyset(&sa.sa_mask) < 0)
    287 		return -1;
    288 	if (sigaction(SIGSEGV, &sa, NULL) < 0)
    289 		return -1;
    290 	if (sigaction(SIGBUS, &sa, NULL) < 0)
    291 		return -1;
    292 	if (sigaction(SIGFPE, &sa, NULL) < 0)
    293 		return -1;
    294 	if (sigaction(SIGVTALRM, &sa, NULL) < 0)
    295 		return -1;
    296 	if (sigaction(SIGTRAP, &sa, NULL) < 0)
    297 		return -1;
    298 	return 0;
    299 }
    300