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