Skip to content

Commit

Permalink
i#58 MacOS: signaling a specific thread
Browse files Browse the repository at this point in the history
Adds the ability to send a signal to a thread that we know about, by
storing its Mach thread port in the dcontext.  We obtain the port via
MACH_thread_self_trap, which requires sysenter to invoke.  We add
dynamorio_mach_syscall for this purpose.

We still don't have a way to send a signal to an unknown thread.

Still TODO: handle_suspend_signal for Mac.

SVN-Revision: 2618
  • Loading branch information
derekbruening committed Mar 30, 2014
1 parent 677730e commit 4e21420
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 11 deletions.
3 changes: 3 additions & 0 deletions core/globals.h
Expand Up @@ -777,6 +777,9 @@ struct _dcontext_t {
thread_id_t owning_thread;
#ifdef UNIX
process_id_t owning_process; /* handle shared address space w/o shared pid */
#endif
#ifdef MACOS
uint thread_port; /* mach_port_t */
#endif
thread_record_t *thread_record; /* so don't have to do a thread_lookup */
where_am_i_t whereami; /* where control is at the moment */
Expand Down
94 changes: 94 additions & 0 deletions core/unix/include/syscall_mach.h
@@ -0,0 +1,94 @@
/* *******************************************************************************
* Copyright (c) 2014 Google, Inc. All rights reserved.
* *******************************************************************************/

/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/

/*
* MacOS Mach system calls
*/

#ifndef _SYSCALL_MACH_H_
#define _SYSCALL_MACH_H_ 1

#define SYSCALL_NUM_MARKER_MACH 0x1000000
#define SYSCALL_NUM_MARKER_MACHDEP 0x3000000

#define MACH__kernelrpc_mach_vm_allocate_trap 10
#define MACH__kernelrpc_mach_vm_deallocate_trap 12
#define MACH__kernelrpc_mach_vm_protect_trap 14
#define MACH__kernelrpc_mach_vm_map_trap 15
#define MACH__kernelrpc_mach_port_allocate_trap 16
#define MACH__kernelrpc_mach_port_destroy_trap 17
#define MACH__kernelrpc_mach_port_deallocate_trap 18
#define MACH__kernelrpc_mach_port_mod_refs_trap 19
#define MACH__kernelrpc_mach_port_move_member_trap 20
#define MACH__kernelrpc_mach_port_insert_right_trap 21
#define MACH__kernelrpc_mach_port_insert_member_trap 22
#define MACH__kernelrpc_mach_port_extract_member_trap 23
#define MACH__kernelrpc_mach_port_construct_trap 24
#define MACH__kernelrpc_mach_port_destruct_trap 25
#define MACH_mach_reply_port 26
#define MACH_thread_self_trap 27
#define MACH_task_self_trap 28
#define MACH_host_self_trap 29
#define MACH_mach_msg_trap 31
#define MACH_mach_msg_overwrite_trap 32
#define MACH_semaphore_signal_trap 33
#define MACH_semaphore_signal_all_trap 34
#define MACH_semaphore_signal_thread_trap 35
#define MACH_semaphore_wait_trap 36
#define MACH_semaphore_wait_signal_trap 37
#define MACH_semaphore_timedwait_trap 38
#define MACH_semaphore_timedwait_signal_trap 39
#define MACH__kernelrpc_mach_port_guard_trap 41
#define MACH__kernelrpc_mach_port_unguard_trap 42
#define MACH_map_fd 43
#define MACH_task_name_for_pid 44
#define MACH_task_for_pid 45
#define MACH_pid_for_task 46
#define MACH_macx_swapon 48
#define MACH_macx_swapoff 49
#define MACH_macx_triggers 51
#define MACH_macx_backing_store_suspend 52
#define MACH_macx_backing_store_recovery 53
#define MACH_pfz_exit 58
#define MACH_swtch_pri 59
#define MACH_swtch 60
#define MACH_thread_switch 61
#define MACH_clock_sleep_trap 62
#define MACH_mach_timebase_info_trap 89
#define MACH_mach_wait_until_trap 90
#define MACH_mk_timer_create_trap 91
#define MACH_mk_timer_destroy_trap 92
#define MACH_mk_timer_arm_trap 93
#define MACH_mk_timer_cancel_trap 94
#define MACH_iokit_user_client_trap 100

#endif /* _SYSCALL_MACH_H_ */
33 changes: 27 additions & 6 deletions core/unix/os.c
Expand Up @@ -70,6 +70,7 @@
#ifdef MACOS
# include <sys/sysctl.h> /* for sysctl */
# include <mach/mach_traps.h> /* for swtch_pri */
# include "include/syscall_mach.h"
#endif

#ifdef LINUX
Expand Down Expand Up @@ -1710,6 +1711,12 @@ os_thread_init(dcontext_t *dcontext)

LOG(THREAD, LOG_THREADS, 1, "cur gs base is "PFX"\n", get_segment_base(SEG_GS));
LOG(THREAD, LOG_THREADS, 1, "cur fs base is "PFX"\n", get_segment_base(SEG_FS));

#ifdef MACOS
/* XXX: do we need to free/close dcontext->thread_port? I don't think so. */
dcontext->thread_port = dynamorio_mach_syscall(MACH_thread_self_trap, 0);
LOG(THREAD, LOG_ALL, 1, "Mach thread port: %d\n", dcontext->thread_port);
#endif
}

