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