blob: b7c8241dc38a8b5b0683feb7e3ad8f3ba53aebc4 [file] [log] [blame]
njnf76d27a2009-05-28 01:53:07 +00001
2/*--------------------------------------------------------------------*/
3/*--- Darwin-specific syscalls, etc. syswrap-x86-darwin.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
9
sewardj0f157dd2013-10-18 14:27:36 +000010 Copyright (C) 2005-2013 Apple Inc.
njnf76d27a2009-05-28 01:53:07 +000011 Greg Parker gparker@apple.com
12
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29*/
30
njn8b68b642009-06-24 00:37:09 +000031#if defined(VGP_x86_darwin)
32
njnf76d27a2009-05-28 01:53:07 +000033#include "pub_core_basics.h"
34#include "pub_core_vki.h"
sewardj38a28592011-04-11 22:08:06 +000035#include "pub_core_libcsetjmp.h" // to keep _threadstate.h happy
njnf76d27a2009-05-28 01:53:07 +000036#include "pub_core_threadstate.h"
37#include "pub_core_aspacemgr.h"
38#include "pub_core_xarray.h"
39#include "pub_core_clientstate.h"
40#include "pub_core_debuglog.h"
41#include "pub_core_debuginfo.h" // VG_(di_notify_*)
42#include "pub_core_transtab.h" // VG_(discard_translations)
43#include "pub_core_libcbase.h"
44#include "pub_core_libcassert.h"
45#include "pub_core_libcfile.h"
46#include "pub_core_libcprint.h"
47#include "pub_core_libcproc.h"
48#include "pub_core_libcsignal.h"
49#include "pub_core_mallocfree.h"
50#include "pub_core_options.h"
51#include "pub_core_scheduler.h"
52#include "pub_core_signals.h"
53#include "pub_core_syscall.h"
54#include "pub_core_syswrap.h"
55#include "pub_core_tooliface.h"
56
57#include "priv_types_n_macros.h"
58#include "priv_syswrap-generic.h" /* for decls of generic wrappers */
59#include "priv_syswrap-darwin.h" /* for decls of darwin-ish wrappers */
60#include "priv_syswrap-main.h"
61
62
63#include <mach/mach.h>
64
65static void x86_thread_state32_from_vex(i386_thread_state_t *mach,
66 VexGuestX86State *vex)
67{
68 mach->__eax = vex->guest_EAX;
69 mach->__ebx = vex->guest_EBX;
70 mach->__ecx = vex->guest_ECX;
71 mach->__edx = vex->guest_EDX;
72 mach->__edi = vex->guest_EDI;
73 mach->__esi = vex->guest_ESI;
74 mach->__ebp = vex->guest_EBP;
75 mach->__esp = vex->guest_ESP;
76 mach->__ss = vex->guest_SS;
77 mach->__eflags = LibVEX_GuestX86_get_eflags(vex);
78 mach->__eip = vex->guest_EIP;
79 mach->__cs = vex->guest_CS;
80 mach->__ds = vex->guest_DS;
81 mach->__es = vex->guest_ES;
82 mach->__fs = vex->guest_FS;
83 mach->__gs = vex->guest_GS;
84}
85
86
87static void x86_float_state32_from_vex(i386_float_state_t *mach,
88 VexGuestX86State *vex)
89{
90 // DDD: #warning GrP fixme fp state
91
92 VG_(memcpy)(&mach->__fpu_xmm0, &vex->guest_XMM0, 8 * sizeof(mach->__fpu_xmm0));
93}
94
95
96void thread_state_from_vex(thread_state_t mach_generic,
97 thread_state_flavor_t flavor,
98 mach_msg_type_number_t count,
99 VexGuestArchState *vex_generic)
100{
101 VexGuestX86State *vex = (VexGuestX86State *)vex_generic;
102
103 switch (flavor) {
104 case i386_THREAD_STATE:
105 vg_assert(count == i386_THREAD_STATE_COUNT);
106 x86_thread_state32_from_vex((i386_thread_state_t *)mach_generic, vex);
107 break;
108
109 case i386_FLOAT_STATE:
110 vg_assert(count == i386_FLOAT_STATE_COUNT);
111 x86_float_state32_from_vex((i386_float_state_t *)mach_generic, vex);
112 break;
113
114 default:
115 vg_assert(0);
116 }
117}
118
119
120static void x86_thread_state32_to_vex(const i386_thread_state_t *mach,
121 VexGuestX86State *vex)
122{
123 LibVEX_GuestX86_initialise(vex);
124 vex->guest_EAX = mach->__eax;
125 vex->guest_EBX = mach->__ebx;
126 vex->guest_ECX = mach->__ecx;
127 vex->guest_EDX = mach->__edx;
128 vex->guest_EDI = mach->__edi;
129 vex->guest_ESI = mach->__esi;
130 vex->guest_EBP = mach->__ebp;
131 vex->guest_ESP = mach->__esp;
132 vex->guest_SS = mach->__ss;
133 // DDD: #warning GrP fixme eflags
134 vex->guest_EIP = mach->__eip;
135 vex->guest_CS = mach->__cs;
136 vex->guest_DS = mach->__ds;
137 vex->guest_ES = mach->__es;
138 vex->guest_FS = mach->__fs;
139 vex->guest_GS = mach->__gs;
140}
141
142static void x86_float_state32_to_vex(const i386_float_state_t *mach,
143 VexGuestX86State *vex)
144{
145 // DDD: #warning GrP fixme fp state
146
147 VG_(memcpy)(&vex->guest_XMM0, &mach->__fpu_xmm0, 8 * sizeof(mach->__fpu_xmm0));
148}
149
150
151void thread_state_to_vex(const thread_state_t mach_generic,
152 thread_state_flavor_t flavor,
153 mach_msg_type_number_t count,
154 VexGuestArchState *vex_generic)
155{
156 VexGuestX86State *vex = (VexGuestX86State *)vex_generic;
157
158 switch(flavor) {
159 case i386_THREAD_STATE:
160 vg_assert(count == i386_THREAD_STATE_COUNT);
161 x86_thread_state32_to_vex((const i386_thread_state_t*)mach_generic,vex);
162 break;
163 case i386_FLOAT_STATE:
164 vg_assert(count == i386_FLOAT_STATE_COUNT);
165 x86_float_state32_to_vex((const i386_float_state_t*)mach_generic,vex);
166 break;
167
168 default:
169 vg_assert(0);
170 break;
171 }
172}
173
174
175ThreadState *build_thread(const thread_state_t state,
176 thread_state_flavor_t flavor,
177 mach_msg_type_number_t count)
178{
179 ThreadId tid = VG_(alloc_ThreadState)();
180 ThreadState *tst = VG_(get_ThreadState)(tid);
181
182 vg_assert(flavor == i386_THREAD_STATE);
183 vg_assert(count == i386_THREAD_STATE_COUNT);
184
185 // Initialize machine registers
186
187 thread_state_to_vex(state, flavor, count, &tst->arch.vex);
188
189 I_die_here;
190 // GrP fixme signals, sig_mask, tmp_sig_mask, os_state.parent
191
192 find_stack_segment(tid, tst->arch.vex.guest_ESP);
193
194 return tst;
195}
196
197
198// Edit the thread state to send to the real kernel.
199// The real thread will run start_thread_NORETURN(tst)
200// on a separate non-client stack.
201void hijack_thread_state(thread_state_t mach_generic,
202 thread_state_flavor_t flavor,
203 mach_msg_type_number_t count,
204 ThreadState *tst)
205{
206 i386_thread_state_t *mach = (i386_thread_state_t *)mach_generic;
207 char *stack;
208
209 vg_assert(flavor == i386_THREAD_STATE);
210 vg_assert(count == i386_THREAD_STATE_COUNT);
211
212 stack = (char *)allocstack(tst->tid);
213 stack -= 64+320; // make room for top frame
214 memset(stack, 0, 64+320); // ...and clear it
215 *(uintptr_t *)stack = (uintptr_t)tst; // set parameter
216 stack -= sizeof(uintptr_t);
217 *(uintptr_t *)stack = 0; // push fake return address
218
219 mach->__eip = (uintptr_t)&start_thread_NORETURN;
220 mach->__esp = (uintptr_t)stack;
221}
222
223
224/* Call f(arg1), but first switch stacks, using 'stack' as the new
225 stack, and use 'retaddr' as f's return-to address. Also, clear all
226 the integer registers before entering f.*/
227__attribute__((noreturn))
228void call_on_new_stack_0_1 ( Addr stack,
229 Addr retaddr,
230 void (*f)(Word),
231 Word arg1 );
232// 4(%esp) == stack (must be 16-byte aligned)
233// 8(%esp) == retaddr
234// 12(%esp) == f
235// 16(%esp) == arg1
236asm(
237".globl _call_on_new_stack_0_1\n"
238"_call_on_new_stack_0_1:\n"
239" movl %esp, %esi\n" // remember old stack pointer
240" movl 4(%esi), %esp\n" // set new stack
241" pushl $0\n" // align stack
242" pushl $0\n" // align stack
243" pushl $0\n" // align stack
244" pushl 16(%esi)\n" // arg1 to stack
245" pushl 8(%esi)\n" // retaddr to stack
246" pushl 12(%esi)\n" // f to stack
247" movl $0, %eax\n" // zero all GP regs
248" movl $0, %ebx\n"
249" movl $0, %ecx\n"
250" movl $0, %edx\n"
251" movl $0, %esi\n"
252" movl $0, %edi\n"
253" movl $0, %ebp\n"
254" ret\n" // jump to f
255" ud2\n" // should never get here
256);
257
258
259asm(
260".globl _pthread_hijack_asm\n"
261"_pthread_hijack_asm:\n"
262" movl %esp,%ebp\n"
263" push $0\n" // alignment pad
264" push %ebp\n" // original sp
265" push %esi\n" // flags
266" push %edi\n" // stacksize
267" push %edx\n" // func_arg
268" push %ecx\n" // func
269" push %ebx\n" // kport
270" push %eax\n" // self
271" push $0\n" // fake return address
272" jmp _pthread_hijack\n"
273 );
274
275
276
277void pthread_hijack(Addr self, Addr kport, Addr func, Addr func_arg,
278 Addr stacksize, Addr flags, Addr sp)
279{
280 vki_sigset_t blockall;
281 ThreadState *tst = (ThreadState *)func_arg;
282 VexGuestX86State *vex = &tst->arch.vex;
283
284 // VG_(printf)("pthread_hijack pthread %p, machthread %p, func %p, arg %p, stack %p, flags %p, stack %p\n", self, kport, func, func_arg, stacksize, flags, sp);
285
286 // Wait for parent thread's permission.
287 // The parent thread holds V's lock on our behalf.
288 semaphore_wait(tst->os_state.child_go);
289
290 /* Start the thread with all signals blocked. VG_(scheduler) will
291 set the mask correctly when we finally get there. */
292 VG_(sigfillset)(&blockall);
293 VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, NULL);
294
295 // Set thread's registers
296 // Do this FIRST because some code below tries to collect a backtrace,
297 // which requires valid register data.
298 // DDD: need to do post_reg_write events here?
299 LibVEX_GuestX86_initialise(vex);
300 vex->guest_EIP = pthread_starter;
301 vex->guest_EAX = self;
302 vex->guest_EBX = kport;
303 vex->guest_ECX = func;
304 vex->guest_EDX = tst->os_state.func_arg;
305 vex->guest_EDI = stacksize;
306 vex->guest_ESI = flags;
307 vex->guest_ESP = sp;
308
309 // Record thread's stack and Mach port and pthread struct
310 tst->os_state.pthread = self;
311 tst->os_state.lwpid = kport;
312 record_named_port(tst->tid, kport, MACH_PORT_RIGHT_SEND, "thread-%p");
313
314 if ((flags & 0x01000000) == 0) {
315 // kernel allocated stack - needs mapping
316 Addr stack = VG_PGROUNDUP(sp) - stacksize;
317 tst->client_stack_highest_word = stack+stacksize;
318 tst->client_stack_szB = stacksize;
319
320 // pthread structure
321 ML_(notify_core_and_tool_of_mmap)(
322 stack+stacksize, pthread_structsize,
323 VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0);
324 // stack contents
325 ML_(notify_core_and_tool_of_mmap)(
326 stack, stacksize,
327 VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0);
328 // guard page
329 ML_(notify_core_and_tool_of_mmap)(
330 stack-VKI_PAGE_SIZE, VKI_PAGE_SIZE,
331 0, VKI_MAP_PRIVATE, -1, 0);
332 } else {
333 // client allocated stack
334 find_stack_segment(tst->tid, sp);
335 }
sewardj3847cd32009-08-07 20:28:58 +0000336 ML_(sync_mappings)("after", "pthread_hijack", 0);
njnf76d27a2009-05-28 01:53:07 +0000337
338 // DDD: should this be here rather than in POST(sys_bsdthread_create)?
339 // But we don't have ptid here...
340 //VG_TRACK ( pre_thread_ll_create, ptid, tst->tid );
341
342 // Tell parent thread's POST(sys_bsdthread_create) that we're done
343 // initializing registers and mapping memory.
344 semaphore_signal(tst->os_state.child_done);
345 // LOCK IS GONE BELOW THIS POINT
346
347 // Go!
348 call_on_new_stack_0_1(tst->os_state.valgrind_stack_init_SP, 0,
349 start_thread_NORETURN, (Word)tst);
350
351 /*NOTREACHED*/
352 vg_assert(0);
353}
354
355
356
357asm(
358".globl _wqthread_hijack_asm\n"
359"_wqthread_hijack_asm:\n"
360" movl %esp,%ebp\n"
361" push $0\n" // alignment
362" push $0\n" // alignment
363" push %ebp\n" // original sp
364" push %edi\n" // reuse
365" push %edx\n" // workitem
366" push %ecx\n" // stackaddr
367" push %ebx\n" // kport
368" push %eax\n" // self
369" push $0\n" // fake return address
370" jmp _wqthread_hijack\n"
371 );
372
373
374/* wqthread note: The kernel may create or destroy pthreads in the
375 wqthread pool at any time with no userspace interaction,
376 and wqthread_start may be entered at any time with no userspace
377 interaction.
378 To handle this in valgrind, we create and destroy a valgrind
379 thread for every work item.
380*/
381void wqthread_hijack(Addr self, Addr kport, Addr stackaddr, Addr workitem,
382 Int reuse, Addr sp)
383{
384 ThreadState *tst;
385 VexGuestX86State *vex;
386 Addr stack;
387 SizeT stacksize;
388 vki_sigset_t blockall;
389
390 /* When we enter here we hold no lock (!), so we better acquire it
391 pronto. Why do we hold no lock? Because (presumably) the only
392 way to get here is as a result of a SfMayBlock syscall
393 "workq_ops(WQOPS_THREAD_RETURN)", which will have dropped the
394 lock. At least that's clear for the 'reuse' case. The
395 non-reuse case? Dunno, perhaps it's a new thread the kernel
396 pulled out of a hat. In any case we still need to take a
397 lock. */
398 VG_(acquire_BigLock_LL)("wqthread_hijack");
399
400 /* Start the thread with all signals blocked. VG_(scheduler) will
401 set the mask correctly when we finally get there. */
402 VG_(sigfillset)(&blockall);
403 VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, NULL);
404
405 if (reuse) {
sewardjb5784062011-09-30 07:05:17 +0000406
407 /* For whatever reason, tst->os_state.pthread appear to have a
408 constant offset of 72 on 10.7, but zero on 10.6 and 10.5. No
409 idea why. */
410# if DARWIN_VERS <= DARWIN_10_6
411 UWord magic_delta = 0;
sewardjae284e52012-08-02 18:25:04 +0000412# elif DARWIN_VERS >= DARWIN_10_7
sewardjb5784062011-09-30 07:05:17 +0000413 UWord magic_delta = 0x48;
414# endif
415
njnf76d27a2009-05-28 01:53:07 +0000416 // This thread already exists; we're merely re-entering
417 // after leaving via workq_ops(WQOPS_THREAD_RETURN).
418 // Don't allocate any V thread resources.
419 // Do reset thread registers.
420 ThreadId tid = VG_(lwpid_to_vgtid)(kport);
421 vg_assert(VG_(is_valid_tid)(tid));
422 vg_assert(mach_thread_self() == kport);
423
424 tst = VG_(get_ThreadState)(tid);
sewardjb5784062011-09-30 07:05:17 +0000425
426 if (0) VG_(printf)("wqthread_hijack reuse %s: tid %d, tst %p, "
427 "tst->os_state.pthread %#lx, self %#lx\n",
428 tst->os_state.pthread == self ? "SAME" : "DIFF",
429 tid, tst, tst->os_state.pthread, self);
430
njnf76d27a2009-05-28 01:53:07 +0000431 vex = &tst->arch.vex;
sewardjb5784062011-09-30 07:05:17 +0000432 vg_assert(tst->os_state.pthread - magic_delta == self);
njnf76d27a2009-05-28 01:53:07 +0000433 }
434 else {
435 // This is a new thread.
436 tst = VG_(get_ThreadState)(VG_(alloc_ThreadState)());
437 vex = &tst->arch.vex;
438 allocstack(tst->tid);
439 LibVEX_GuestX86_initialise(vex);
440 }
441
442 // Set thread's registers
443 // Do this FIRST because some code below tries to collect a backtrace,
444 // which requires valid register data.
445 vex->guest_EIP = wqthread_starter;
446 vex->guest_EAX = self;
447 vex->guest_EBX = kport;
448 vex->guest_ECX = stackaddr;
449 vex->guest_EDX = workitem;
450 vex->guest_EDI = reuse;
451 vex->guest_ESI = 0;
452 vex->guest_ESP = sp;
453
454 stacksize = 512*1024; // wq stacks are always DEFAULT_STACK_SIZE
455 stack = VG_PGROUNDUP(sp) - stacksize;
456
457 if (reuse) {
458 // Continue V's thread back in the scheduler.
459 // The client thread is of course in another location entirely.
460
461 /* Drop the lock before going into
462 ML_(wqthread_continue_NORETURN). The latter will immediately
463 attempt to reacquire it in non-LL mode, which is a bit
464 wasteful but I don't think is harmful. A better solution
465 would be to not drop the lock but instead "upgrade" it from a
466 LL lock to a full lock, but that's too much like hard work
467 right now. */
468 VG_(release_BigLock_LL)("wqthread_hijack(1)");
469 ML_(wqthread_continue_NORETURN)(tst->tid);
470 }
471 else {
472 // Record thread's stack and Mach port and pthread struct
473 tst->os_state.pthread = self;
474 tst->os_state.lwpid = kport;
475 record_named_port(tst->tid, kport, MACH_PORT_RIGHT_SEND, "wqthread-%p");
476
477 // kernel allocated stack - needs mapping
478 tst->client_stack_highest_word = stack+stacksize;
479 tst->client_stack_szB = stacksize;
480
481 // GrP fixme scheduler lock?!
482
483 // pthread structure
484 ML_(notify_core_and_tool_of_mmap)(
485 stack+stacksize, pthread_structsize,
486 VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0);
487 // stack contents
488 // GrP fixme uninitialized!
489 ML_(notify_core_and_tool_of_mmap)(
490 stack, stacksize,
491 VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0);
492 // guard page
493 // GrP fixme ban_mem_stack!
494 ML_(notify_core_and_tool_of_mmap)(
495 stack-VKI_PAGE_SIZE, VKI_PAGE_SIZE,
496 0, VKI_MAP_PRIVATE, -1, 0);
497
sewardj3847cd32009-08-07 20:28:58 +0000498 ML_(sync_mappings)("after", "wqthread_hijack", 0);
njnf76d27a2009-05-28 01:53:07 +0000499
500 // Go!
501 /* Same comments as the 'release' in the then-clause.
502 start_thread_NORETURN calls run_thread_NORETURN calls
503 thread_wrapper which acquires the lock before continuing.
504 Let's hope nothing non-thread-local happens until that point.
505
506 DDD: I think this is plain wrong .. if we get to
507 thread_wrapper not holding the lock, and someone has recycled
508 this thread slot in the meantime, we're hosed. Is that
509 possible, though? */
510 VG_(release_BigLock_LL)("wqthread_hijack(2)");
511 call_on_new_stack_0_1(tst->os_state.valgrind_stack_init_SP, 0,
512 start_thread_NORETURN, (Word)tst);
513 }
514
515 /*NOTREACHED*/
516 vg_assert(0);
517}
518
njn8b68b642009-06-24 00:37:09 +0000519#endif // defined(VGP_x86_darwin)
520
njnf76d27a2009-05-28 01:53:07 +0000521/*--------------------------------------------------------------------*/
njn8b68b642009-06-24 00:37:09 +0000522/*--- end ---*/
njnf76d27a2009-05-28 01:53:07 +0000523/*--------------------------------------------------------------------*/