void
Expand Down Expand Up @@ -2563,7 +2570,7 @@ thread_signal(process_id_t pid, thread_id_t tid, int signum)
* Need to figure out whether we support raw Mach threads w/o pthread on top.
*/
ASSERT_NOT_IMPLEMENTED(false);
return (dynamorio_syscall(SYS___pthread_kill, 2, tid, signum) == 0);
return false;
#else
/* FIXME: for non-NPTL use SYS_kill */
/* Note that the pid is equivalent to the thread group id.
Expand All @@ -2575,6 +2582,20 @@ thread_signal(process_id_t pid, thread_id_t tid, int signum)
#endif
}

static bool
known_thread_signal(thread_record_t *tr, int signum)
{
#ifdef MACOS
ptr_int_t res;
if (tr->dcontext == NULL)
return FALSE;
res = dynamorio_syscall(SYS___pthread_kill, 2, tr->dcontext->thread_port, signum);
return res == 0;
#else
return thread_signal(tr->pid, tr->id, signum);
#endif
}

void
os_thread_sleep(uint64 milliseconds)
{
Expand Down Expand Up @@ -2648,7 +2669,7 @@ os_thread_suspend(thread_record_t *tr)
* to match Windows behavior.
*/
ASSERT(ksynch_get_value(&ostd->suspended) == 0);
if (!thread_signal(tr->pid, tr->id, SUSPEND_SIGNAL)) {
if (!known_thread_signal(tr, SUSPEND_SIGNAL)) {
ostd->suspend_count--;
mutex_unlock(&ostd->suspend_lock);
return false;
Expand Down Expand Up @@ -2725,7 +2746,7 @@ os_thread_terminate(thread_record_t *tr)
os_thread_data_t *ostd = (os_thread_data_t *) tr->dcontext->os_field;
ASSERT(ostd != NULL);
ostd->terminate = true;
return thread_signal(tr->pid, tr->id, SUSPEND_SIGNAL);
return known_thread_signal(tr, SUSPEND_SIGNAL);
}

bool
Expand Down Expand Up @@ -4014,16 +4035,16 @@ os_normalized_sysnum(int num_raw, instr_t *gateway, dcontext_t *dcontext)
num = (int) num_raw; /* Keep Mach and Machdep bits */
# else
if ((ptr_int_t)num_raw < 0) /* Mach syscall */
return (0x1000000 | -(int)num_raw);
return (SYSCALL_NUM_MARKER_MACH | -(int)num_raw);
else {
/* Bottom 16 bits are the number, top are arg size. */
num = (int)(num_raw & 0xffff);
}
# endif
if (interrupt == 0x81)
num |= 0x1000000; /* Mach */
num |= SYSCALL_NUM_MARKER_MACH;
else if (interrupt == 0x82)
num |= 0x3000000; /* Machdep */
num |= SYSCALL_NUM_MARKER_MACHDEP;
return num;
#else
return num_raw;
Expand Down
1 change: 1 addition & 0 deletions core/x86/arch_exports.h
Expand Up @@ -837,6 +837,7 @@ void client_int_syscall(void);
/* Some 32-bit syscalls return 64-bit values (e.g., SYS_lseek) in eax:edx */
int64 dynamorio_syscall(uint sysnum, uint num_args, ...);
int64 dynamorio_mach_dep_syscall(uint sysnum, uint num_args, ...);
ptr_int_t dynamorio_mach_syscall(uint sysnum, uint num_args, ...);
# else
ptr_int_t dynamorio_syscall(uint sysnum, uint num_args, ...);
# endif
Expand Down
103 changes: 98 additions & 5 deletions core/x86/x86.asm
Expand Up @@ -72,6 +72,7 @@ START_FILE
# ifdef LINUX
# include "include/syscall.h"
# else
# include "include/syscall_mach.h"
# include <sys/syscall.h>
# endif
#endif
Expand Down Expand Up @@ -935,7 +936,7 @@ GLOBAL_LABEL(dynamorio_syscall_sysenter:)
ret
END_FUNC(dynamorio_syscall_sysenter)

DECLARE_GLOBAL(dynamorio_sysenter_fixup)
DECLARE_GLOBAL(dynamorio_mach_syscall_fixup)
DECLARE_FUNC(dynamorio_syscall_sygate_sysenter)
GLOBAL_LABEL(dynamorio_syscall_sygate_sysenter:)
/* stack looks like:
Expand All @@ -949,7 +950,7 @@ GLOBAL_LABEL(dynamorio_syscall_sygate_sysenter:)
* fix for case 5441 where steal a ret from ntdll.dll so need to mangle
* our stack to look like
* esp + 0 sysenter_ret_address
* 4 dynamorio_sysenter_fixup
* 4 dynamorio_mach_syscall_fixup
* 8+ syscall args
* sysenter_tls_slot return address
* before we do the edx <- esp
Expand All @@ -974,10 +975,10 @@ GLOBAL_LABEL(dynamorio_syscall_sygate_sysenter:)
pop REG_XAX
#ifdef X64
/* Can't push a 64-bit immed */
mov REG_XCX, dynamorio_sysenter_fixup
mov REG_XCX, dynamorio_mach_syscall_fixup
push REG_XCX
#else
push dynamorio_sysenter_fixup
push dynamorio_mach_syscall_fixup
#endif
push PTRSZ SYMREF(sysenter_ret_address)
mov REG_XDX, REG_XSP
Expand All @@ -986,7 +987,7 @@ GLOBAL_LABEL(dynamorio_syscall_sygate_sysenter:)
#else
sysenter
#endif
ADDRTAKEN_LABEL(dynamorio_sysenter_fixup:)
ADDRTAKEN_LABEL(dynamorio_mach_syscall_fixup:)
/* push whatever (was the slot for the eax arg) */
push REG_XAX
/* ecx/edx should be dead here, just borrow one */
Expand Down Expand Up @@ -1324,6 +1325,98 @@ mach_dep_syscall_0args:
mach_dep_syscall_success:
ret
END_FUNC(dynamorio_mach_dep_syscall)


/* Mach syscall invocation.
* Signature: ptr_int_t dynamorio_mach_syscall(sysnum, num_args, arg1, arg2, ...)
* Only supports up to 4 args.
* Does not support returning a 64-bit value in 32-bit mode.
*/
DECLARE_FUNC(dynamorio_mach_syscall)
GLOBAL_LABEL(dynamorio_mach_syscall:)
/* x64 kernel doesn't clobber all the callee-saved registers */
push REG_XBX
# ifdef X64
/* reverse order so we don't clobber earlier args */
mov REG_XBX, ARG2 /* put num_args where we can reference it longer */
mov rax, ARG1 /* sysnum: only need eax, but need rax to use ARG1 (or movzx) */
cmp REG_XBX, 0
je dynamorio_mach_syscall_ready
mov ARG1, ARG3
cmp REG_XBX, 1
je dynamorio_mach_syscall_ready
mov ARG2, ARG4
cmp REG_XBX, 2
je dynamorio_mach_syscall_ready
mov ARG3, ARG5
cmp REG_XBX, 3
je dynamorio_mach_syscall_ready
mov ARG4, ARG6
# else
push REG_XBP
push REG_XSI
push REG_XDI
/* add 16 to skip the 4 pushes */
mov ecx, [16+ 8 + esp] /* num_args */
cmp ecx, 0
je dynamorio_mach_syscall_0args
cmp ecx, 1
je dynamorio_mach_syscall_1args
cmp ecx, 2
je dynamorio_mach_syscall_2args
cmp ecx, 3
je dynamorio_mach_syscall_3args
mov esi, [16+24 + esp] /* arg4 */
dynamorio_mach_syscall_3args:
mov edx, [16+20 + esp] /* arg3 */
dynamorio_mach_syscall_2args:
mov ecx, [16+16 + esp] /* arg2 */
dynamorio_mach_syscall_1args:
mov ebx, [16+12 + esp] /* arg1 */
dynamorio_mach_syscall_0args:
mov eax, [16+ 4 + esp] /* sysnum */
# ifdef X64
or eax, SYSCALL_NUM_MARKER_MACH
# else
/* The sysnum is passed as a negative number */
neg eax
# endif
/* args are on stack, w/ an extra slot (retaddr of syscall wrapper) */
lea REG_XSP, [-2*ARG_SZ + REG_XSP] /* maintain align-16: retaddr-5th below */
/* args are on stack, w/ an extra slot (retaddr of syscall wrapper) */
push esi
push edx
push ecx
push ebx
push 0 /* extra slot */
# endif
/* If we use ADDRTAKEN_LABEL and GLOBAL_REF we get text relocation
* complaints so we instead do this hack:
*/
call dynamorio_mach_syscall_next
dynamorio_mach_syscall_next:
pop REG_XDX
lea REG_XDX, [1/*pop*/ + 3/*lea*/ + 2/*sysenter*/ + 2/*mov*/ + REG_XDX]
mov REG_XCX, REG_XSP
/* We have to use sysenter for a Mach syscall, else we get SIGSYS.
* This implies that we can't return 64-bit in 32-bit mode.
*/
sysenter
# ifndef X64
lea esp, [7*ARG_SZ + esp] /* must not change flags */
pop REG_XDI
pop REG_XSI
pop REG_XBP
# endif
pop REG_XBX
/* return val is in eax for us */
/* convert to -errno */
jae dynamorio_mach_syscall_success
neg eax
dynamorio_mach_syscall_success:
ret
END_FUNC(dynamorio_mach_syscall)

# endif /* MACOS */

/* FIXME: this function should be in #ifdef CLIENT_INTERFACE
Expand Down

0 comments on commit 4e21420

Please sign in to comment.