vx32

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

darwin.c (7339B)


      1 // Code specific to x86 hosts running Mac OS X
      2 
      3 // get access to mcontext and thread_state fields with their normal names,
      4 // not with everything prefixed by __ due to namespace cleanliness paranoia
      5 #define __DARWIN_UNIX03 0
      6 
      7 #include <string.h>
      8 #include <signal.h>
      9 #include <assert.h>
     10 #include <ucontext.h>
     11 #include <ucontext.h>
     12 #include <architecture/i386/table.h>
     13 #include <i386/user_ldt.h>
     14 
     15 #include "vx32.h"
     16 #include "vx32impl.h"
     17 #include "os.h"
     18 
     19 #if __LP64__
     20 #error "vx32 on Darwin x86-64 is unimplemented."
     21 #endif
     22 
     23 // First LDT selector number to use for our translator segments.
     24 // Avoid stepping on the low selectors used by the kernel.
     25 // XXX is there a way to know how many selectors the kernel uses?
     26 #define SELOFS	((32*8) + 4 + 3)		// 4=LDT, 3=RPL
     27 
     28 
     29 static void setbase(union ldt_entry *desc, unsigned long base)
     30 {
     31 	desc->code.base00 = base & 0xffff;
     32 	desc->code.base16 = (base >> 16) & 0xff;
     33 	desc->code.base24 = base >> 24;
     34 }
     35 
     36 static void setlimit(union ldt_entry *desc, unsigned long limit)
     37 {
     38 	desc->code.limit00 = limit & 0xffff;
     39 	desc->code.limit16 = limit >> 16;
     40 }
     41 
     42 
     43 // Set up LDT segments needed by the instruction translater
     44 // whenever a vx32 process's memory is mapped.
     45 int vxemu_map(vxemu *emu, vxmmap *mm)
     46 {
     47 	int s, sel;
     48 	struct vxproc *vxp;
     49 	union ldt_entry desc;
     50 	
     51 	vxp = emu->proc;
     52 
     53 	if (emu->ldt_base != (uintptr_t)mm->base || emu->ldt_size != mm->size) {
     54 		// Set up the process's data segment selector (for DS,ES,SS).
     55 		memset(&desc, 0, sizeof(desc));
     56 		setbase(&desc, (unsigned long)mm->base);
     57 		setlimit(&desc, (mm->size - 1) >> VXPAGESHIFT);
     58 		desc.data.type = DESC_DATA_WRITE;
     59 		desc.data.dpl = 3;
     60 		desc.data.present = 1;
     61 		desc.data.stksz = DESC_DATA_32B;
     62 		desc.data.granular = 1;
     63 		if(emu->datasel == 0){
     64 			if ((s = i386_set_ldt(LDT_AUTO_ALLOC, &desc, 1)) < 0)
     65 				return -1;
     66 			emu->datasel = (s<<3) + 4 + 3;	// 4=LDT, 3=RPL
     67 		}else if(i386_set_ldt(emu->datasel >> 3, &desc, 1) < 0)
     68 			return -1;
     69 
     70 		// Set up the process's vxemu segment selector (for FS).
     71 		setbase(&desc, (unsigned long)emu);
     72 		setlimit(&desc, (VXCODEBUFSIZE - 1) >> VXPAGESHIFT);
     73 		if(emu->emusel == 0){
     74 			if ((s = i386_set_ldt(LDT_AUTO_ALLOC, &desc, 1)) < 0)
     75 				return -1;
     76 			emu->emusel = (s<<3) + 4 + 3;	// 4=LDT, 3=RPL
     77 		}else if(i386_set_ldt(emu->emusel >> 3, &desc, 1) < 0)
     78 			return -1;
     79 
     80 		emu->ldt_base = (uintptr_t)mm->base;
     81 		emu->ldt_size = mm->size;
     82 	}
     83 
     84 	return 0;
     85 }
     86 
     87 // Note: it's not generally safe to call printf from here,
     88 // even just for debugging, because we're running on a small signal stack.
     89 // Vxprint is an attempt to use less stack space but
     90 // even that is not a sure thing.
     91 int vx32_sighandler(int signo, siginfo_t *si, void *v)
     92 {
     93 	int r;
     94 	uint32_t magic;
     95 	uint16_t vs, oldvs;
     96 	vxproc *vxp;
     97 	vxemu *emu;
     98 	ucontext_t *uc;
     99 	mcontext_t ctx;
    100 
    101 	// In Darwin, 'mcontext_t' is actually a pointer...
    102 	uc = v;
    103 	ctx = uc->uc_mcontext;
    104 
    105 	// We can't be sure that vxemu is running,
    106 	// and thus that %VSEG is actually mapped to a
    107 	// valid vxemu.  The only way to tell is to look at %VSEG.
    108 
    109 	// First sanity check vxproc segment number.
    110 	// Darwin reset the register before entering the handler!
    111 	asm("movw %"VSEGSTR",%0"
    112 		: "=r" (oldvs));
    113 	vs = ctx->ss_vs & 0xFFFF;	/* ss_vs #defined in os.h */
    114 
    115 	if(0) vxprint("vx32_sighandler signo=%d eip=%#x esp=%#x vs=%#x currentvs=%#x\n",
    116 		signo, ctx->ss.eip, ctx->ss.esp, vs, oldvs);
    117 
    118 	if ((vs & 7) != 7)	// LDT, RPL=3
    119 		return 0;
    120 
    121 	// Okay, assume mapped; check for vxemu by reading
    122 	// first word from vs.  Have to put vs into the segment
    123 	// register and then take it back out.
    124 	asm("movw %"VSEGSTR",%1\n"
    125 		"movw %2,%"VSEGSTR"\n"
    126 		"movl %"VSEGSTR":%3,%0\n"
    127 		"movw %1,%"VSEGSTR"\n"
    128 		: "=r" (magic), "=r" (oldvs)
    129 		: "r" (vs), "m" (((vxemu*)0)->magic));
    130 	if (magic != VXEMU_MAGIC)
    131 		return 0;
    132 
    133 	// Okay, we're convinced.
    134 
    135 	// Find current vxproc and vxemu.
    136 	asm("movw %"VSEGSTR",%1\n"
    137 		"movw %2,%"VSEGSTR"\n"
    138 		"movl %"VSEGSTR":%3,%0\n"
    139 		"movw %1,%"VSEGSTR"\n"
    140 		: "=r" (vxp), "=r" (oldvs)
    141 		: "r" (vs), "m" (((vxemu*)0)->proc));
    142 	emu = vxp->emu;
    143 
    144 	// Get back our regular host segment register state,
    145 	// so that thread-local storage and such works.
    146 	vxrun_cleanup(emu);
    147 
    148 	int newtrap;
    149 	switch(signo){
    150 	case SIGSEGV:
    151 	case SIGBUS:
    152 		newtrap = VXTRAP_PAGEFAULT;
    153 		break;
    154 	
    155 	case SIGFPE:
    156 		newtrap = VXTRAP_FLOAT;
    157 		break;
    158 
    159 	case SIGVTALRM:
    160 		newtrap = VXTRAP_IRQ + VXIRQ_TIMER;
    161 		break;
    162 
    163 	case SIGTRAP:
    164 		// OS X sends SIGTRAP when it gets a processor 
    165 		// debug exception, which is caused by single-stepping
    166 		// with the TF bit, among other things.  The processor
    167 		// turns off the TF bit before generating the trap, but
    168 		// it appears that the handler turns it back on for us.
    169 		// Let's use it to confirm that this is a single-step trap.
    170 		if (ctx->ss.eflags & EFLAGS_TF){
    171 			newtrap = VXTRAP_SINGLESTEP;
    172 			ctx->ss.eflags &= ~EFLAGS_TF;
    173 		}else{
    174 			vxprint("Unexpected sigtrap eflags=%#x\n", ctx->ss.eflags);
    175 			newtrap = VXTRAP_SIGNAL + signo;
    176 		}
    177 		break;
    178 
    179 	default:
    180 		newtrap = VXTRAP_SIGNAL + signo;
    181 		break;
    182 	}
    183 	
    184 	if (emu->cpu_trap) {
    185 		// There's already a pending trap!
    186 		// Handle the new trap, and assume that when it
    187 		// finishes, restarting the code at cpu.eip will trigger
    188 		// the old trap again.
    189 		// Have to fix up eip for int 0x30 and syscall instructions.
    190 		if (emu->cpu_trap == VXTRAP_SYSCALL ||
    191 				(emu->cpu_trap&VXTRAP_CATEGORY) == VXTRAP_SOFT)
    192 			emu->cpu.eip -= 2;
    193 	}
    194 	emu->cpu_trap = newtrap;
    195 
    196 	r = vxemu_sighandler(emu, ctx->ss.eip);
    197 	
    198 	if (r == VXSIG_SINGLESTEP){
    199 		// Vxemu_sighandler wants us to single step.
    200 		// Execution state is in intermediate state - don't touch.
    201 		ctx->ss.eflags |= EFLAGS_TF;		// x86 TF (single-step) bit
    202 		vxrun_setup(emu);
    203 		return 1;
    204 	}
    205 
    206 	// Copy execution state into emu.
    207 	if ((r & VXSIG_SAVE_ALL) == VXSIG_SAVE_ALL) {
    208 		emu->cpu.reg[EAX] = ctx->ss.eax;
    209 		emu->cpu.reg[EBX] = ctx->ss.ebx;
    210 		emu->cpu.reg[ECX] = ctx->ss.ecx;
    211 		emu->cpu.reg[EDX] = ctx->ss.edx;
    212 		emu->cpu.reg[ESI] =  ctx->ss.esi;
    213 		emu->cpu.reg[EDI] = ctx->ss.edi;
    214 		emu->cpu.reg[ESP] = ctx->ss.esp;	// or esp_at_signal ???
    215 		emu->cpu.reg[EBP] = ctx->ss.ebp;
    216 		emu->cpu.eflags = ctx->ss.eflags;
    217 	} else if (r & VXSIG_SAVE_ALL) {
    218 		if (r & VXSIG_SAVE_EAX)
    219 			emu->cpu.reg[EAX] = ctx->ss.eax;
    220 		if (r & VXSIG_SAVE_EBX)
    221 			emu->cpu.reg[EBX] = ctx->ss.ebx;
    222 		if (r & VXSIG_SAVE_ECX)
    223 			emu->cpu.reg[ECX] = ctx->ss.ecx;
    224 		if (r & VXSIG_SAVE_EDX)
    225 			emu->cpu.reg[EDX] = ctx->ss.edx;
    226 		if (r & VXSIG_SAVE_ESI)
    227 			emu->cpu.reg[ESI] =  ctx->ss.esi;
    228 		if (r & VXSIG_SAVE_EDI)
    229 			emu->cpu.reg[EDI] = ctx->ss.edi;
    230 		if (r & VXSIG_SAVE_ESP)
    231 			emu->cpu.reg[ESP] = ctx->ss.esp;	// or esp_at_signal ???
    232 		if (r & VXSIG_SAVE_EBP)
    233 			emu->cpu.reg[EBP] = ctx->ss.ebp;
    234 		if (r & VXSIG_SAVE_EFLAGS)
    235 			emu->cpu.eflags = ctx->ss.eflags;
    236 	}
    237 	r &= ~VXSIG_SAVE_ALL;
    238 
    239 	if (r & VXSIG_SAVE_EBX_AS_EIP)
    240 		emu->cpu.eip = ctx->ss.ebx;
    241 	r &= ~VXSIG_SAVE_EBX_AS_EIP;
    242 
    243 	if (r & VXSIG_ADD_COUNT_TO_ESP) {
    244 		emu->cpu.reg[ESP] += (uint16_t)(r >> VXSIG_COUNT_SHIFT);
    245 		r &= ~VXSIG_ADD_COUNT_TO_ESP;
    246 		r &= ~(0xFFFF << VXSIG_COUNT_SHIFT);
    247 	}
    248 	
    249 	if (r &  VXSIG_INC_ECX) {
    250 		emu->cpu.reg[ECX]++;
    251 		r &= ~VXSIG_INC_ECX;
    252 	}
    253 	
    254 	if (r == VXSIG_TRAP) {
    255 		if (emu->trapenv == NULL)
    256 			return 0;
    257 		emu->cpu.traperr = ctx->es.err;
    258 		emu->cpu.trapva = (uint32_t)si->si_addr;
    259 		ctx->ss = *emu->trapenv;
    260 		return 1;
    261 	}
    262 
    263 	// The signal handler is confused; so are we.
    264 	return 0;
    265 }
    266