#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;}
#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;
}