Skip to content

Commit

Permalink
i#58 MacOS: thread synch and termination
Browse files Browse the repository at this point in the history
Implements handle_suspend_signal(), dynamorio_semaphore_signal_all(),
dynamorio_sys_exit_group(), and dynamorio_sys_exit() for Mac.
Unfortunately we have to use the stack for 32-bit syscalls, so we use the
app stack under the assumption that it's valid (we're terminating so there
should be minimal transparency impact).

SVN-Revision: 2619
  • Loading branch information
derekbruening committed Mar 30, 2014
1 parent 4e21420 commit b9d438a
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 28 deletions.
9 changes: 7 additions & 2 deletions core/unix/ksynch_macos.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
* ksynch_macos.c - synchronization via the kernel
*
* FIXME i#58: NYI (see comments below as well):
* + haven't really tested this yet
* + longer-term i#1291: use raw syscalls instead of libSystem wrappers
* + Haven't really tested this yet
* + Longer-term i#1291: use raw syscalls instead of libSystem wrappers.
* Some of these are basically just Mach syscall wrappers, but others like
* semaphore_create() are slightly more involved.
*/

#include "../globals.h"
Expand Down Expand Up @@ -79,6 +81,8 @@ ksynch_init_var(mac_synch_t *synch)
SYNC_POLICY_PREPOST, 0);
ASSERT(synch->sem != 0); /* we assume 0 is never a legitimate value */
synch->value = 0;
LOG(THREAD_GET, LOG_THREADS, 2, "semaphore %d created, status %d\n",
synch->sem, res);
return (res == KERN_SUCCESS);
}

Expand All @@ -93,6 +97,7 @@ bool
ksynch_free_var(mac_synch_t *synch)
{
kern_return_t res = semaphore_destroy(mach_task_self(), synch->sem);
synch->sem = 0;
return (res == KERN_SUCCESS);
}

Expand Down
27 changes: 21 additions & 6 deletions core/unix/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -5493,25 +5493,40 @@ handle_suspend_signal(dcontext_t *dcontext, kernel_ucontext_t *ucxt)

