300 lines · 9.9 KB · C
RawDownload
#include <sys/signal.h>#include <ucontext.h>#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <string.h>#include <signal.h> #include "vx32.h"#include "vx32impl.h" static void sighandler(int signo, siginfo_t *si, void *ucontext){	if (vx32_sighandler(signo, si, ucontext)){		// Back into the fire.		return;	}		// vx32_sighandler thought that vx32 wasn't	// running or otherwise didn't know what to do.	// TODO: If there was a handler registered before us, call it?	// If the signal is not important, ignore it.	if (signo == SIGVTALRM || signo == SIGALRM)		return;		// Otherwise, deregister the handler and let the signal	// happen again -- this time we'll crash and get a core dump.	vxprint("Unregistering signal handler %d\n", signo);	signal(signo, SIG_DFL);} // vx32_sighandler saves execution context, finds emu,// and calls vxemu_sighandler.  If we're here, the fs segment// register pointed at a valid emu, but we still might not have// been executing actual vx32 translations.// Trapeip is 0xffffffff if the other segment registers // suggest we weren't inside vxrun_setup ... vxrun_cleanup.int vxemu_sighandler(vxemu *emu, uint32_t trapeip){	if (vx32_debugxlate)		vxprint("vxemu_sighandler %p %#x\n", emu, trapeip); 	if (emu->cpu_trap == 0)		return VXSIG_ERROR;		// Not in vx32 code: assume registers saved already and trap.	if (trapeip == 0xffffffff)		return VXSIG_TRAP;		if (emu->ininst) {		// In the middle of translating an instruction,		// so the registers are already saved.		// The signal hander knows the exact fault address,		// which may be a bit after emu->ininst.		// In fact, the fault address may be one or two		// instructions past emu->cpu.eip, and a real 		// processor would not throw the trap until it		// actually got to the unreadable instruction,		// but there's not really any harm in trapping now instead.		return VXSIG_TRAP;	} 	// Single-stepping?  Use the original trap code.	if (emu->cpu_trap == VXTRAP_SINGLESTEP && emu->saved_trap){		emu->cpu_trap = emu->saved_trap;		emu->saved_trap = 0;	} 	// In vx32 runtime code (rts.S, run32/64.S), the registers are in flux.	// Single-step until we can get out; then they'll be safe to look at.	// This only makes sense if the trap is an external trap like a timer.	char *eip = (char*)(uintptr_t)trapeip;	if ((emu->cpu_trap&VXTRAP_CATEGORY) != VXTRAP_CPU){		// The check for run32/64.S doesn't look for the entire file.		// Instead it looks for anywhere in the file except		// vxrun_cleanup.  If we single-step out of vxrun_cleanup,		// then vx32_sighandler will get a single-step trap when		// the vx32 segment register doesn't point at emu and 		// won't know what to do.  If we're in vxrun_cleanup, then		// all the cpu registers are known to be saved.		extern char vx_rts_S_start[], vx_rts_S_end[];		extern char vx_run_S_start[];		if ((vx_rts_S_start <= eip && eip < vx_rts_S_end)		||  (vx_run_S_start <= eip && eip < (char*)vxrun_cleanup)){		SingleStep:			if(++emu->nsinglestep > 500){				// Give up: something is wrong.				vxprint("vx32: single-stepping but stuck\n");				return VXSIG_ERROR;			}			emu->saved_trap = emu->cpu_trap;			emu->cpu_trap = 0;			return VXSIG_SINGLESTEP;		}	}	emu->nsinglestep = 0; 	// In translated code buffer?  Find original eip.	if ((char*)emu->codebuf <= eip && eip < (char*)emu->codefree){		// Binary search for the translated chunk in which the trap occurred.		// NB: frags appear in the opposite order as the code itself.		uint32_t *codetab = emu->codetab;		unsigned lofrag = 0;		unsigned hifrag = (uint32_t*)emu->codetop - codetab - 1;		while (hifrag > lofrag) {			unsigned midfrag = (lofrag + hifrag) / 2;			uint32_t mideip = codetab[midfrag];			if (trapeip >= mideip)				hifrag = midfrag;			else				lofrag = midfrag + 1;		}		struct vxfrag *frag = (vxfrag*)(intptr_t)codetab[lofrag]; 		// The eip is known to be in the translation buffer,		// but the buffer contains both fragment headers and		// fragment code.  Make sure it's not in a fragment header.		if (trapeip < (uint32_t)(intptr_t)FRAGCODE(frag))			return VXSIG_ERROR;		unsigned ofs = trapeip - (uint32_t)(intptr_t)FRAGCODE(frag); 		// The very first instruction in each fragment is the one that		// restores ebx.  It is not included in the table, because it 		// doesn't correspond to any original source instruction.		// If we're there, pretend we're at the first instruction in the		// fragment but don't save ebx -- it's already saved.		if (ofs < frag->insn[0].dstofs) {			emu->cpu.eip = frag->eip + frag->insn[0].srcofs;			return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);		} 		// Binary search through this fragment's instruction table		// for the actual faulting translated instruction,		// and compute the corresponding EIP in the original vx32 code.		unsigned ninsn = frag->ninsn;		unsigned loinsn = 0;		unsigned hiinsn = ninsn - 1;		while (hiinsn > loinsn) {			unsigned midinsn = (loinsn + hiinsn + 1) / 2;			unsigned midofs = frag->insn[midinsn].dstofs;			if (ofs >= midofs)				loinsn = midinsn;			else				hiinsn = midinsn - 1;		}		struct vxinsn *insn = &frag->insn[loinsn];				if (ofs < insn->dstofs) {			// How did that happen?			// We checked for ofs < frag->insn[0].dstofs above.			assert(0);		}		emu->cpu.eip = frag->eip + insn->srcofs;				// At the beginning of a translated instruction (before the		// translation has begun to execute) all registers are valid.		if (ofs == insn->dstofs)			return VXSIG_TRAP | VXSIG_SAVE_ALL;				// But some translations end up being more than one instruction,		// and we have to handle those specially, if they've executed 		// only some of the instructions.		int r;		switch (insn->itype) {		default:			assert(0); 		case VXI_JUMP:		case VXI_ENDFRAG:			// Direct jumps don't trash any registers while			// making their way into vxrun_lookup_backpatch.			return VXSIG_TRAP | VXSIG_SAVE_ALL; 		case VXI_CALL:			// Call pushes a return address onto the stack			// as the first instruction of the translation.			// We can't just pop it back off, because we would			// still need to restore the value that got overwritten.			// The target eip is stored in the word at byte 26 in			// the translation.			// Call does not trash any registers on the way 			// into vxrun_lookup_backpatch.			emu->cpu.eip = *(uint32_t*)(FRAGCODE(frag)+insn->dstofs+26);			return VXSIG_TRAP | VXSIG_SAVE_ALL; 		case VXI_JUMPIND:			// Indirect jumps save ebx as their first instruction,			// and then they trash it.  Since we're not at the first			// instruction (see above), it might or might not be 			// trashed but is definitely already saved.  			return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX); 		case VXI_CALLIND:			// Indirect call is like indirect jump except that it			// pushes a return address onto the stack in the			// instruction that ends at dstlen-5.  Until that point,			// only ebx has been trashed (but saved) and the stack			// is unmodified.  At that point, though, the stack is now			// modified and we must commit the instruction:			// ebx contains the target eip.			r = VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);			if (ofs >= insn->dstofs + insn->dstlen - 5)				r |= VXSIG_SAVE_EBX_AS_EIP;			return r;					case VXI_RETURN:		case VXI_RETURN_IMM:;			// Return is like indirect jump, but we have to treat it			// as committed once the pop ebx happens.			// Pop ebx happens at byte 7 of the translation.			r = VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);			if (ofs - insn->dstofs > 7) {				r |= VXSIG_SAVE_EBX_AS_EIP;							if (insn->itype == VXI_RETURN_IMM) {					// Return immediate is like return but if we've committed					// to popping ebx we also have to commit to popping					// the immediate number of bytes off the stack.					// The immediate is a 32-bit word at byte 10 of					// the translation, but it was originally only 16 bits					// in the original return instruction, so it's okay to					// truncate.					r |= VXSIG_ADD_COUNT_TO_ESP;					r |= *(uint16_t*)(FRAGCODE(frag)+insn->dstofs+10) << VXSIG_COUNT_SHIFT;				}			}			return r; 		case VXI_TRAP:			// Traps save eax as their first instruction and then			// they trash it.  Since we're not at the first instruction			// (see above), it might or might not be trashed but			// is definitely already saved.			return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EAX);				case VXI_LOOP:		case VXI_LOOPZ:		case VXI_LOOPNZ:			// The first instruction in the loop translation decrements ecx.			// The rest figure out whether to jump.			// We can back out by re-incrementing ecx.			// Untested (most loops translate into actual loop instructions).			return VXSIG_TRAP | VXSIG_SAVE_ALL | VXSIG_INC_ECX;		}	} 	// Let's see.	// The fs segment register pointed at a vxemu, so we're in vxproc_run.	// The eip is not in rts.S, nor in run32/64.S.	// The eip is not in a fragment translation.	// We're not translating an instruction (emu->ininst == nil).	// That pretty much means we're in some interstitial instruction.	// Assume the registers are already saved.	return VXSIG_TRAP;} int vx32_siginit(void){	stack_t ss;	void *stk;	struct sigaction sa;		// See if there's already an alternate signal stack.	if (sigaltstack(NULL, &ss) < 0)		return -1; 	assert(!(ss.ss_flags & SS_ONSTACK));	if (ss.ss_flags & SS_DISABLE) {		// Allocate an alternate signal stack.		ss.ss_size = 64*1024;		stk = malloc(ss.ss_size);		if (stk == NULL)			return -1;		ss.ss_flags = 0;		ss.ss_sp = stk;		if (sigaltstack(&ss, NULL) < 0) {			free(stk);			return -1;		}	}		// Register our signal handler.	memset(&sa, 0, sizeof sa);	sa.sa_handler = (void*)sighandler;	sa.sa_flags = SA_ONSTACK | SA_SIGINFO;	if (sigemptyset(&sa.sa_mask) < 0)		return -1;	if (sigaction(SIGSEGV, &sa, NULL) < 0)		return -1;	if (sigaction(SIGBUS, &sa, NULL) < 0)		return -1;	if (sigaction(SIGFPE, &sa, NULL) < 0)		return -1;	if (sigaction(SIGVTALRM, &sa, NULL) < 0)		return -1;	if (sigaction(SIGTRAP, &sa, NULL) < 0)		return -1;	return 0;} 

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t