2008-08-04 18:24:25 -06:00
|
|
|
// 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.
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
#define __DARWIN_UNIX03 0
|
|
|
|
|
2008-08-04 18:24:25 -06:00
|
|
|
#include <u.h>
|
2008-09-16 13:33:00 -06:00
|
|
|
#include <sys/ptrace.h>
|
|
|
|
#include <sys/signal.h>
|
|
|
|
#include <mach/mach.h>
|
2008-10-20 16:26:38 -06:00
|
|
|
#include <mach/mach_traps.h>
|
2008-09-16 13:33:00 -06:00
|
|
|
#include <errno.h>
|
2008-08-04 18:24:25 -06:00
|
|
|
#include <libc.h>
|
|
|
|
#include <bio.h>
|
2009-08-14 11:46:04 -06:00
|
|
|
#include <mach.h>
|
2009-05-26 18:20:57 -06:00
|
|
|
#define Ureg Ureg32
|
|
|
|
#include <ureg_x86.h>
|
|
|
|
#undef Ureg
|
|
|
|
#define Ureg Ureg64
|
2008-09-16 13:33:00 -06:00
|
|
|
#include <ureg_amd64.h>
|
2009-05-26 18:20:57 -06:00
|
|
|
#undef Ureg
|
2008-11-14 11:45:23 -07:00
|
|
|
#undef waitpid /* want Unix waitpid, not Plan 9 */
|
2008-09-16 13:33:00 -06:00
|
|
|
|
2009-05-26 18:20:57 -06:00
|
|
|
typedef struct Ureg32 Ureg32;
|
|
|
|
typedef struct Ureg64 Ureg64;
|
|
|
|
|
2008-10-20 16:26:38 -06:00
|
|
|
extern mach_port_t mach_reply_port(void); // should be in system headers, is not
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// 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",
|
2008-08-04 18:24:25 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
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)
|
2008-08-04 18:24:25 -06:00
|
|
|
{
|
2008-09-16 13:33:00 -06:00
|
|
|
int i;
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
if(r == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for(i=0; i<nelem(macherr); i++){
|
|
|
|
if(r == macherr[i].code){
|
2010-03-20 23:37:23 -06:00
|
|
|
werrstr("mach: %s", macherr[i].name);
|
2008-09-16 13:33:00 -06:00
|
|
|
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.
|
|
|
|
//
|
2011-05-30 02:02:59 -06:00
|
|
|
// To accommodate Mach, we employ a clumsy hack: in this interface,
|
2008-09-16 13:33:00 -06:00
|
|
|
// 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*);
|
2008-11-14 11:45:23 -07:00
|
|
|
static void* waitthread(void*);
|
2008-09-16 13:33:00 -06:00
|
|
|
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)
|
|
|
|
{
|
2008-10-20 16:26:38 -06:00
|
|
|
int i, j;
|
2008-09-16 13:33:00 -06:00
|
|
|
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;
|
2011-06-28 10:04:50 -06:00
|
|
|
int err;
|
2008-09-16 13:33:00 -06:00
|
|
|
|
|
|
|
excport = mach_reply_port();
|
|
|
|
pthread_mutex_init(&mu, nil);
|
|
|
|
pthread_cond_init(&cond, nil);
|
2011-06-28 10:04:50 -06:00
|
|
|
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();
|
|
|
|
}
|
2008-09-16 13:33:00 -06:00
|
|
|
first = 0;
|
|
|
|
}
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
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);
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
id = -(id+1);
|
|
|
|
if(id >= nthr)
|
|
|
|
return nil;
|
|
|
|
return &thr[id];
|
|
|
|
}
|
|
|
|
|
2008-10-20 16:26:38 -06:00
|
|
|
/*
|
2008-09-16 13:33:00 -06:00
|
|
|
static int
|
|
|
|
idtopid(int id)
|
|
|
|
{
|
|
|
|
Thread *t;
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
if((t = idtotable(id)) == nil)
|
|
|
|
return -1;
|
|
|
|
return t->pid;
|
2008-08-04 18:24:25 -06:00
|
|
|
}
|
2008-10-20 16:26:38 -06:00
|
|
|
*/
|
2008-08-04 18:24:25 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
static mach_port_t
|
|
|
|
idtotask(int id)
|
|
|
|
{
|
|
|
|
Thread *t;
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
if((t = idtotable(id)) == nil)
|
|
|
|
return -1;
|
|
|
|
return t->task;
|
|
|
|
}
|
|
|
|
|
|
|
|
static mach_port_t
|
|
|
|
idtothread(int id)
|
|
|
|
{
|
|
|
|
Thread *t;
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
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.
|
2008-08-04 18:24:25 -06:00
|
|
|
int
|
2009-02-03 16:00:09 -07:00
|
|
|
procthreadpids(int id, int *out, int nout)
|
2008-08-04 18:24:25 -06:00
|
|
|
{
|
2008-09-16 13:33:00 -06:00
|
|
|
Thread *t;
|
|
|
|
int i, n, pid;
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
t = idtotable(id);
|
|
|
|
if(t == nil)
|
|
|
|
return -1;
|
|
|
|
pid = t->pid;
|
2008-09-26 15:10:33 -06:00
|
|
|
addpid(pid, 1); // force refresh of thread list
|
2008-09-16 13:33:00 -06:00
|
|
|
n = 0;
|
2009-02-03 16:00:09 -07:00
|
|
|
for(i=0; i<nthr; i++) {
|
|
|
|
if(thr[i].pid == pid) {
|
|
|
|
if(n < nout)
|
|
|
|
out[n] = -(i+1);
|
2008-09-16 13:33:00 -06:00
|
|
|
n++;
|
2009-02-03 16:00:09 -07:00
|
|
|
}
|
|
|
|
}
|
2008-09-16 13:33:00 -06:00
|
|
|
return n;
|
2008-08-04 18:24:25 -06:00
|
|
|
}
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// Detach from proc.
|
|
|
|
// TODO(rsc): Perhaps should unsuspend any threads and clean-up the table.
|
2008-08-04 18:24:25 -06:00
|
|
|
void
|
|
|
|
detachproc(Map *m)
|
|
|
|
{
|
2008-09-16 13:33:00 -06:00
|
|
|
free(m);
|
2008-08-04 18:24:25 -06:00
|
|
|
}
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// Should return array of pending signals (notes)
|
|
|
|
// but don't know how to do that on OS X.
|
2008-08-04 18:24:25 -06:00
|
|
|
int
|
|
|
|
procnotes(int pid, char ***pnotes)
|
|
|
|
{
|
2008-09-16 13:33:00 -06:00
|
|
|
*pnotes = 0;
|
|
|
|
return 0;
|
2008-08-04 18:24:25 -06:00
|
|
|
}
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// There must be a way to do this. Gdb can do it.
|
|
|
|
// But I don't see, in the Apple gdb sources, how.
|
2008-08-04 18:24:25 -06:00
|
|
|
char*
|
|
|
|
proctextfile(int pid)
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// 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;
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
task = idtotask(map->pid);
|
|
|
|
if(task == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if(isr){
|
2008-10-20 16:26:38 -06:00
|
|
|
vm_size_t nn;
|
2008-09-16 13:33:00 -06:00
|
|
|
nn = n;
|
2010-03-20 23:37:23 -06:00
|
|
|
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);
|
2008-09-16 13:33:00 -06:00
|
|
|
return -1;
|
2010-03-20 23:37:23 -06:00
|
|
|
}
|
2008-09-16 13:33:00 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-26 18:20:57 -06:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// Convert Ureg offset to x86_thread_state64_t offset.
|
|
|
|
static int
|
2009-05-26 18:20:57 -06:00
|
|
|
go2darwin64(uvlong addr)
|
2008-09-16 13:33:00 -06:00
|
|
|
{
|
|
|
|
switch(addr){
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, ax):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rax);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, bx):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rbx);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, cx):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rcx);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, dx):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rdx);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, si):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rsi);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, di):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rdi);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, bp):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rbp);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, r8):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, r8);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, r9):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, r9);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, r10):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, r10);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, r11):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, r11);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, r12):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, r12);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, r13):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, r13);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, r14):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, r14);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, r15):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, r15);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, fs):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, fs);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, gs):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, gs);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, ip):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rip);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, cs):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, cs);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, flags):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rflags);
|
2009-05-26 18:20:57 -06:00
|
|
|
case offsetof(Ureg64, sp):
|
2008-09-16 13:33:00 -06:00
|
|
|
return offsetof(x86_thread_state64_t, rsp);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-05-26 18:20:57 -06:00
|
|
|
extern Mach mi386;
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// Read/write from fake register segment.
|
|
|
|
static int
|
|
|
|
machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
|
|
|
|
{
|
2009-05-26 18:20:57 -06:00
|
|
|
uint nn, count, state;
|
2008-09-16 13:33:00 -06:00
|
|
|
mach_port_t thread;
|
2008-10-20 16:26:38 -06:00
|
|
|
int reg;
|
2008-11-14 11:45:23 -07:00
|
|
|
char buf[100];
|
2008-09-16 13:33:00 -06:00
|
|
|
union {
|
2009-05-26 18:20:57 -06:00
|
|
|
x86_thread_state64_t reg64;
|
|
|
|
x86_thread_state32_t reg32;
|
2008-09-16 13:33:00 -06:00
|
|
|
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;
|
|
|
|
}
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2009-05-26 18:20:57 -06:00
|
|
|
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;
|
2008-09-16 13:33:00 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!isr && me(thread_suspend(thread)) < 0){
|
|
|
|
werrstr("thread suspend %#x: %r", thread);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-05-26 18:20:57 -06:00
|
|
|
nn = count;
|
|
|
|
if(me(thread_get_state(thread, state, (void*)u.p, &nn)) < 0){
|
2008-09-16 13:33:00 -06:00
|
|
|
if(!isr)
|
|
|
|
thread_resume(thread);
|
2008-11-14 11:45:23 -07:00
|
|
|
rerrstr(buf, sizeof buf);
|
2011-03-14 15:12:37 -06:00
|
|
|
if(strstr(buf, "send invalid dest") != nil)
|
2008-11-14 11:45:23 -07:00
|
|
|
werrstr("process exited");
|
|
|
|
else
|
|
|
|
werrstr("thread_get_state: %r");
|
2008-09-16 13:33:00 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = u.p+reg;
|
|
|
|
if(isr)
|
|
|
|
memmove(v, p, n);
|
|
|
|
else{
|
|
|
|
memmove(p, v, n);
|
2009-05-26 18:20:57 -06:00
|
|
|
nn = count;
|
|
|
|
if(me(thread_set_state(thread, state, (void*)u.p, nn)) < 0){
|
2008-09-16 13:33:00 -06:00
|
|
|
thread_resume(thread);
|
|
|
|
werrstr("thread_set_state: %r");
|
|
|
|
return -1;
|
|
|
|
}
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
if(me(thread_resume(thread)) < 0){
|
|
|
|
werrstr("thread_resume: %r");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
2009-05-26 18:20:57 -06:00
|
|
|
FLAGS_TF = 0x100 // x86 single-step processor flag
|
2008-09-16 13:33:00 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// 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))
|
2008-10-20 16:26:38 -06:00
|
|
|
return 0;
|
2008-09-16 13:33:00 -06:00
|
|
|
|
|
|
|
// Set or clear the processor single-step flag, as appropriate.
|
2009-05-26 18:20:57 -06:00
|
|
|
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;
|
|
|
|
}
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
2009-01-16 15:58:14 -07:00
|
|
|
if(nthr > 0)
|
|
|
|
addpid(thr[0].pid, 1);
|
|
|
|
for(i=0; i<nthr; i++){
|
|
|
|
if(thr[i].thread == thread){
|
|
|
|
t = &thr[i];
|
|
|
|
goto havet;
|
|
|
|
}
|
|
|
|
}
|
2008-09-16 13:33:00 -06:00
|
|
|
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]);
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-11-14 11:45:23 -07:00
|
|
|
// 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");
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// Synchronize with waitstop below.
|
|
|
|
pthread_mutex_lock(&mu);
|
|
|
|
pthread_cond_broadcast(&cond);
|
|
|
|
pthread_mutex_unlock(&mu);
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
return KERN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exception watching thread, started in addpid above.
|
|
|
|
static void*
|
|
|
|
excthread(void *v)
|
2008-08-04 18:24:25 -06:00
|
|
|
{
|
2008-09-16 13:33:00 -06:00
|
|
|
extern boolean_t exc_server();
|
|
|
|
mach_msg_server(exc_server, 2048, excport, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-14 11:45:23 -07:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// Wait for thread t to stop.
|
|
|
|
static int
|
|
|
|
waitstop(Thread *t)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&mu);
|
2008-11-14 11:45:23 -07:00
|
|
|
while(!exited && !threadstopped(t))
|
2008-09-16 13:33:00 -06:00
|
|
|
pthread_cond_wait(&cond, &mu);
|
|
|
|
pthread_mutex_unlock(&mu);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ctlproc(int id, char *msg)
|
|
|
|
{
|
|
|
|
Thread *t;
|
2008-10-20 16:26:38 -06:00
|
|
|
int status;
|
2008-09-16 13:33:00 -06:00
|
|
|
|
|
|
|
// 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);
|
2010-11-05 21:03:53 -06:00
|
|
|
if(t == nil) {
|
|
|
|
fprint(2, "ctlproc attached: addpid: %r\n");
|
2008-09-16 13:33:00 -06:00
|
|
|
return -1;
|
2010-11-05 21:03:53 -06:00
|
|
|
}
|
2008-09-16 13:33:00 -06:00
|
|
|
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.
|
2010-11-05 21:03:53 -06:00
|
|
|
if(ptrace(PT_CONTINUE, id, (caddr_t)1, 0) < 0) {
|
|
|
|
fprint(2, "ctlproc attached: ptrace continue: %r\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2008-09-16 13:33:00 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
if(strcmp(msg, "waitstop") == 0)
|
|
|
|
return waitstop(t);
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
// sysstop not available on OS X
|
|
|
|
|
|
|
|
werrstr("unknown control message");
|
2008-08-04 18:24:25 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
char*
|
|
|
|
procstatus(int id)
|
|
|
|
{
|
|
|
|
Thread *t;
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
if((t = idtotable(id)) == nil)
|
|
|
|
return "gone!";
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
if(threadstopped(t))
|
|
|
|
return "Stopped";
|
2008-09-26 15:10:33 -06:00
|
|
|
|
2008-09-16 13:33:00 -06:00
|
|
|
return "Running";
|
|
|
|
}
|
|
|
|
|