2004-04-19 19:29:25 +00:00
|
|
|
/*
|
|
|
|
* process interface for Linux.
|
|
|
|
*
|
|
|
|
* Uses ptrace for registers and data,
|
|
|
|
* /proc for some process status.
|
|
|
|
* There's not much point to worrying about
|
|
|
|
* byte order here -- using ptrace means
|
|
|
|
* we're running on the architecture we're debugging,
|
|
|
|
* unless truly weird stuff is going on.
|
|
|
|
*
|
|
|
|
* It is tempting to use /proc/%d/mem along with
|
|
|
|
* the sp and pc in the stat file to get a stack trace
|
|
|
|
* without attaching to the program, but unfortunately
|
|
|
|
* you can't read the mem file unless you've attached.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <u.h>
|
|
|
|
#include <sys/ptrace.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <mach.h>
|
|
|
|
#include "ureg386.h"
|
|
|
|
|
|
|
|
Mach *machcpu = &mach386;
|
|
|
|
|
|
|
|
typedef struct PtraceRegs PtraceRegs;
|
|
|
|
|
|
|
|
struct PtraceRegs
|
|
|
|
{
|
|
|
|
Regs r;
|
|
|
|
int pid;
|
|
|
|
};
|
|
|
|
|
2004-06-11 12:52:08 +00:00
|
|
|
static int ptracerw(Map*, Seg*, ulong, void*, uint, int);
|
|
|
|
static int ptraceregrw(Regs*, char*, ulong*, int);
|
2004-04-19 19:29:25 +00:00
|
|
|
|
2004-05-05 04:22:16 +00:00
|
|
|
static int attachedpids[1000];
|
|
|
|
static int nattached;
|
|
|
|
|
2004-04-19 19:29:25 +00:00
|
|
|
void
|
|
|
|
unmapproc(Map *map)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if(map == nil)
|
|
|
|
return;
|
|
|
|
for(i=0; i<map->nseg; i++)
|
|
|
|
while(i<map->nseg && map->seg[i].pid){
|
|
|
|
map->nseg--;
|
|
|
|
memmove(&map->seg[i], &map->seg[i+1],
|
|
|
|
(map->nseg-i)*sizeof(map->seg[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
mapproc(int pid, Map *map, Regs **rp)
|
|
|
|
{
|
2004-05-05 04:22:16 +00:00
|
|
|
int i;
|
2004-04-19 19:29:25 +00:00
|
|
|
Seg s;
|
|
|
|
PtraceRegs *r;
|
|
|
|
|
2004-05-05 04:22:16 +00:00
|
|
|
if(nattached==1 && attachedpids[0] == pid)
|
|
|
|
goto already;
|
|
|
|
if(nattached)
|
|
|
|
detachproc(attachedpids[0]);
|
|
|
|
|
|
|
|
for(i=0; i<nattached; i++)
|
|
|
|
if(attachedpids[i]==pid)
|
|
|
|
goto already;
|
|
|
|
if(nattached == nelem(attachedpids)){
|
|
|
|
werrstr("attached to too many processes");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-04-19 19:29:25 +00:00
|
|
|
if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
|
|
|
|
werrstr("ptrace attach %d: %r", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
2004-05-05 04:22:16 +00:00
|
|
|
|
2004-04-19 19:29:25 +00:00
|
|
|
if(ctlproc(pid, "waitstop") < 0){
|
2004-05-05 04:22:16 +00:00
|
|
|
fprint(2, "waitstop: %r");
|
2004-04-19 19:29:25 +00:00
|
|
|
ptrace(PTRACE_DETACH, pid, 0, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
2004-05-05 04:22:16 +00:00
|
|
|
attachedpids[nattached++] = pid;
|
2004-04-19 19:29:25 +00:00
|
|
|
|
2004-05-05 04:22:16 +00:00
|
|
|
already:
|
2004-04-19 19:29:25 +00:00
|
|
|
memset(&s, 0, sizeof s);
|
|
|
|
s.base = 0;
|
|
|
|
s.size = 0xFFFFFFFF;
|
|
|
|
s.offset = 0;
|
|
|
|
s.name = "data";
|
|
|
|
s.file = nil;
|
|
|
|
s.rw = ptracerw;
|
|
|
|
s.pid = pid;
|
2004-05-05 04:22:16 +00:00
|
|
|
if(addseg(map, s) < 0){
|
|
|
|
fprint(2, "addseg: %r\n");
|
2004-04-19 19:29:25 +00:00
|
|
|
return -1;
|
2004-05-05 04:22:16 +00:00
|
|
|
}
|
2004-04-19 19:29:25 +00:00
|
|
|
|
2004-05-05 04:22:16 +00:00
|
|
|
if((r = mallocz(sizeof(PtraceRegs), 1)) == nil){
|
|
|
|
fprint(2, "mallocz: %r\n");
|
2004-04-19 19:29:25 +00:00
|
|
|
return -1;
|
2004-05-05 04:22:16 +00:00
|
|
|
}
|
2004-04-19 19:29:25 +00:00
|
|
|
r->r.rw = ptraceregrw;
|
|
|
|
r->pid = pid;
|
|
|
|
*rp = (Regs*)r;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
detachproc(int pid)
|
|
|
|
{
|
2004-05-05 04:22:16 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for(i=0; i<nattached; i++){
|
|
|
|
if(attachedpids[i] == pid){
|
|
|
|
attachedpids[i] = attachedpids[--nattached];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-04-19 19:29:25 +00:00
|
|
|
return ptrace(PTRACE_DETACH, pid, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2004-06-11 12:52:08 +00:00
|
|
|
ptracerw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
|
2004-04-19 19:29:25 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u32int u;
|
|
|
|
uchar buf[4];
|
|
|
|
|
|
|
|
addr += seg->base;
|
|
|
|
for(i=0; i<n; i+=4){
|
|
|
|
if(isr){
|
|
|
|
errno = 0;
|
|
|
|
u = ptrace(PTRACE_PEEKDATA, seg->pid, addr+i, 0);
|
|
|
|
if(errno)
|
|
|
|
goto ptraceerr;
|
|
|
|
if(n-i >= 4)
|
|
|
|
*(u32int*)((char*)v+i) = u;
|
|
|
|
else{
|
|
|
|
*(u32int*)buf = u;
|
|
|
|
memmove((char*)v+i, buf, n-i);
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if(n-i >= 4)
|
|
|
|
u = *(u32int*)((char*)v+i);
|
|
|
|
else{
|
|
|
|
errno = 0;
|
|
|
|
u = ptrace(PTRACE_PEEKDATA, seg->pid, addr+i, 0);
|
|
|
|
if(errno)
|
|
|
|
return -1;
|
|
|
|
*(u32int*)buf = u;
|
|
|
|
memmove(buf, (char*)v+i, n-i);
|
|
|
|
u = *(u32int*)buf;
|
|
|
|
}
|
|
|
|
if(ptrace(PTRACE_POKEDATA, seg->pid, addr+i, &u) < 0)
|
|
|
|
goto ptraceerr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ptraceerr:
|
|
|
|
werrstr("ptrace: %r");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char* linuxregs[] = {
|
|
|
|
"BX",
|
|
|
|
"CX",
|
|
|
|
"DX",
|
|
|
|
"SI",
|
|
|
|
"DI",
|
|
|
|
"BP",
|
|
|
|
"AX",
|
|
|
|
"DS",
|
|
|
|
"ES",
|
|
|
|
"FS",
|
|
|
|
"GS",
|
|
|
|
"OAX",
|
|
|
|
"PC",
|
|
|
|
"CS",
|
|
|
|
"EFLAGS",
|
|
|
|
"SP",
|
|
|
|
"SS",
|
|
|
|
};
|
|
|
|
|
|
|
|
static ulong
|
|
|
|
reg2linux(char *reg)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for(i=0; i<nelem(linuxregs); i++)
|
|
|
|
if(strcmp(linuxregs[i], reg) == 0)
|
|
|
|
return 4*i;
|
|
|
|
return ~(ulong)0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2004-06-11 12:52:08 +00:00
|
|
|
ptraceregrw(Regs *regs, char *name, ulong *val, int isr)
|
2004-04-19 19:29:25 +00:00
|
|
|
{
|
|
|
|
int pid;
|
|
|
|
ulong addr;
|
|
|
|
u32int u;
|
|
|
|
|
|
|
|
pid = ((PtraceRegs*)regs)->pid;
|
|
|
|
addr = reg2linux(name);
|
|
|
|
if(~addr == 0){
|
|
|
|
if(isr){
|
2004-06-11 12:52:08 +00:00
|
|
|
*val = ~(ulong)0;
|
2004-04-19 19:29:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
werrstr("register not available");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(isr){
|
|
|
|
errno = 0;
|
|
|
|
u = ptrace(PTRACE_PEEKUSER, pid, addr, 0);
|
|
|
|
if(errno)
|
|
|
|
goto ptraceerr;
|
|
|
|
*val = u;
|
|
|
|
}else{
|
|
|
|
u = *val;
|
|
|
|
if(ptrace(PTRACE_POKEUSER, pid, addr, &u) < 0)
|
|
|
|
goto ptraceerr;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ptraceerr:
|
|
|
|
werrstr("ptrace: %r");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
isstopped(int pid)
|
|
|
|
{
|
|
|
|
char buf[1024];
|
|
|
|
int fd, n;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
snprint(buf, sizeof buf, "/proc/%d/stat", pid);
|
|
|
|
if((fd = open(buf, OREAD)) < 0)
|
|
|
|
return 0;
|
|
|
|
n = read(fd, buf, sizeof buf-1);
|
|
|
|
close(fd);
|
|
|
|
if(n <= 0)
|
|
|
|
return 0;
|
|
|
|
buf[n] = 0;
|
|
|
|
|
|
|
|
/* command name is in parens, no parens afterward */
|
|
|
|
p = strrchr(buf, ')');
|
|
|
|
if(p == nil || *++p != ' ')
|
|
|
|
return 0;
|
|
|
|
++p;
|
|
|
|
|
|
|
|
/* next is state - T is stopped for tracing */
|
|
|
|
return *p == 'T';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* /proc/pid/stat contains
|
|
|
|
pid
|
|
|
|
command in parens
|
|
|
|
0. state
|
|
|
|
1. ppid
|
|
|
|
2. pgrp
|
|
|
|
3. session
|
|
|
|
4. tty_nr
|
|
|
|
5. tpgid
|
|
|
|
6. flags (math=4, traced=10)
|
|
|
|
7. minflt
|
|
|
|
8. cminflt
|
|
|
|
9. majflt
|
|
|
|
10. cmajflt
|
|
|
|
11. utime
|
|
|
|
12. stime
|
|
|
|
13. cutime
|
|
|
|
14. cstime
|
|
|
|
15. priority
|
|
|
|
16. nice
|
|
|
|
17. 0
|
|
|
|
18. itrealvalue
|
|
|
|
19. starttime
|
|
|
|
20. vsize
|
|
|
|
21. rss
|
|
|
|
22. rlim
|
|
|
|
23. startcode
|
|
|
|
24. endcode
|
|
|
|
25. startstack
|
|
|
|
26. kstkesp
|
|
|
|
27. kstkeip
|
|
|
|
28. pending signal bitmap
|
|
|
|
29. blocked signal bitmap
|
|
|
|
30. ignored signal bitmap
|
|
|
|
31. caught signal bitmap
|
|
|
|
32. wchan
|
|
|
|
33. nswap
|
|
|
|
34. cnswap
|
|
|
|
35. exit_signal
|
|
|
|
36. processor
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
procnotes(int pid, char ***pnotes)
|
|
|
|
{
|
|
|
|
char buf[1024], *f[40];
|
|
|
|
int fd, i, n, nf;
|
|
|
|
char *p, *s, **notes;
|
|
|
|
ulong sigs;
|
|
|
|
extern char *_p9sigstr(int, char*);
|
|
|
|
|
|
|
|
*pnotes = nil;
|
|
|
|
snprint(buf, sizeof buf, "/proc/%d/stat", pid);
|
|
|
|
if((fd = open(buf, OREAD)) < 0){
|
|
|
|
fprint(2, "open %s: %r\n", buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
n = read(fd, buf, sizeof buf-1);
|
|
|
|
close(fd);
|
|
|
|
if(n <= 0){
|
|
|
|
fprint(2, "read %s: %r\n", buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
buf[n] = 0;
|
|
|
|
|
|
|
|
/* command name is in parens, no parens afterward */
|
|
|
|
p = strrchr(buf, ')');
|
|
|
|
if(p == nil || *++p != ' '){
|
|
|
|
fprint(2, "bad format in /proc/%d/stat\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
++p;
|
|
|
|
|
|
|
|
nf = tokenize(p, f, nelem(f));
|
|
|
|
if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
|
|
|
|
strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
|
|
|
|
strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
|
|
|
|
if(nf <= 28)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
|
|
|
|
if(sigs == 0){
|
|
|
|
*pnotes = nil;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
notes = mallocz(32*sizeof(char*), 0);
|
|
|
|
if(notes == nil)
|
|
|
|
return -1;
|
|
|
|
n = 0;
|
|
|
|
for(i=0; i<32; i++){
|
|
|
|
if((sigs&(1<<i)) == 0)
|
|
|
|
continue;
|
|
|
|
if((s = _p9sigstr(i, nil)) == nil)
|
|
|
|
continue;
|
|
|
|
notes[n++] = s;
|
|
|
|
}
|
|
|
|
*pnotes = notes;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef waitpid
|
|
|
|
|
|
|
|
int
|
|
|
|
ctlproc(int pid, char *msg)
|
|
|
|
{
|
|
|
|
int p, status;
|
|
|
|
|
|
|
|
if(strcmp(msg, "hang") == 0){
|
|
|
|
if(pid == getpid())
|
|
|
|
return ptrace(PTRACE_TRACEME, 0, 0, 0);
|
|
|
|
werrstr("can only hang self");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(strcmp(msg, "kill") == 0)
|
|
|
|
return ptrace(PTRACE_KILL, pid, 0, 0);
|
|
|
|
if(strcmp(msg, "startstop") == 0){
|
|
|
|
if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
|
|
|
|
return -1;
|
|
|
|
goto waitstop;
|
|
|
|
}
|
|
|
|
if(strcmp(msg, "sysstop") == 0){
|
|
|
|
if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
|
|
|
|
return -1;
|
|
|
|
goto waitstop;
|
|
|
|
}
|
|
|
|
if(strcmp(msg, "stop") == 0){
|
|
|
|
if(kill(pid, SIGSTOP) < 0)
|
|
|
|
return -1;
|
|
|
|
goto waitstop;
|
|
|
|
}
|
|
|
|
if(strcmp(msg, "waitstop") == 0){
|
|
|
|
waitstop:
|
|
|
|
if(isstopped(pid))
|
|
|
|
return 0;
|
|
|
|
for(;;){
|
2004-05-05 04:22:16 +00:00
|
|
|
p = waitpid(pid, &status, WUNTRACED|__WALL);
|
|
|
|
if(p <= 0){
|
|
|
|
if(errno == ECHILD){
|
|
|
|
if(isstopped(pid))
|
|
|
|
return 0;
|
|
|
|
}
|
2004-04-19 19:29:25 +00:00
|
|
|
return -1;
|
2004-05-05 04:22:16 +00:00
|
|
|
}
|
2004-04-19 19:29:25 +00:00
|
|
|
if(WIFEXITED(status) || WIFSTOPPED(status))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(strcmp(msg, "start") == 0)
|
|
|
|
return ptrace(PTRACE_CONT, pid, 0, 0);
|
|
|
|
werrstr("unknown control message '%s'", msg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
|
|
|
proctextfile(int pid)
|
|
|
|
{
|
|
|
|
static char buf[1024], pbuf[128];
|
|
|
|
|
|
|
|
snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
|
|
|
|
if(readlink(pbuf, buf, sizeof buf) >= 0)
|
|
|
|
return buf;
|
|
|
|
if(access(pbuf, AEXIST) >= 0)
|
|
|
|
return pbuf;
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
snprint(buf, sizeof buf, "/proc/%d/maps", pid);
|
|
|
|
if((b = Bopen(buf, OREAD)) == nil){
|
|
|
|
werrstr("open %s: %r", buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
|
|
|
|
08056000-08058000 rw-p 0000d000 03:0c 64593 /usr/sbin/gpm
|
|
|
|
08058000-0805b000 rwxp 00000000 00:00 0
|
|
|
|
40000000-40013000 r-xp 00000000 03:0c 4165 /lib/ld-2.2.4.so
|
|
|
|
40013000-40015000 rw-p 00012000 03:0c 4165 /lib/ld-2.2.4.so
|
|
|
|
4001f000-40135000 r-xp 00000000 03:0c 45494 /lib/libc-2.2.4.so
|
|
|
|
40135000-4013e000 rw-p 00115000 03:0c 45494 /lib/libc-2.2.4.so
|
|
|
|
4013e000-40142000 rw-p 00000000 00:00 0
|
|
|
|
bffff000-c0000000 rwxp 00000000 00:00 0
|
|
|
|
*/
|
|
|
|
|
|
|
|
file = nil;
|
|
|
|
while((p = Brdline(b, '\n')) != nil){
|
|
|
|
p[Blinelen(b)-1] = 0;
|
|
|
|
memset(f, 0, sizeof f);
|
|
|
|
if((nf = getfields(p, f, 6, 1, " ")) < 5)
|
|
|
|
continue;
|
|
|
|
base = strtoul(f[0], &p, 16);
|
|
|
|
if(*p != '-')
|
|
|
|
continue;
|
|
|
|
end = strtoul(p+1, &p, 16);
|
|
|
|
if(*p != 0)
|
|
|
|
continue;
|
|
|
|
offset = strtoul(f[2], &p, 16);
|
|
|
|
if(*p != 0)
|
|
|
|
continue;
|
|
|
|
if(nf == 6)
|
|
|
|
file = f[5];
|
|
|
|
zero = atoi(f[4]) == 0;
|
|
|
|
print("%lux-%lux %lux %s %s\n", base, end, offset, zero ? "data" : "text", file ? file : "");
|
|
|
|
s.base = base;
|
|
|
|
s.size = end - base;
|
|
|
|
s.offset = offset;
|
|
|
|
s.name = zero ? "data" : "text";
|
|
|
|
s.file = strdup(file);
|
|
|
|
s.rw = ptracerw;
|
|
|
|
s.pid = pid;
|
|
|
|
if(addseg(map, s) < 0){
|
|
|
|
Bterm(b);
|
|
|
|
ptrace(PTRACE_DETACH, pid, 0, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Bterm(b);
|
|
|
|
#endif
|
|
|
|
|