diff options
Diffstat (limited to 'src/libmach/darwin.c')
-rw-r--r-- | src/libmach/darwin.c | 897 |
1 files changed, 0 insertions, 897 deletions
diff --git a/src/libmach/darwin.c b/src/libmach/darwin.c deleted file mode 100644 index 807dfa0d8..000000000 --- a/src/libmach/darwin.c +++ /dev/null @@ -1,897 +0,0 @@ -// Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#define __DARWIN_UNIX03 0 - -#include <u.h> -#include <sys/ptrace.h> -#include <sys/signal.h> -#include <mach/mach.h> -#include <mach/mach_traps.h> -#include <errno.h> -#include <libc.h> -#include <bio.h> -#include <mach.h> -#define Ureg Ureg32 -#include <ureg_x86.h> -#undef Ureg -#define Ureg Ureg64 -#include <ureg_amd64.h> -#undef Ureg -#undef waitpid /* want Unix waitpid, not Plan 9 */ - -typedef struct Ureg32 Ureg32; -typedef struct Ureg64 Ureg64; - -extern mach_port_t mach_reply_port(void); // should be in system headers, is not - -// Mach-error wrapper. -// Takes a mach return code and converts it into 0 / -1, -// setting errstr when it returns -1. - -static struct { - int code; - char *name; -} macherr[] = { - KERN_INVALID_ADDRESS, "invalid address", - KERN_PROTECTION_FAILURE, "protection failure", - KERN_NO_SPACE, "no space", - KERN_INVALID_ARGUMENT, "invalid argument", - KERN_FAILURE, "failure", - KERN_RESOURCE_SHORTAGE, "resource shortage", - KERN_NOT_RECEIVER, "not receiver", - KERN_NO_ACCESS, "no access", - KERN_MEMORY_FAILURE, "memory failure", - KERN_MEMORY_ERROR, "memory error", - KERN_ALREADY_IN_SET, "already in set", - KERN_NOT_IN_SET, "not in set", - KERN_NAME_EXISTS, "name exists", - KERN_ABORTED, "aborted", - KERN_INVALID_NAME, "invalid name", - KERN_INVALID_TASK, "invalid task", - KERN_INVALID_RIGHT, "invalid right", - KERN_INVALID_VALUE, "invalid value", - KERN_UREFS_OVERFLOW, "urefs overflow", - KERN_INVALID_CAPABILITY, "invalid capability", - KERN_RIGHT_EXISTS, "right exists", - KERN_INVALID_HOST, "invalid host", - KERN_MEMORY_PRESENT, "memory present", - KERN_MEMORY_DATA_MOVED, "memory data moved", - KERN_MEMORY_RESTART_COPY, "memory restart copy", - KERN_INVALID_PROCESSOR_SET, "invalid processor set", - KERN_POLICY_LIMIT, "policy limit", - KERN_INVALID_POLICY, "invalid policy", - KERN_INVALID_OBJECT, "invalid object", - KERN_ALREADY_WAITING, "already waiting", - KERN_DEFAULT_SET, "default set", - KERN_EXCEPTION_PROTECTED, "exception protected", - KERN_INVALID_LEDGER, "invalid ledger", - KERN_INVALID_MEMORY_CONTROL, "invalid memory control", - KERN_INVALID_SECURITY, "invalid security", - KERN_NOT_DEPRESSED, "not depressed", - KERN_TERMINATED, "terminated", - KERN_LOCK_SET_DESTROYED, "lock set destroyed", - KERN_LOCK_UNSTABLE, "lock unstable", - KERN_LOCK_OWNED, "lock owned", - KERN_LOCK_OWNED_SELF, "lock owned self", - KERN_SEMAPHORE_DESTROYED, "semaphore destroyed", - KERN_RPC_SERVER_TERMINATED, "rpc server terminated", - KERN_RPC_TERMINATE_ORPHAN, "rpc terminate orphan", - KERN_RPC_CONTINUE_ORPHAN, "rpc continue orphan", - KERN_NOT_SUPPORTED, "not supported", - KERN_NODE_DOWN, "node down", - KERN_NOT_WAITING, "not waiting", - KERN_OPERATION_TIMED_OUT, "operation timed out", - KERN_RETURN_MAX, "return max", - - MACH_SEND_IN_PROGRESS, "send in progress", - MACH_SEND_INVALID_DATA, "send invalid data", - MACH_SEND_INVALID_DEST, "send invalid dest", - MACH_SEND_TIMED_OUT, "send timed out", - MACH_SEND_INTERRUPTED, "send interrupted", - MACH_SEND_MSG_TOO_SMALL, "send msg too small", - MACH_SEND_INVALID_REPLY, "send invalid reply", - MACH_SEND_INVALID_RIGHT, "send invalid right", - MACH_SEND_INVALID_NOTIFY, "send invalid notify", - MACH_SEND_INVALID_MEMORY, "send invalid memory", - MACH_SEND_NO_BUFFER, "send no buffer", - MACH_SEND_TOO_LARGE, "send too large", - MACH_SEND_INVALID_TYPE, "send invalid type", - MACH_SEND_INVALID_HEADER, "send invalid header", - MACH_SEND_INVALID_TRAILER, "send invalid trailer", - MACH_SEND_INVALID_RT_OOL_SIZE, "send invalid rt ool size", - MACH_RCV_IN_PROGRESS, "rcv in progress", - MACH_RCV_INVALID_NAME, "rcv invalid name", - MACH_RCV_TIMED_OUT, "rcv timed out", - MACH_RCV_TOO_LARGE, "rcv too large", - MACH_RCV_INTERRUPTED, "rcv interrupted", - MACH_RCV_PORT_CHANGED, "rcv port changed", - MACH_RCV_INVALID_NOTIFY, "rcv invalid notify", - MACH_RCV_INVALID_DATA, "rcv invalid data", - MACH_RCV_PORT_DIED, "rcv port died", - MACH_RCV_IN_SET, "rcv in set", - MACH_RCV_HEADER_ERROR, "rcv header error", - MACH_RCV_BODY_ERROR, "rcv body error", - MACH_RCV_INVALID_TYPE, "rcv invalid type", - MACH_RCV_SCATTER_SMALL, "rcv scatter small", - MACH_RCV_INVALID_TRAILER, "rcv invalid trailer", - MACH_RCV_IN_PROGRESS_TIMED, "rcv in progress timed", - - MIG_TYPE_ERROR, "mig type error", - MIG_REPLY_MISMATCH, "mig reply mismatch", - MIG_REMOTE_ERROR, "mig remote error", - MIG_BAD_ID, "mig bad id", - MIG_BAD_ARGUMENTS, "mig bad arguments", - MIG_NO_REPLY, "mig no reply", - MIG_EXCEPTION, "mig exception", - MIG_ARRAY_TOO_LARGE, "mig array too large", - MIG_SERVER_DIED, "server died", - MIG_TRAILER_ERROR, "trailer has an unknown format", -}; - -static int -me(kern_return_t r) -{ - int i; - - if(r == 0) - return 0; - - for(i=0; i<nelem(macherr); i++){ - if(r == macherr[i].code){ - werrstr("mach: %s", macherr[i].name); - return -1; - } - } - werrstr("mach error %#x", r); - return -1; -} - -// Plan 9 and Linux do not distinguish between -// process ids and thread ids, so the interface here doesn't either. -// Unfortunately, Mach has three kinds of identifiers: process ids, -// handles to tasks (processes), and handles to threads within a -// process. All of them are small integers. -// -// To accommodate Mach, we employ a clumsy hack: in this interface, -// if you pass in a positive number, that's a process id. -// If you pass in a negative number, that identifies a thread that -// has been previously returned by procthreadpids (it indexes -// into the Thread table below). - -// Table of threads we have handles for. -typedef struct Thread Thread; -struct Thread -{ - int pid; - mach_port_t task; - mach_port_t thread; - int stopped; - int exc; - int code[10]; - Map *map; -}; -static Thread thr[1000]; -static int nthr; -static pthread_mutex_t mu; -static pthread_cond_t cond; -static void* excthread(void*); -static void* waitthread(void*); -static mach_port_t excport; - -enum { - ExcMask = EXC_MASK_BAD_ACCESS | - EXC_MASK_BAD_INSTRUCTION | - EXC_MASK_ARITHMETIC | - EXC_MASK_BREAKPOINT | - EXC_MASK_SOFTWARE -}; - -// Add process pid to the thread table. -// If it's already there, don't re-add it (unless force != 0). -static Thread* -addpid(int pid, int force) -{ - int i, j; - mach_port_t task; - mach_port_t *thread; - uint nthread; - Thread *ret; - static int first = 1; - - if(first){ - // Allocate a port for exception messages and - // send all thread exceptions to that port. - // The excthread reads that port and signals - // us if we are waiting on that thread. - pthread_t p; - int err; - - excport = mach_reply_port(); - pthread_mutex_init(&mu, nil); - pthread_cond_init(&cond, nil); - err = pthread_create(&p, nil, excthread, nil); - if (err != 0) { - fprint(2, "pthread_create failed: %s\n", strerror(err)); - abort(); - } - err = pthread_create(&p, nil, waitthread, (void*)(uintptr)pid); - if (err != 0) { - fprint(2, "pthread_create failed: %s\n", strerror(err)); - abort(); - } - first = 0; - } - - if(!force){ - for(i=0; i<nthr; i++) - if(thr[i].pid == pid) - return &thr[i]; - } - if(me(task_for_pid(mach_task_self(), pid, &task)) < 0) - return nil; - if(me(task_threads(task, &thread, &nthread)) < 0) - return nil; - mach_port_insert_right(mach_task_self(), excport, excport, MACH_MSG_TYPE_MAKE_SEND); - if(me(task_set_exception_ports(task, ExcMask, - excport, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)) < 0){ - fprint(2, "warning: cannot set excport: %r\n"); - } - ret = nil; - for(j=0; j<nthread; j++){ - if(force){ - // If we're forcing a refresh, don't re-add existing threads. - for(i=0; i<nthr; i++) - if(thr[i].pid == pid && thr[i].thread == thread[j]){ - if(ret == nil) - ret = &thr[i]; - goto skip; - } - } - if(nthr >= nelem(thr)) - return nil; - // TODO: We probably should save the old thread exception - // ports for each bit and then put them back when we exit. - // Probably the BSD signal handlers have put stuff there. - mach_port_insert_right(mach_task_self(), excport, excport, MACH_MSG_TYPE_MAKE_SEND); - if(me(thread_set_exception_ports(thread[j], ExcMask, - excport, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)) < 0){ - fprint(2, "warning: cannot set excport: %r\n"); - } - thr[nthr].pid = pid; - thr[nthr].task = task; - thr[nthr].thread = thread[j]; - if(ret == nil) - ret = &thr[nthr]; - nthr++; - skip:; - } - return ret; -} - -static Thread* -idtotable(int id) -{ - if(id >= 0) - return addpid(id, 1); - - id = -(id+1); - if(id >= nthr) - return nil; - return &thr[id]; -} - -/* -static int -idtopid(int id) -{ - Thread *t; - - if((t = idtotable(id)) == nil) - return -1; - return t->pid; -} -*/ - -static mach_port_t -idtotask(int id) -{ - Thread *t; - - if((t = idtotable(id)) == nil) - return -1; - return t->task; -} - -static mach_port_t -idtothread(int id) -{ - Thread *t; - - if((t = idtotable(id)) == nil) - return -1; - return t->thread; -} - -static int machsegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr); -static int machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr); - -Map* -attachproc(int id, Fhdr *fp) -{ - Thread *t; - Map *map; - - if((t = idtotable(id)) == nil) - return nil; - if(t->map) - return t->map; - map = newmap(0, 4); - if(!map) - return nil; - map->pid = -((t-thr) + 1); - if(mach->regsize) - setmap(map, -1, 0, mach->regsize, 0, "regs", machregrw); - setmap(map, -1, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtaddr, "*text", machsegrw); - setmap(map, -1, fp->dataddr, mach->utop, fp->dataddr, "*data", machsegrw); - t->map = map; - return map; -} - -// Return list of ids for threads in id. -int -procthreadpids(int id, int *out, int nout) -{ - Thread *t; - int i, n, pid; - - t = idtotable(id); - if(t == nil) - return -1; - pid = t->pid; - addpid(pid, 1); // force refresh of thread list - n = 0; - for(i=0; i<nthr; i++) { - if(thr[i].pid == pid) { - if(n < nout) - out[n] = -(i+1); - n++; - } - } - return n; -} - -// Detach from proc. -// TODO(rsc): Perhaps should unsuspend any threads and clean-up the table. -void -detachproc(Map *m) -{ - free(m); -} - -// Should return array of pending signals (notes) -// but don't know how to do that on OS X. -int -procnotes(int pid, char ***pnotes) -{ - USED(pid); - *pnotes = 0; - return 0; -} - -// There must be a way to do this. Gdb can do it. -// But I don't see, in the Apple gdb sources, how. -char* -proctextfile(int pid) -{ - USED(pid); - return nil; -} - -// Read/write from a Mach data segment. -static int -machsegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr) -{ - mach_port_t task; - int r; - - USED(seg); - - task = idtotask(map->pid); - if(task == -1) - return -1; - - if(isr){ - vm_size_t nn; - nn = n; - if(me(vm_read_overwrite(task, addr, n, (uintptr)v, &nn)) < 0) { - fprint(2, "vm_read_overwrite %#llux %d to %p: %r\n", addr, n, v); - return -1; - } - return nn; - }else{ - r = vm_write(task, addr, (uintptr)v, n); - if(r == KERN_INVALID_ADDRESS){ - // Happens when writing to text segment. - // Change protections. - if(me(vm_protect(task, addr, n, 0, VM_PROT_WRITE|VM_PROT_READ|VM_PROT_EXECUTE)) < 0){ - fprint(2, "vm_protect: %s\n", r); - return -1; - } - r = vm_write(task, addr, (uintptr)v, n); - } - if(r != 0){ - me(r); - return -1; - } - return n; - } -} - -// Convert Ureg offset to x86_thread_state32_t offset. -static int -go2darwin32(uvlong addr) -{ - switch(addr){ - case offsetof(Ureg32, ax): - return offsetof(x86_thread_state32_t, eax); - case offsetof(Ureg32, bx): - return offsetof(x86_thread_state32_t, ebx); - case offsetof(Ureg32, cx): - return offsetof(x86_thread_state32_t, ecx); - case offsetof(Ureg32, dx): - return offsetof(x86_thread_state32_t, edx); - case offsetof(Ureg32, si): - return offsetof(x86_thread_state32_t, esi); - case offsetof(Ureg32, di): - return offsetof(x86_thread_state32_t, edi); - case offsetof(Ureg32, bp): - return offsetof(x86_thread_state32_t, ebp); - case offsetof(Ureg32, fs): - return offsetof(x86_thread_state32_t, fs); - case offsetof(Ureg32, gs): - return offsetof(x86_thread_state32_t, gs); - case offsetof(Ureg32, pc): - return offsetof(x86_thread_state32_t, eip); - case offsetof(Ureg32, cs): - return offsetof(x86_thread_state32_t, cs); - case offsetof(Ureg32, flags): - return offsetof(x86_thread_state32_t, eflags); - case offsetof(Ureg32, sp): - return offsetof(x86_thread_state32_t, esp); - } - return -1; -} - -// Convert Ureg offset to x86_thread_state64_t offset. -static int -go2darwin64(uvlong addr) -{ - switch(addr){ - case offsetof(Ureg64, ax): - return offsetof(x86_thread_state64_t, rax); - case offsetof(Ureg64, bx): - return offsetof(x86_thread_state64_t, rbx); - case offsetof(Ureg64, cx): - return offsetof(x86_thread_state64_t, rcx); - case offsetof(Ureg64, dx): - return offsetof(x86_thread_state64_t, rdx); - case offsetof(Ureg64, si): - return offsetof(x86_thread_state64_t, rsi); - case offsetof(Ureg64, di): - return offsetof(x86_thread_state64_t, rdi); - case offsetof(Ureg64, bp): - return offsetof(x86_thread_state64_t, rbp); - case offsetof(Ureg64, r8): - return offsetof(x86_thread_state64_t, r8); - case offsetof(Ureg64, r9): - return offsetof(x86_thread_state64_t, r9); - case offsetof(Ureg64, r10): - return offsetof(x86_thread_state64_t, r10); - case offsetof(Ureg64, r11): - return offsetof(x86_thread_state64_t, r11); - case offsetof(Ureg64, r12): - return offsetof(x86_thread_state64_t, r12); - case offsetof(Ureg64, r13): - return offsetof(x86_thread_state64_t, r13); - case offsetof(Ureg64, r14): - return offsetof(x86_thread_state64_t, r14); - case offsetof(Ureg64, r15): - return offsetof(x86_thread_state64_t, r15); - case offsetof(Ureg64, fs): - return offsetof(x86_thread_state64_t, fs); - case offsetof(Ureg64, gs): - return offsetof(x86_thread_state64_t, gs); - case offsetof(Ureg64, ip): - return offsetof(x86_thread_state64_t, rip); - case offsetof(Ureg64, cs): - return offsetof(x86_thread_state64_t, cs); - case offsetof(Ureg64, flags): - return offsetof(x86_thread_state64_t, rflags); - case offsetof(Ureg64, sp): - return offsetof(x86_thread_state64_t, rsp); - } - return -1; -} - -extern Mach mi386; - -// Read/write from fake register segment. -static int -machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr) -{ - uint nn, count, state; - mach_port_t thread; - int reg; - char buf[100]; - union { - x86_thread_state64_t reg64; - x86_thread_state32_t reg32; - uchar p[1]; - } u; - uchar *p; - - USED(seg); - - if(n > 8){ - werrstr("asked for %d-byte register", n); - return -1; - } - - thread = idtothread(map->pid); - if(thread == -1){ - werrstr("no such id"); - return -1; - } - - if(mach == &mi386) { - count = x86_THREAD_STATE32_COUNT; - state = x86_THREAD_STATE32; - if((reg = go2darwin32(addr)) < 0 || reg+n > sizeof u){ - if(isr){ - memset(v, 0, n); - return 0; - } - werrstr("register %llud not available", addr); - return -1; - } - } else { - count = x86_THREAD_STATE64_COUNT; - state = x86_THREAD_STATE64; - if((reg = go2darwin64(addr)) < 0 || reg+n > sizeof u){ - if(isr){ - memset(v, 0, n); - return 0; - } - werrstr("register %llud not available", addr); - return -1; - } - } - - if(!isr && me(thread_suspend(thread)) < 0){ - werrstr("thread suspend %#x: %r", thread); - return -1; - } - nn = count; - if(me(thread_get_state(thread, state, (void*)u.p, &nn)) < 0){ - if(!isr) - thread_resume(thread); - rerrstr(buf, sizeof buf); - if(strstr(buf, "send invalid dest") != nil) - werrstr("process exited"); - else - werrstr("thread_get_state: %r"); - return -1; - } - - p = u.p+reg; - if(isr) - memmove(v, p, n); - else{ - memmove(p, v, n); - nn = count; - if(me(thread_set_state(thread, state, (void*)u.p, nn)) < 0){ - thread_resume(thread); - werrstr("thread_set_state: %r"); - return -1; - } - - if(me(thread_resume(thread)) < 0){ - werrstr("thread_resume: %r"); - return -1; - } - } - return 0; -} - -enum -{ - FLAGS_TF = 0x100 // x86 single-step processor flag -}; - -// Is thread t suspended? -static int -threadstopped(Thread *t) -{ - struct thread_basic_info info; - uint size; - - size = sizeof info; - if(me(thread_info(t->thread, THREAD_BASIC_INFO, (thread_info_t)&info, &size)) < 0){ - fprint(2, "threadstopped thread_info %#x: %r\n"); - return 1; - } - return info.suspend_count > 0; -} - -// If thread t is suspended, start it up again. -// If singlestep is set, only let it execute one instruction. -static int -threadstart(Thread *t, int singlestep) -{ - int i; - uint n; - struct thread_basic_info info; - - if(!threadstopped(t)) - return 0; - - // Set or clear the processor single-step flag, as appropriate. - if(mach == &mi386) { - x86_thread_state32_t regs; - n = x86_THREAD_STATE32_COUNT; - if(me(thread_get_state(t->thread, x86_THREAD_STATE32, - (thread_state_t)®s, - &n)) < 0) - return -1; - if(singlestep) - regs.eflags |= FLAGS_TF; - else - regs.eflags &= ~FLAGS_TF; - if(me(thread_set_state(t->thread, x86_THREAD_STATE32, - (thread_state_t)®s, - x86_THREAD_STATE32_COUNT)) < 0) - return -1; - } else { - x86_thread_state64_t regs; - n = x86_THREAD_STATE64_COUNT; - if(me(thread_get_state(t->thread, x86_THREAD_STATE64, - (thread_state_t)®s, - &n)) < 0) - return -1; - if(singlestep) - regs.rflags |= FLAGS_TF; - else - regs.rflags &= ~FLAGS_TF; - if(me(thread_set_state(t->thread, x86_THREAD_STATE64, - (thread_state_t)®s, - x86_THREAD_STATE64_COUNT)) < 0) - return -1; - } - - // Run. - n = sizeof info; - if(me(thread_info(t->thread, THREAD_BASIC_INFO, (thread_info_t)&info, &n)) < 0) - return -1; - for(i=0; i<info.suspend_count; i++) - if(me(thread_resume(t->thread)) < 0) - return -1; - return 0; -} - -// Stop thread t. -static int -threadstop(Thread *t) -{ - if(threadstopped(t)) - return 0; - if(me(thread_suspend(t->thread)) < 0) - return -1; - return 0; -} - -// Callback for exc_server below. Called when a thread we are -// watching has an exception like hitting a breakpoint. -kern_return_t -catch_exception_raise(mach_port_t eport, mach_port_t thread, - mach_port_t task, exception_type_t exception, - exception_data_t code, mach_msg_type_number_t ncode) -{ - Thread *t; - int i; - - USED(eport); - USED(task); - - t = nil; - for(i=0; i<nthr; i++){ - if(thr[i].thread == thread){ - t = &thr[i]; - goto havet; - } - } - if(nthr > 0) - addpid(thr[0].pid, 1); - for(i=0; i<nthr; i++){ - if(thr[i].thread == thread){ - t = &thr[i]; - goto havet; - } - } - fprint(2, "did not find thread in catch_exception_raise\n"); - return KERN_SUCCESS; // let thread continue - -havet: - t->exc = exception; - if(ncode > nelem(t->code)) - ncode = nelem(t->code); - memmove(t->code, code, ncode*sizeof t->code[0]); - - // Suspend thread, so that we can look at it & restart it later. - if(me(thread_suspend(thread)) < 0) - fprint(2, "catch_exception_raise thread_suspend: %r\n"); - - // Synchronize with waitstop below. - pthread_mutex_lock(&mu); - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&mu); - - return KERN_SUCCESS; -} - -// Exception watching thread, started in addpid above. -static void* -excthread(void *v) -{ - USED(v); - extern boolean_t exc_server(mach_msg_header_t *, mach_msg_header_t *); - mach_msg_server(exc_server, 2048, excport, 0); - return 0; -} - -// Wait for pid to exit. -static int exited; -static void* -waitthread(void *v) -{ - int pid, status; - - pid = (int)(uintptr)v; - waitpid(pid, &status, 0); - exited = 1; - // Synchronize with waitstop below. - pthread_mutex_lock(&mu); - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&mu); - return nil; -} - -// Wait for thread t to stop. -static int -waitstop(Thread *t) -{ - pthread_mutex_lock(&mu); - while(!exited && !threadstopped(t)) - pthread_cond_wait(&cond, &mu); - pthread_mutex_unlock(&mu); - return 0; -} - -int -ctlproc(int id, char *msg) -{ - Thread *t; - int status; - - // Hang/attached dance is for debugging newly exec'ed programs. - // After fork, the child does ctlproc("hang") before exec, - // and the parent does ctlproc("attached") and then waitstop. - // Using these requires the BSD ptrace interface, unlike everything - // else we do, which uses only the Mach interface. Our goal here - // is to do as little as possible using ptrace and then flip over to Mach. - - if(strcmp(msg, "hang") == 0) - return ptrace(PT_TRACE_ME, 0, 0, 0); - - if(strcmp(msg, "attached") == 0){ - // The pid "id" has done a ctlproc "hang" and then - // exec, so we should find it stoppped just before exec - // of the new program. - #undef waitpid - if(waitpid(id, &status, WUNTRACED) < 0){ - fprint(2, "ctlproc attached waitpid: %r\n"); - return -1; - } - if(WIFEXITED(status) || !WIFSTOPPED(status)){ - fprint(2, "ctlproc attached: bad process state\n"); - return -1; - } - - // Find Mach thread for pid and suspend it. - t = addpid(id, 1); - if(t == nil) { - fprint(2, "ctlproc attached: addpid: %r\n"); - return -1; - } - if(me(thread_suspend(t->thread)) < 0){ - fprint(2, "ctlproc attached: thread_suspend: %r\n"); - return -1; - } - - // Let ptrace tell the process to keep going: - // then ptrace is out of the way and we're back in Mach land. - if(ptrace(PT_CONTINUE, id, (caddr_t)1, 0) < 0) { - fprint(2, "ctlproc attached: ptrace continue: %r\n"); - return -1; - } - - return 0; - } - - // All the other control messages require a Thread structure. - if((t = idtotable(id)) == nil){ - werrstr("no such thread"); - return -1; - } - - if(strcmp(msg, "kill") == 0) - return ptrace(PT_KILL, t->pid, 0, 0); - - if(strcmp(msg, "start") == 0) - return threadstart(t, 0); - - if(strcmp(msg, "stop") == 0) - return threadstop(t); - - if(strcmp(msg, "startstop") == 0){ - if(threadstart(t, 0) < 0) - return -1; - return waitstop(t); - } - - if(strcmp(msg, "step") == 0){ - if(threadstart(t, 1) < 0) - return -1; - return waitstop(t); - } - - if(strcmp(msg, "waitstop") == 0) - return waitstop(t); - - // sysstop not available on OS X - - werrstr("unknown control message"); - return -1; -} - -char* -procstatus(int id) -{ - Thread *t; - - if((t = idtotable(id)) == nil) - return "gone!"; - - if(threadstopped(t)) - return "Stopped"; - - return "Running"; -} - |