summaryrefslogtreecommitdiff
path: root/src/libmach/darwin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmach/darwin.c')
-rw-r--r--src/libmach/darwin.c897
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)&regs,
- &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)&regs,
- 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)&regs,
- &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)&regs,
- 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";
-}
-