| //	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) | 
 | { | 
 | 	*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) | 
 | { | 
 | 	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; | 
 |  | 
 | 	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; | 
 |  | 
 | 	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; | 
 |  | 
 | 	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) | 
 | { | 
 | 	extern boolean_t exc_server(); | 
 | 	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"; | 
 | } | 
 |  |