if (ostd->terminate) {
/* PR 297902: exit this thread, without using any stack */
#ifdef MACOS
/* We need a stack as 32-bit syscalls take args on the stack.
* We go ahead and use it for x64 too for simpler sysenter return.
* We don't have a lot of options: we're terminating, so we go ahead
* and use the app stack.
*/
byte *app_xsp = (byte *) get_mcontext(dcontext)->xsp;
#endif
LOG(THREAD, LOG_ASYNCH, 2, "handle_suspend_signal: exiting\n");
if (ksynch_kernel_support()) {
#ifdef LINUX
/* can't use stack once set terminated to 1 so in asm we do:
* ostd->terminated = 1;
* futex_wake_all(&ostd->terminated);
* futex_wake_all(&ostd->terminated in xax);
* semaphore_signal_all(&ostd->terminated in xax);
*/
#ifdef MACOS
KSYNCH_TYPE *term = &ostd->terminated;
ASSERT(sizeof(ostd->terminated.sem) == 4);
#else
volatile int *term = &ostd->terminated;
#endif
asm("mov %0, %%"ASM_XAX : : "m"(term));
#ifdef MACOS
asm("movl $1,4(%"ASM_XAX")");
asm("mov %0, %%"ASM_XSP : : "m"(app_xsp));
asm("jmp _dynamorio_semaphore_signal_all");
#else
asm("movl $1,(%"ASM_XAX")");
asm("jmp dynamorio_futex_wake_and_exit");
#else
/* FIXME i#1277: need MacOS version of this */
ASSERT_NOT_IMPLEMENTED(false);
#endif
} else {
ksynch_set_value(&ostd->terminated, 1);
#ifdef MACOS
/* XXX: won't this kill the whole process? */
asm("mov %0, %%"ASM_XSP : : "m"(app_xsp));
asm("jmp _dynamorio_sys_exit");
#else
asm("jmp dynamorio_sys_exit");
Expand Down
5 changes: 4 additions & 1 deletion core/x86/arch_exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -843,8 +843,11 @@ ptr_int_t dynamorio_syscall(uint sysnum, uint num_args, ...);
# endif
void dynamorio_sigreturn(void);
void dynamorio_sys_exit(void);
# ifdef MACOS
void dynamorio_semaphore_signal_all(KSYNCH_TYPE *ksynch/*in xax*/);
# endif
# ifdef LINUX
void dynamorio_futex_wake_and_exit(volatile int *futex);
void dynamorio_futex_wake_and_exit(volatile int *futex/* in xax*/);
# ifndef X64
void dynamorio_nonrt_sigreturn(void);
# endif
Expand Down
120 changes: 101 additions & 19 deletions core/x86/x86.asm
Original file line number Diff line number Diff line change
Expand Up @@ -1490,28 +1490,63 @@ GLOBAL_LABEL(dynamorio_sigreturn:)

/* we need to exit without using any stack, to support
* THREAD_SYNCH_TERMINATED_AND_CLEANED.
* XXX: on MacOS this does use the stack.
* FIXME i#1403: on MacOS we fail to free the app's stack: we need to pass it to
* bsdthread_terminate.
*/
DECLARE_FUNC(dynamorio_sys_exit)
GLOBAL_LABEL(dynamorio_sys_exit:)
#ifdef X64
mov edi, 0 /* exit code: hardcoded */
mov eax, HEX(3c) /* SYS_exit */
#ifdef MACOS
/* We need the mach port in order to invoke bsdthread_terminate */
mov eax, MACH_thread_self_trap
# ifdef X64
or eax, SYSCALL_NUM_MARKER_MACH
# else
neg eax
/* XXX: what about stack alignment? hard to control since we jumped here */
# endif
/* see dynamorio_mach_syscall about why we do this call;pop and sysenter */
call dynamorio_sys_exit_next
dynamorio_sys_exit_next:
pop REG_XDX
lea REG_XDX, [1/*pop*/ + 3/*lea*/ + 2/*sysenter*/ + 2/*mov*/ + REG_XDX]
mov REG_XCX, REG_XSP
sysenter
jae dynamorio_sys_exit_failed
# ifdef X64
mov ARG4, 0 /* stack to free: NULL */
mov ARG3, 0 /* stack free size: 0 */
mov ARG2, REG_XAX /* kernel port, which we just acquired */
mov ARG1, 0 /* join semaphore: SEMAPHORE_NULL */
mov eax, SYS_bsdthread_terminate
mov r10, rcx
syscall
#else
# ifdef MACOS
/* XXX: won't this kill the whole process? dynamorio_sys_exit_group
* should do this, and here we should invoke something else
*/
lea REG_XSP, [-3*ARG_SZ + REG_XSP] /* maintain align-16: offset retaddr */
# else
lea REG_XSP, [-ARG_SZ + REG_XSP] /* maintain align-16: offset retaddr */
push 0 /* stack to free: NULL */
push 0 /* stack free size: 0 */
push REG_XAX /* kernel port, which we just acquired */
push 0 /* join semaphore: SEMAPHORE_NULL */
push 0 /* retaddr slot */
mov eax, SYS_bsdthread_terminate
int HEX(80)
# endif
#else /* LINUX: */
# ifdef X64
mov edi, 0 /* exit code: hardcoded */
mov eax, SYS_exit
mov r10, rcx
syscall
# else
mov ebx, 0 /* exit code: hardcoded */
mov eax, HEX(1) /* SYS_exit */
mov eax, SYS_exit
/* PR 254280: we assume int$80 is ok even for LOL64 */
int HEX(80)
# endif
#endif
/* should not return. if we somehow do, infinite loop is intentional.
* FIXME: do better in release build! FIXME - why not an int3? */
dynamorio_sys_exit_failed:
jmp GLOBAL_REF(unexpected_return)
END_FUNC(dynamorio_sys_exit)

Expand Down Expand Up @@ -1543,32 +1578,79 @@ GLOBAL_LABEL(dynamorio_futex_wake_and_exit:)
/* PR 254280: we assume int$80 is ok even for LOL64 */
int HEX(80)
#endif
jmp GLOBAL_REF(dynamorio_sys_exit)
jmp GLOBAL_REF(dynamorio_sys_exit)
END_FUNC(dynamorio_futex_wake_and_exit)
#endif /* LINUX */

#ifdef MACOS
/* We need to call semaphore_signal_all without using dstack, to support
* THREAD_SYNCH_TERMINATED_AND_CLEANED. We have to put syscall args on
* the stack for 32-bit, and we use the stack for call;pop for
* sysenter -- so we use the app stack, which we assume the caller has
* put us on. We're only called when terminating a thread so transparency
* should be ok so long as the app's stack is valid.
* Takes KSYNCH_TYPE* in xax.
*/
DECLARE_FUNC(dynamorio_semaphore_signal_all)
GLOBAL_LABEL(dynamorio_semaphore_signal_all:)
mov REG_XAX, DWORD [REG_XAX] /* load mach_synch_t->sem */
# ifdef X64
mov ARG1, REG_XAX
mov eax, MACH_semaphore_signal_all_trap
or eax, SYSCALL_NUM_MARKER_MACH
# else
push REG_XAX
mov eax, MACH_semaphore_signal_all_trap
neg eax
/* args are on stack, w/ an extra slot (retaddr of syscall wrapper) */
push 0 /* extra slot */
/* XXX: what about stack alignment? hard to control since we jumped here */
# endif
/* see dynamorio_mach_syscall about why we do this call;pop and sysenter */
call dynamorio_semaphore_next
dynamorio_semaphore_next:
pop REG_XDX
lea REG_XDX, [1/*pop*/ + 3/*lea*/ + 2/*sysenter*/ + 2/*mov*/ + REG_XDX]
mov REG_XCX, REG_XSP
sysenter
# ifndef X64
lea esp, [2*ARG_SZ + esp] /* must not change flags */
# endif
/* we ignore return val */
jmp GLOBAL_REF(dynamorio_sys_exit)
END_FUNC(dynamorio_semaphore_signal_all)
#endif /* MACOS */

/* exit entire group without using any stack, in case something like
* SYS_kill via cleanup_and_terminate fails
* SYS_kill via cleanup_and_terminate fails.
* XXX: on 32-bit MacOS this does use the stack.
*/
DECLARE_FUNC(dynamorio_sys_exit_group)
GLOBAL_LABEL(dynamorio_sys_exit_group:)
#ifdef X64
mov edi, 0 /* exit code: hardcoded */
mov eax, HEX(e7) /* SYS_exit_group */
# ifdef MACOS
mov eax, SYS_exit
# else
mov eax, SYS_exit_group
# endif
mov r10, rcx
syscall
#else
# ifdef MACOS
/* XXX: invoke SYS_exit here and something else in dynamorio_sys_exit */
lea REG_XSP, [-3*ARG_SZ + REG_XSP] /* maintain align-16: offset retaddr */
# endif
# ifdef MACOS
lea REG_XSP, [-ARG_SZ + REG_XSP] /* maintain align-16: offset retaddr */
push 0 /* exit code: hardcoded */
push 0 /* retaddr slot */
mov eax, SYS_exit
# else
mov ebx, 0 /* exit code: hardcoded */
mov eax, HEX(fc) /* SYS_exit_group */
mov eax, SYS_exit_group
# endif
/* PR 254280: we assume int$80 is ok even for LOL64 */
int HEX(80)
#endif
/* should not return. if we somehow do, infinite loop is intentional.
* FIXME: do better in release build! FIXME - why not an int3? */
* FIXME: do better in release build! why not an int3? */
jmp GLOBAL_REF(unexpected_return)
END_FUNC(dynamorio_sys_exit_group)

Expand Down

0 comments on commit b9d438a

Please sign in to comment.