blob: 0347db4544194ef93409cd2e5d1d7718e4e6a14c [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- Darwin-specific syscalls, etc. syswrap-amd64-darwin.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2005-2009 Apple Inc.
Greg Parker gparker@apple.com
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#if defined(VGP_amd64_darwin)
#include "pub_core_basics.h"
#include "pub_core_vki.h"
#include "pub_core_threadstate.h"
#include "pub_core_aspacemgr.h"
#include "pub_core_xarray.h"
#include "pub_core_clientstate.h"
#include "pub_core_debuglog.h"
#include "pub_core_debuginfo.h" // VG_(di_notify_*)
#include "pub_core_transtab.h" // VG_(discard_translations)
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcfile.h"
#include "pub_core_libcprint.h"
#include "pub_core_libcproc.h"
#include "pub_core_libcsignal.h"
#include "pub_core_mallocfree.h"
#include "pub_core_options.h"
#include "pub_core_scheduler.h"
#include "pub_core_sigframe.h" // For VG_(sigframe_destroy)()
#include "pub_core_signals.h"
#include "pub_core_syscall.h"
#include "pub_core_syswrap.h"
#include "pub_core_tooliface.h"
#include "priv_types_n_macros.h"
#include "priv_syswrap-generic.h" /* for decls of generic wrappers */
#include "priv_syswrap-darwin.h" /* for decls of darwin-ish wrappers */
#include "priv_syswrap-main.h"
#include <mach/mach.h>
static void x86_thread_state64_from_vex(x86_thread_state64_t *mach,
VexGuestAMD64State *vex)
{
mach->__rax = vex->guest_RAX;
mach->__rbx = vex->guest_RBX;
mach->__rcx = vex->guest_RCX;
mach->__rdx = vex->guest_RDX;
mach->__rdi = vex->guest_RDI;
mach->__rsi = vex->guest_RSI;
mach->__rbp = vex->guest_RBP;
mach->__rsp = vex->guest_RSP;
mach->__rflags = LibVEX_GuestAMD64_get_rflags(vex);
mach->__rip = vex->guest_RIP;
mach->__r8 = vex->guest_R8;
mach->__r9 = vex->guest_R9;
mach->__r10 = vex->guest_R10;
mach->__r11 = vex->guest_R11;
mach->__r12 = vex->guest_R12;
mach->__r13 = vex->guest_R13;
mach->__r14 = vex->guest_R14;
mach->__r15 = vex->guest_R15;
/* GrP fixme
mach->__cs = vex->guest_CS;
mach->__fs = vex->guest_FS;
mach->__gs = vex->guest_GS;
*/
}
static void x86_float_state64_from_vex(x86_float_state64_t *mach,
VexGuestAMD64State *vex)
{
// DDD: #warning GrP fixme fp state
VG_(memcpy)(&mach->__fpu_xmm0, &vex->guest_XMM0, 16 * sizeof(mach->__fpu_xmm0));
}
void thread_state_from_vex(thread_state_t mach_generic,
thread_state_flavor_t flavor,
mach_msg_type_number_t count,
VexGuestArchState *vex_generic)
{
VexGuestAMD64State *vex = (VexGuestAMD64State *)vex_generic;
switch (flavor) {
case x86_THREAD_STATE64:
vg_assert(count == x86_THREAD_STATE64_COUNT);
x86_thread_state64_from_vex((x86_thread_state64_t *)mach_generic, vex);
break;
case x86_FLOAT_STATE64:
vg_assert(count == x86_FLOAT_STATE64_COUNT);
x86_float_state64_from_vex((x86_float_state64_t *)mach_generic, vex);
break;
default:
vg_assert(0);
}
}
static void x86_thread_state64_to_vex(const x86_thread_state64_t *mach,
VexGuestAMD64State *vex)
{
LibVEX_GuestAMD64_initialise(vex);
vex->guest_RAX = mach->__rax;
vex->guest_RBX = mach->__rbx;
vex->guest_RCX = mach->__rcx;
vex->guest_RDX = mach->__rdx;
vex->guest_RDI = mach->__rdi;
vex->guest_RSI = mach->__rsi;
vex->guest_RBP = mach->__rbp;
vex->guest_RSP = mach->__rsp;
// DDD: #warning GrP fixme eflags
vex->guest_RIP = mach->__rip;
vex->guest_R8 = mach->__r8;
vex->guest_R9 = mach->__r9;
vex->guest_R10 = mach->__r10;
vex->guest_R11 = mach->__r11;
vex->guest_R12 = mach->__r12;
vex->guest_R13 = mach->__r13;
vex->guest_R14 = mach->__r14;
vex->guest_R15 = mach->__r15;
/* GrP fixme
vex->guest_CS = mach->__cs;
vex->guest_FS = mach->__fs;
vex->guest_GS = mach->__gs;
*/
}
static void x86_float_state64_to_vex(const x86_float_state64_t *mach,
VexGuestAMD64State *vex)
{
// DDD: #warning GrP fixme fp state
VG_(memcpy)(&vex->guest_XMM0, &mach->__fpu_xmm0, 16 * sizeof(mach->__fpu_xmm0));
}
void thread_state_to_vex(const thread_state_t mach_generic,
thread_state_flavor_t flavor,
mach_msg_type_number_t count,
VexGuestArchState *vex_generic)
{
VexGuestAMD64State *vex = (VexGuestAMD64State *)vex_generic;
switch(flavor) {
case x86_THREAD_STATE64:
vg_assert(count == x86_THREAD_STATE64_COUNT);
x86_thread_state64_to_vex((const x86_thread_state64_t*)mach_generic,vex);
break;
case x86_FLOAT_STATE64:
vg_assert(count == x86_FLOAT_STATE64_COUNT);
x86_float_state64_to_vex((const x86_float_state64_t*)mach_generic,vex);
break;
default:
vg_assert(0);
break;
}
}
ThreadState *build_thread(const thread_state_t state,
thread_state_flavor_t flavor,
mach_msg_type_number_t count)
{
ThreadId tid = VG_(alloc_ThreadState)();
ThreadState *tst = VG_(get_ThreadState)(tid);
vg_assert(flavor == x86_THREAD_STATE64);
vg_assert(count == x86_THREAD_STATE64_COUNT);
// Initialize machine registers
thread_state_to_vex(state, flavor, count, &tst->arch.vex);
I_die_here;
// GrP fixme signals, sig_mask, tmp_sig_mask, os_state.parent
find_stack_segment(tid, tst->arch.vex.guest_RSP);
return tst;
}
// Edit the thread state to send to the real kernel.
// The real thread will run start_thread_NORETURN(tst)
// on a separate non-client stack.
void hijack_thread_state(thread_state_t mach_generic,
thread_state_flavor_t flavor,
mach_msg_type_number_t count,
ThreadState *tst)
{
x86_thread_state64_t *mach = (x86_thread_state64_t *)mach_generic;
char *stack;
vg_assert(flavor == x86_THREAD_STATE64);
vg_assert(count == x86_THREAD_STATE64_COUNT);
stack = (char *)allocstack(tst->tid);
stack -= 64+320; // make room for top frame
memset(stack, 0, 64+320); // ...and clear it
*(uintptr_t *)stack = 0; // push fake return address
mach->__rdi = (uintptr_t)tst; // arg1 = tst
mach->__rip = (uintptr_t)&start_thread_NORETURN;
mach->__rsp = (uintptr_t)stack;
}
/* Call f(arg1), but first switch stacks, using 'stack' as the new
stack, and use 'retaddr' as f's return-to address. Also, clear all
the integer registers before entering f.*/
__attribute__((noreturn))
void call_on_new_stack_0_1 ( Addr stack,
Addr retaddr,
void (*f)(Word),
Word arg1 );
// %rdi == stack (must be 16-byte aligned)
// %rsi == retaddr
// %rdx == f
// %rcx == arg1
asm(
".globl _call_on_new_stack_0_1\n"
"_call_on_new_stack_0_1:\n"
" movq %rsp, %rbp\n" // remember old stack pointer
" movq %rdi, %rsp\n" // set new stack
" movq %rcx, %rdi\n" // set arg1
" pushq %rsi\n" // retaddr to new stack
" pushq %rdx\n" // f to new stack
" movq $0, %rax\n" // zero all other GP regs
" movq $0, %rbx\n"
" movq $0, %rcx\n"
" movq $0, %rdx\n"
" movq $0, %rsi\n"
" movq $0, %rbp\n"
" movq $0, %r8\n"
" movq $0, %r9\n"
" movq $0, %r10\n"
" movq $0, %r11\n"
" movq $0, %r12\n"
" movq $0, %r13\n"
" movq $0, %r14\n"
" movq $0, %r15\n"
" ret\n" // jump to f
" ud2\n" // should never get here
);
asm(
".globl _pthread_hijack_asm\n"
"_pthread_hijack_asm:\n"
" movq %rsp,%rbp\n"
" push $0\n" // alignment pad
" push %rbp\n" // original sp
// other values stay where they are in registers
" push $0\n" // fake return address
" jmp _pthread_hijack\n"
);
void pthread_hijack(Addr self, Addr kport, Addr func, Addr func_arg,
Addr stacksize, Addr flags, Addr sp)
{
ThreadState *tst = (ThreadState *)func_arg;
VexGuestAMD64State *vex = &tst->arch.vex;
// 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);
// Wait for parent thread's permission.
// The parent thread holds V's lock on our behalf.
semaphore_wait(tst->os_state.child_go);
// Set thread's registers
// Do this FIRST because some code below tries to collect a backtrace,
// which requires valid register data.
LibVEX_GuestAMD64_initialise(vex);
vex->guest_RIP = pthread_starter;
vex->guest_RDI = self;
vex->guest_RSI = kport;
vex->guest_RDX = func;
vex->guest_RCX = tst->os_state.func_arg;
vex->guest_R8 = stacksize;
vex->guest_R9 = flags;
vex->guest_RSP = sp;
// Record thread's stack and Mach port and pthread struct
tst->os_state.pthread = self;
tst->os_state.lwpid = kport;
record_named_port(tst->tid, kport, MACH_PORT_RIGHT_SEND, "thread-%p");
if ((flags & 0x01000000) == 0) {
// kernel allocated stack - needs mapping
Addr stack = VG_PGROUNDUP(sp) - stacksize;
tst->client_stack_highest_word = stack+stacksize;
tst->client_stack_szB = stacksize;
// pthread structure
ML_(notify_core_and_tool_of_mmap)(
stack+stacksize, pthread_structsize,
VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0);
// stack contents
ML_(notify_core_and_tool_of_mmap)(
stack, stacksize,
VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0);
// guard page
ML_(notify_core_and_tool_of_mmap)(
stack-VKI_PAGE_SIZE, VKI_PAGE_SIZE, 0, VKI_MAP_PRIVATE, -1, 0);
} else {
// client allocated stack
find_stack_segment(tst->tid, sp);
}
VG_(am_do_sync_check)("after", "pthread_hijack", 0);
// Tell parent thread's POST(sys_bsdthread_create) that we're done
// initializing registers and mapping memory.
semaphore_signal(tst->os_state.child_done);
// LOCK IS GONE BELOW THIS POINT
// Go!
call_on_new_stack_0_1(tst->os_state.valgrind_stack_init_SP, 0,
start_thread_NORETURN, (Word)tst);
/*NOTREACHED*/
vg_assert(0);
}
asm(
".globl _wqthread_hijack_asm\n"
"_wqthread_hijack_asm:\n"
" movq %rsp,%r9\n" // original sp
// other values stay where they are in registers
" push $0\n" // fake return address
" jmp _wqthread_hijack\n"
);
/*
wqthread note: The kernel may create or destroy pthreads in the
wqthread pool at any time with no userspace interaction,
and wqthread_start may be entered at any time with no userspace
interaction.
To handle this in valgrind, we create and destroy a valgrind
thread for every work item.
*/
void wqthread_hijack(Addr self, Addr kport, Addr stackaddr, Addr workitem,
Int reuse, Addr sp)
{
ThreadState *tst;
VexGuestAMD64State *vex;
Addr stack;
SizeT stacksize;
if (reuse) {
// This thread already exists; we're merely re-entering
// after leaving via workq_ops(WQOPS_THREAD_RETURN).
// Don't allocate any V thread resources.
// Do reset thread registers.
ThreadId tid = VG_(lwpid_to_vgtid)(kport);
vg_assert(VG_(is_valid_tid)(tid));
vg_assert(mach_thread_self() == kport);
tst = VG_(get_ThreadState)(tid);
vex = &tst->arch.vex;
vg_assert(tst->os_state.pthread == self);
}
else {
// This is a new thread.
tst = VG_(get_ThreadState)(VG_(alloc_ThreadState)());
vex = &tst->arch.vex;
allocstack(tst->tid);
LibVEX_GuestAMD64_initialise(vex);
}
// Set thread's registers
// Do this FIRST because some code below tries to collect a backtrace,
// which requires valid register data.
vex->guest_RIP = wqthread_starter;
vex->guest_RDI = self;
vex->guest_RSI = kport;
vex->guest_RDX = stackaddr;
vex->guest_RCX = workitem;
vex->guest_R8 = reuse;
vex->guest_R9 = 0;
vex->guest_RSP = sp;
stacksize = 512*1024; // wq stacks are always DEFAULT_STACK_SIZE
stack = VG_PGROUNDUP(sp) - stacksize;
if (reuse) {
// Continue V's thread back in the scheduler.
// The client thread is of course in another location entirely.
ML_(wqthread_continue_NORETURN)(tst->tid);
}
else {
// Record thread's stack and Mach port and pthread struct
tst->os_state.pthread = self;
tst->os_state.lwpid = kport;
record_named_port(tst->tid, kport, MACH_PORT_RIGHT_SEND, "wqthread-%p");
// kernel allocated stack - needs mapping
tst->client_stack_highest_word = stack+stacksize;
tst->client_stack_szB = stacksize;
// GrP fixme scheduler lock?!
// pthread structure
ML_(notify_core_and_tool_of_mmap)(
stack+stacksize, pthread_structsize,
VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0);
// stack contents
// GrP fixme uninitialized!
ML_(notify_core_and_tool_of_mmap)(
stack, stacksize,
VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0);
// guard page
// GrP fixme ban_mem_stack!
ML_(notify_core_and_tool_of_mmap)(
stack-VKI_PAGE_SIZE, VKI_PAGE_SIZE, 0, VKI_MAP_PRIVATE, -1, 0);
VG_(am_do_sync_check)("after", "wqthread_hijack", 0);
// Go!
call_on_new_stack_0_1(tst->os_state.valgrind_stack_init_SP, 0,
start_thread_NORETURN, (Word)tst);
}
/*NOTREACHED*/
vg_assert(0);
}
#endif // defined(VGP_amd64_darwin)
/*--------------------------------------------------------------------*/
/*--- end ---*/
/*--------------------------------------------------------------------*/