darwin.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) 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