// Code specific to x86 hosts running Linux. #define _GNU_SOURCE#include <stdio.h>#include <string.h>#include <signal.h>#include <assert.h>#include <ucontext.h>#include <sys/ucontext.h>#include <asm/ldt.h>#include <errno.h> #include "vx32.h"#include "vx32impl.h"#include "os.h" extern int modify_ldt(int, void*, unsigned long); int vxemu_map(vxemu *emu, vxmmap *mm){ struct vxproc *vxp; struct user_desc desc; uint ldt[2];#ifdef __x86_64 static int didflat;#endif vxp = emu->proc; emu->datasel = vxp->vxpno * 16 + 16 + 4 + 3; // 4=LDT, 3=RPL emu->emusel = emu->datasel + 8; if (emu->ldt_base != (uintptr_t)mm->base || emu->ldt_size != mm->size) { // Set up the process's data segment selector (for DS,ES,SS). memset(&desc, 0, sizeof(desc)); desc.seg_32bit = 1; desc.read_exec_only = 0; desc.limit_in_pages = 1; desc.seg_not_present = 0; desc.useable = 1; desc.entry_number = emu->datasel / 8; desc.base_addr = (uintptr_t)mm->base; desc.limit = (mm->size - 1) >> VXPAGESHIFT; desc.contents = MODIFY_LDT_CONTENTS_DATA; if (modify_ldt(1, &desc, sizeof(desc)) < 0) return -1; // Set up the process's vxemu segment selector (for FS). desc.entry_number = emu->emusel / 8; desc.base_addr = (uintptr_t)emu; desc.limit = (VXCODEBUFSIZE - 1) >> VXPAGESHIFT; desc.contents = MODIFY_LDT_CONTENTS_DATA; if (modify_ldt(1, &desc, sizeof(desc)) < 0) return -1; emu->ldt_base = (uintptr_t)mm->base; emu->ldt_size = mm->size; } #ifdef __x86_64 // Set up 32-bit mode code and data segments (not vxproc-specific), // giving access to the full low 32 bits of linear address space. // The code segment is necessary to get into 32-bit compatibility mode; // the data segment is needed because Linux for x86-64 // doesn't give 64-bit processes a "real" data segment by default // but instead just loads zero into the data segment selectors! emu->runptr.sel = FLATCODE; if (!didflat) { didflat = 1; memset(&desc, 0, sizeof(desc)); desc.seg_32bit = 1; desc.read_exec_only = 0; desc.limit_in_pages = 1; desc.seg_not_present = 0; desc.useable = 1; desc.entry_number = FLATCODE / 8; desc.base_addr = 0; desc.limit = 0xfffff; desc.contents = MODIFY_LDT_CONTENTS_CODE; if (modify_ldt(1, &desc, sizeof(desc)) < 0) return -1; /* * Linux 2.6.27 has a bug: it does not load the L (long mode) * bit from desc.lm when copying desc into its own * copy of the LDT entry on the kernel stack. * Instead, it leaves L uninitialized, picking up whatever * random bit was left on the kernel stack by the * previous call sequence. We need L to be 0. * If it ends up 1, the *ljmpq in run64.S will GP fault. * Luckily, we can look for this by asking to read * back the raw LDT bytes. If we observe this problem, * try to fix it by doing a modify_ldt with base = limit = 0, * which clears the entire stack ldt structure, and then * quickly do another modify_ldt with desc, hoping that * the bit will still be zero when we get there for the * second modify_ldt. I wish I were making this up. * This is fixed in Linus's git repository, but the Ubuntu * git repositories are still out of date. See for example * http://swtch.com/go/ubuntu-ldt * http://swtch.com/go/linus-ldt * * Remember, folks, Free Software is only free if your * time has no value. */ if(modify_ldt(0, ldt, sizeof ldt) < 0) return -1; if(ldt[1] & 0x00200000) { if (vx32_debugxlate) vxprint("FLATCODE LDT=%08x %08x; working around\n", ldt[0], ldt[1]); desc.limit = 0; modify_ldt(1, &desc, sizeof desc); desc.limit = 0xfffff; modify_ldt(1, &desc, sizeof desc); modify_ldt(0, ldt, sizeof ldt); if(ldt[1] & 0x00200000) { vxprint("cannot work around Linux FLATCODE bug\n"); errno = EBADE; return -1; } if (vx32_debugxlate) vxprint("FLATCODE LDT=%08x %08x\n", ldt[0], ldt[1]); } desc.entry_number = FLATDATA / 8; desc.base_addr = 0; desc.limit = 0xfffff; desc.contents = MODIFY_LDT_CONTENTS_DATA; if (modify_ldt(1, &desc, sizeof(desc)) < 0) return -1; } // Set up a far return vector in emu->retptr // for getting back into 64-bit long mode. extern void vxrun_return(); asm volatile("movw %%cs,%0" : "=r" (emu->retptr.sel)); emu->retptr.ofs = (uint32_t)(intptr_t)vxrun_return;#endif return 0;} static void dumpsigcontext(struct sigcontext *ctx){#ifdef i386 printf( "eax %08lx ebx %08lx\necx %08lx edx %08lx " "rsi %08lx rdi %08lx\nrbp %08lx rsp %08lx\n" "eip %08lx efl %08lx cs %04x\n" "err %08lx trapno %08lx cr2 %08lx\n", ctx->eax, ctx->ebx, ctx->ecx, ctx->edx, ctx->esi, ctx->edi, ctx->ebp, ctx->esp, ctx->eip, ctx->eflags, ctx->cs, ctx->err, ctx->trapno, ctx->cr2);#else printf( "rax %016lx rbx %016lx\nrcx %016lx rdx %016lx\n" "rsi %016lx rdi %016lx\nrbp %016lx rsp %016lx\n" "r8 %016lx r9 %016lx\nr10 %016lx r11 %016lx\n" "r12 %016lx r13 %016lx\nr14 %016lx r15 %016lx\n" "rip %016lx efl %016lx cs %04x ss %04x\n" "err %016lx trapno %016lx cr2 %016lx\n", ctx->rax, ctx->rbx, ctx->rcx, ctx->rdx, ctx->rsi, ctx->rdi, ctx->rbp, ctx->rsp, ctx->r8, ctx->r9, ctx->r10, ctx->r11, ctx->r12, ctx->r13, ctx->r14, ctx->r15, ctx->rip, ctx->eflags, ctx->cs, ctx->__pad0, ctx->err, ctx->trapno, ctx->cr2);#endif} #ifdef i386#define VX32_BELIEVE_EIP (ctx->ds == vs - 8)#define ctxeip eip#else#define VX32_BELIEVE_EIP (ctx->cs == FLATCODE) // On x86-64, make x86 names work for ctx->xxx.#define eax rax#define ebx rbx#define ecx rcx#define edx rdx#define esi rsi#define edi rdi#define esp rsp#define ebp rbp#define ctxeip rip#endif static voidfprestore(struct _fpstate *s){ asm volatile("frstor 0(%%eax); fwait\n" : : "a" (s) : "memory");} int vx32_sighandler(int signo, siginfo_t *si, void *v){ uint32_t trapeip; uint32_t magic; uint16_t vs; vxproc *vxp; vxemu *emu; struct sigcontext *ctx; ucontext_t *uc; mcontext_t *mc; int r; uc = v; mc = &uc->uc_mcontext; // same layout, and sigcontext is more convenient... ctx = (struct sigcontext*)mc; // We can't be sure that vxemu is running, // and thus that %VSEG is actually mapped to a // valid vxemu. The only way to tell is to look at %VSEG. // First sanity check vxproc segment number. asm("movw %"VSEGSTR",%0" : "=r" (vs)); if(vx32_debugxlate) vxprint("vx32_sighandler signo=%d eip=%#x esp=%#x vs=%#x\n", signo, ctx->ctxeip, ctx->esp, vs); if(vx32_debugxlate) dumpsigcontext(ctx); if ((vs & 15) != 15) // 8 (emu), LDT, RPL=3 return 0; // Okay, assume mapped; check for vxemu. asm("movl %"VSEGSTR":%1,%0" : "=r" (magic) : "m" (((vxemu*)0)->magic)); if (magic != VXEMU_MAGIC) return 0; // Okay, we're convinced. // Find current vxproc and vxemu. asm("mov %"VSEGSTR":%1,%0" : "=r" (vxp) : "m" (((vxemu*)0)->proc)); emu = vxp->emu; // Get back our regular host segment register state, // so that thread-local storage and such works. vxrun_cleanup(emu); // dumpsigcontext(ctx); if (VX32_BELIEVE_EIP) trapeip = ctx->ctxeip; else trapeip = 0xffffffff; int newtrap; switch(signo){ case SIGSEGV: case SIGBUS: newtrap = VXTRAP_PAGEFAULT; break; case SIGFPE: newtrap = VXTRAP_FLOAT; break; case SIGVTALRM: newtrap = VXTRAP_IRQ + VXIRQ_TIMER; break; case SIGTRAP: // Linux sends SIGTRAP when it gets a processor // debug exception, which is caused by single-stepping // with the TF bit, among other things. The processor // turns off the TF bit before generating the trap, but // it appears that Linux turns it back on for us. // Let's use it to confirm that this is a single-step trap. if (ctx->eflags & EFLAGS_TF){ newtrap = VXTRAP_SINGLESTEP; ctx->eflags &= ~EFLAGS_TF; }else{ vxprint("Unexpected sigtrap eflags=%#x\n", ctx->eflags); newtrap = VXTRAP_SIGNAL + signo; } break; default: newtrap = VXTRAP_SIGNAL + signo; break; } int replaced_trap = 0; if (emu->cpu_trap) { // There's already a pending trap! // Handle the new trap, and assume that when it // finishes, restarting the code at cpu.eip will trigger // the old trap again. // Have to fix up eip for int 0x30 and syscall instructions. if (emu->cpu_trap == VXTRAP_SYSCALL || (emu->cpu_trap&VXTRAP_CATEGORY) == VXTRAP_SOFT) emu->cpu.eip -= 2; replaced_trap = emu->cpu_trap; } emu->cpu_trap = newtrap; r = vxemu_sighandler(emu, trapeip); if (r == VXSIG_SINGLESTEP){ // Vxemu_sighandler wants us to single step. // Execution state is in intermediate state - don't touch. ctx->eflags |= EFLAGS_TF; // x86 TF (single-step) bit vxrun_setup(emu); return 1; } // Copy execution state into emu. if ((r & VXSIG_SAVE_ALL) == VXSIG_SAVE_ALL) { emu->cpu.reg[EAX] = ctx->eax; emu->cpu.reg[EBX] = ctx->ebx; emu->cpu.reg[ECX] = ctx->ecx; emu->cpu.reg[EDX] = ctx->edx; emu->cpu.reg[ESI] = ctx->esi; emu->cpu.reg[EDI] = ctx->edi; emu->cpu.reg[ESP] = ctx->esp; // or esp_at_signal ??? emu->cpu.reg[EBP] = ctx->ebp; emu->cpu.eflags = ctx->eflags; } else if (r & VXSIG_SAVE_ALL) { if (r & VXSIG_SAVE_EAX) emu->cpu.reg[EAX] = ctx->eax; if (r & VXSIG_SAVE_EBX) emu->cpu.reg[EBX] = ctx->ebx; if (r & VXSIG_SAVE_ECX) emu->cpu.reg[ECX] = ctx->ecx; if (r & VXSIG_SAVE_EDX) emu->cpu.reg[EDX] = ctx->edx; if (r & VXSIG_SAVE_ESI) emu->cpu.reg[ESI] = ctx->esi; if (r & VXSIG_SAVE_EDI) emu->cpu.reg[EDI] = ctx->edi; if (r & VXSIG_SAVE_ESP) emu->cpu.reg[ESP] = ctx->esp; // or esp_at_signal ??? if (r & VXSIG_SAVE_EBP) emu->cpu.reg[EBP] = ctx->ebp; if (r & VXSIG_SAVE_EFLAGS) emu->cpu.eflags = ctx->eflags; } r &= ~VXSIG_SAVE_ALL; if (r & VXSIG_SAVE_EBX_AS_EIP) emu->cpu.eip = ctx->ebx; r &= ~VXSIG_SAVE_EBX_AS_EIP; if (r & VXSIG_ADD_COUNT_TO_ESP) { emu->cpu.reg[ESP] += (uint16_t)(r >> VXSIG_COUNT_SHIFT); r &= ~VXSIG_ADD_COUNT_TO_ESP; r &= ~(0xFFFF << VXSIG_COUNT_SHIFT); } if (r & VXSIG_INC_ECX) { emu->cpu.reg[ECX]++; r &= ~VXSIG_INC_ECX; } if (r == VXSIG_TRAP) { if (emu->trapenv == NULL) return 0; emu->cpu.traperr = ctx->err; // Usually, ctx->cr2 == si->si_addr. // But on a segmentation fault (as opposed to a paging fault), // cr2 is not updated and the kernel sends an si_addr == 0. // Be sure to use si_addr, not cr2. emu->cpu.trapva = (uint32_t)(uintptr_t)si->si_addr; memmove(mc->gregs, emu->trapenv->gregs, sizeof emu->trapenv->gregs); return 1; } // The signal handler is confused; so are we. return 0;}
// Code specific to x86 hosts running Linux.
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <ucontext.h>
#include <sys/ucontext.h>
#include <asm/ldt.h>
#include <errno.h>
#include "vx32.h"
#include "vx32impl.h"
#include "os.h"
extern int modify_ldt(int, void*, unsigned long);
int vxemu_map(vxemu *emu, vxmmap *mm)
{
struct vxproc *vxp;
struct user_desc desc;
uint ldt[2];
#ifdef __x86_64
static int didflat;
#endif
vxp = emu->proc;
emu->datasel = vxp->vxpno * 16 + 16 + 4 + 3; // 4=LDT, 3=RPL
emu->emusel = emu->datasel + 8;
if (emu->ldt_base != (uintptr_t)mm->base || emu->ldt_size != mm->size) {
// Set up the process's data segment selector (for DS,ES,SS).
memset(&desc, 0, sizeof(desc));
desc.seg_32bit = 1;
desc.read_exec_only = 0;
desc.limit_in_pages = 1;
desc.seg_not_present = 0;
desc.useable = 1;
desc.entry_number = emu->datasel / 8;
desc.base_addr = (uintptr_t)mm->base;
desc.limit = (mm->size - 1) >> VXPAGESHIFT;
desc.contents = MODIFY_LDT_CONTENTS_DATA;
if (modify_ldt(1, &desc, sizeof(desc)) < 0)
return -1;
// Set up the process's vxemu segment selector (for FS).
desc.entry_number = emu->emusel / 8;
desc.base_addr = (uintptr_t)emu;
desc.limit = (VXCODEBUFSIZE - 1) >> VXPAGESHIFT;
desc.contents = MODIFY_LDT_CONTENTS_DATA;
if (modify_ldt(1, &desc, sizeof(desc)) < 0)
return -1;
emu->ldt_base = (uintptr_t)mm->base;
emu->ldt_size = mm->size;
}
#ifdef __x86_64
// Set up 32-bit mode code and data segments (not vxproc-specific),
// giving access to the full low 32 bits of linear address space.
// The code segment is necessary to get into 32-bit compatibility mode;
// the data segment is needed because Linux for x86-64
// doesn't give 64-bit processes a "real" data segment by default
// but instead just loads zero into the data segment selectors!
emu->runptr.sel = FLATCODE;
if (!didflat) {
didflat = 1;
memset(&desc, 0, sizeof(desc));
desc.seg_32bit = 1;
desc.read_exec_only = 0;
desc.limit_in_pages = 1;
desc.seg_not_present = 0;
desc.useable = 1;
desc.entry_number = FLATCODE / 8;
desc.base_addr = 0;
desc.limit = 0xfffff;
desc.contents = MODIFY_LDT_CONTENTS_CODE;
if (modify_ldt(1, &desc, sizeof(desc)) < 0)
return -1;
/*
* Linux 2.6.27 has a bug: it does not load the L (long mode)
* bit from desc.lm when copying desc into its own
* copy of the LDT entry on the kernel stack.
* Instead, it leaves L uninitialized, picking up whatever
* random bit was left on the kernel stack by the
* previous call sequence. We need L to be 0.
* If it ends up 1, the *ljmpq in run64.S will GP fault.
* Luckily, we can look for this by asking to read
* back the raw LDT bytes. If we observe this problem,
* try to fix it by doing a modify_ldt with base = limit = 0,
* which clears the entire stack ldt structure, and then
* quickly do another modify_ldt with desc, hoping that
* the bit will still be zero when we get there for the
* second modify_ldt. I wish I were making this up.
* This is fixed in Linus's git repository, but the Ubuntu
* git repositories are still out of date. See for example
* http://swtch.com/go/ubuntu-ldt
* http://swtch.com/go/linus-ldt
*
* Remember, folks, Free Software is only free if your
* time has no value.
*/
if(modify_ldt(0, ldt, sizeof ldt) < 0)
return -1;
if(ldt[1] & 0x00200000) {
if (vx32_debugxlate)
vxprint("FLATCODE LDT=%08x %08x; working around\n", ldt[0], ldt[1]);
desc.limit = 0;
modify_ldt(1, &desc, sizeof desc);
desc.limit = 0xfffff;
modify_ldt(1, &desc, sizeof desc);
modify_ldt(0, ldt, sizeof ldt);
if(ldt[1] & 0x00200000) {
vxprint("cannot work around Linux FLATCODE bug\n");
errno = EBADE;
return -1;
}
if (vx32_debugxlate)
vxprint("FLATCODE LDT=%08x %08x\n", ldt[0], ldt[1]);
}
desc.entry_number = FLATDATA / 8;
desc.base_addr = 0;
desc.limit = 0xfffff;
desc.contents = MODIFY_LDT_CONTENTS_DATA;
if (modify_ldt(1, &desc, sizeof(desc)) < 0)
return -1;
}
// Set up a far return vector in emu->retptr
// for getting back into 64-bit long mode.
extern void vxrun_return();
asm volatile("movw %%cs,%0" : "=r" (emu->retptr.sel));
emu->retptr.ofs = (uint32_t)(intptr_t)vxrun_return;
#endif
return 0;
}
static void dumpsigcontext(struct sigcontext *ctx)
{
#ifdef i386
printf(
"eax %08lx ebx %08lx\necx %08lx edx %08lx "
"rsi %08lx rdi %08lx\nrbp %08lx rsp %08lx\n"
"eip %08lx efl %08lx cs %04x\n"
"err %08lx trapno %08lx cr2 %08lx\n",
ctx->eax, ctx->ebx, ctx->ecx, ctx->edx,
ctx->esi, ctx->edi, ctx->ebp, ctx->esp,
ctx->eip, ctx->eflags, ctx->cs,
ctx->err, ctx->trapno, ctx->cr2);
#else
printf(
"rax %016lx rbx %016lx\nrcx %016lx rdx %016lx\n"
"rsi %016lx rdi %016lx\nrbp %016lx rsp %016lx\n"
"r8 %016lx r9 %016lx\nr10 %016lx r11 %016lx\n"
"r12 %016lx r13 %016lx\nr14 %016lx r15 %016lx\n"
"rip %016lx efl %016lx cs %04x ss %04x\n"
"err %016lx trapno %016lx cr2 %016lx\n",
ctx->rax, ctx->rbx, ctx->rcx, ctx->rdx,
ctx->rsi, ctx->rdi, ctx->rbp, ctx->rsp,
ctx->r8, ctx->r9, ctx->r10, ctx->r11,
ctx->r12, ctx->r13, ctx->r14, ctx->r15,
ctx->rip, ctx->eflags, ctx->cs, ctx->__pad0,
ctx->err, ctx->trapno, ctx->cr2);
#endif
}
#ifdef i386
#define VX32_BELIEVE_EIP (ctx->ds == vs - 8)
#define ctxeip eip
#else
#define VX32_BELIEVE_EIP (ctx->cs == FLATCODE)
// On x86-64, make x86 names work for ctx->xxx.
#define eax rax
#define ebx rbx
#define ecx rcx
#define edx rdx
#define esi rsi
#define edi rdi
#define esp rsp
#define ebp rbp
#define ctxeip rip
#endif
static void
fprestore(struct _fpstate *s)
{
asm volatile("frstor 0(%%eax); fwait\n" : : "a" (s) : "memory");
}
int vx32_sighandler(int signo, siginfo_t *si, void *v)
{
uint32_t trapeip;
uint32_t magic;
uint16_t vs;
vxproc *vxp;
vxemu *emu;
struct sigcontext *ctx;
ucontext_t *uc;
mcontext_t *mc;
int r;
uc = v;
mc = &uc->uc_mcontext;
// same layout, and sigcontext is more convenient...
ctx = (struct sigcontext*)mc;
// We can't be sure that vxemu is running,
// and thus that %VSEG is actually mapped to a
// valid vxemu. The only way to tell is to look at %VSEG.
// First sanity check vxproc segment number.
asm("movw %"VSEGSTR",%0"
: "=r" (vs));
if(vx32_debugxlate) vxprint("vx32_sighandler signo=%d eip=%#x esp=%#x vs=%#x\n",
signo, ctx->ctxeip, ctx->esp, vs);
if(vx32_debugxlate) dumpsigcontext(ctx);
if ((vs & 15) != 15) // 8 (emu), LDT, RPL=3
return 0;
// Okay, assume mapped; check for vxemu.
asm("movl %"VSEGSTR":%1,%0"
: "=r" (magic)
: "m" (((vxemu*)0)->magic));
if (magic != VXEMU_MAGIC)
return 0;
// Okay, we're convinced.
// Find current vxproc and vxemu.
asm("mov %"VSEGSTR":%1,%0"
: "=r" (vxp)
: "m" (((vxemu*)0)->proc));
emu = vxp->emu;
// Get back our regular host segment register state,
// so that thread-local storage and such works.
vxrun_cleanup(emu);
// dumpsigcontext(ctx);
if (VX32_BELIEVE_EIP)
trapeip = ctx->ctxeip;
else
trapeip = 0xffffffff;
int newtrap;
switch(signo){
case SIGSEGV:
case SIGBUS:
newtrap = VXTRAP_PAGEFAULT;
break;
case SIGFPE:
newtrap = VXTRAP_FLOAT;
break;
case SIGVTALRM:
newtrap = VXTRAP_IRQ + VXIRQ_TIMER;
break;
case SIGTRAP:
// Linux sends SIGTRAP when it gets a processor
// debug exception, which is caused by single-stepping
// with the TF bit, among other things. The processor
// turns off the TF bit before generating the trap, but
// it appears that Linux turns it back on for us.
// Let's use it to confirm that this is a single-step trap.
if (ctx->eflags & EFLAGS_TF){
newtrap = VXTRAP_SINGLESTEP;
ctx->eflags &= ~EFLAGS_TF;
}else{
vxprint("Unexpected sigtrap eflags=%#x\n", ctx->eflags);
newtrap = VXTRAP_SIGNAL + signo;
}
break;
default:
newtrap = VXTRAP_SIGNAL + signo;
break;
}
int replaced_trap = 0;
if (emu->cpu_trap) {
// There's already a pending trap!
// Handle the new trap, and assume that when it
// finishes, restarting the code at cpu.eip will trigger
// the old trap again.
// Have to fix up eip for int 0x30 and syscall instructions.
if (emu->cpu_trap == VXTRAP_SYSCALL ||
(emu->cpu_trap&VXTRAP_CATEGORY) == VXTRAP_SOFT)
emu->cpu.eip -= 2;
replaced_trap = emu->cpu_trap;
}
emu->cpu_trap = newtrap;
r = vxemu_sighandler(emu, trapeip);
if (r == VXSIG_SINGLESTEP){
// Vxemu_sighandler wants us to single step.
// Execution state is in intermediate state - don't touch.
ctx->eflags |= EFLAGS_TF; // x86 TF (single-step) bit
vxrun_setup(emu);
return 1;
}
// Copy execution state into emu.
if ((r & VXSIG_SAVE_ALL) == VXSIG_SAVE_ALL) {
emu->cpu.reg[EAX] = ctx->eax;
emu->cpu.reg[EBX] = ctx->ebx;
emu->cpu.reg[ECX] = ctx->ecx;
emu->cpu.reg[EDX] = ctx->edx;
emu->cpu.reg[ESI] = ctx->esi;
emu->cpu.reg[EDI] = ctx->edi;
emu->cpu.reg[ESP] = ctx->esp; // or esp_at_signal ???
emu->cpu.reg[EBP] = ctx->ebp;
emu->cpu.eflags = ctx->eflags;
} else if (r & VXSIG_SAVE_ALL) {
if (r & VXSIG_SAVE_EAX)
emu->cpu.reg[EAX] = ctx->eax;
if (r & VXSIG_SAVE_EBX)
emu->cpu.reg[EBX] = ctx->ebx;
if (r & VXSIG_SAVE_ECX)
emu->cpu.reg[ECX] = ctx->ecx;
if (r & VXSIG_SAVE_EDX)
emu->cpu.reg[EDX] = ctx->edx;
if (r & VXSIG_SAVE_ESI)
emu->cpu.reg[ESI] = ctx->esi;
if (r & VXSIG_SAVE_EDI)
emu->cpu.reg[EDI] = ctx->edi;
if (r & VXSIG_SAVE_ESP)
emu->cpu.reg[ESP] = ctx->esp; // or esp_at_signal ???
if (r & VXSIG_SAVE_EBP)
emu->cpu.reg[EBP] = ctx->ebp;
if (r & VXSIG_SAVE_EFLAGS)
emu->cpu.eflags = ctx->eflags;
}
r &= ~VXSIG_SAVE_ALL;
if (r & VXSIG_SAVE_EBX_AS_EIP)
emu->cpu.eip = ctx->ebx;
r &= ~VXSIG_SAVE_EBX_AS_EIP;
if (r & VXSIG_ADD_COUNT_TO_ESP) {
emu->cpu.reg[ESP] += (uint16_t)(r >> VXSIG_COUNT_SHIFT);
r &= ~VXSIG_ADD_COUNT_TO_ESP;
r &= ~(0xFFFF << VXSIG_COUNT_SHIFT);
}
if (r & VXSIG_INC_ECX) {
emu->cpu.reg[ECX]++;
r &= ~VXSIG_INC_ECX;
}
if (r == VXSIG_TRAP) {
if (emu->trapenv == NULL)
return 0;
emu->cpu.traperr = ctx->err;
// Usually, ctx->cr2 == si->si_addr.
// But on a segmentation fault (as opposed to a paging fault),
// cr2 is not updated and the kernel sends an si_addr == 0.
// Be sure to use si_addr, not cr2.
emu->cpu.trapva = (uint32_t)(uintptr_t)si->si_addr;
memmove(mc->gregs, emu->trapenv->gregs, sizeof emu->trapenv->gregs);
return 1;
}
// The signal handler is confused; so are we.
return 0;
}