blob: e4be465a8867e01bcadc8dbca945b2e96532cffa [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "ptrace.h"
#include <asm/unistd.h>
#include "common.h"
/* If the system headers did not provide the constants, hard-code the normal
values. */
#ifndef PTRACE_EVENT_FORK
#define PTRACE_OLDSETOPTIONS 21
#define PTRACE_SETOPTIONS 0x4200
#define PTRACE_GETEVENTMSG 0x4201
/* options set using PTRACE_SETOPTIONS */
#define PTRACE_O_TRACESYSGOOD 0x00000001
#define PTRACE_O_TRACEFORK 0x00000002
#define PTRACE_O_TRACEVFORK 0x00000004
#define PTRACE_O_TRACECLONE 0x00000008
#define PTRACE_O_TRACEEXEC 0x00000010
#define PTRACE_O_TRACEVFORKDONE 0x00000020
#define PTRACE_O_TRACEEXIT 0x00000040
/* Wait extended result codes for the above trace options. */
#define PTRACE_EVENT_FORK 1
#define PTRACE_EVENT_VFORK 2
#define PTRACE_EVENT_CLONE 3
#define PTRACE_EVENT_EXEC 4
#define PTRACE_EVENT_VFORK_DONE 5
#define PTRACE_EVENT_EXIT 6
#endif /* PTRACE_EVENT_FORK */
#ifdef ARCH_HAVE_UMOVELONG
extern int arch_umovelong (Process *, void *, long *, arg_type_info *);
int
umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
return arch_umovelong (proc, addr, result, info);
}
#else
/* Read a single long from the process's memory address 'addr' */
int
umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
long pointed_to;
errno = 0;
pointed_to = ptrace (PTRACE_PEEKTEXT, proc->pid, addr, 0);
if (pointed_to == -1 && errno)
return -errno;
*result = pointed_to;
if (info) {
switch(info->type) {
case ARGTYPE_INT:
*result &= 0x00000000ffffffffUL;
default:
break;
};
}
return 0;
}
#endif
void
trace_me(void) {
debug(DEBUG_PROCESS, "trace_me: pid=%d\n", getpid());
if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {
perror("PTRACE_TRACEME");
exit(1);
}
}
int
trace_pid(pid_t pid) {
debug(DEBUG_PROCESS, "trace_pid: pid=%d\n", pid);
if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) {
return -1;
}
/* man ptrace: PTRACE_ATTACH attaches to the process specified
in pid. The child is sent a SIGSTOP, but will not
necessarily have stopped by the completion of this call;
use wait() to wait for the child to stop. */
if (waitpid (pid, NULL, 0) != pid) {
perror ("trace_pid: waitpid");
exit (1);
}
return 0;
}
void
trace_set_options(Process *proc, pid_t pid) {
if (proc->tracesysgood & 0x80)
return;
debug(DEBUG_PROCESS, "trace_set_options: pid=%d\n", pid);
long options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK |
PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE |
PTRACE_O_TRACEEXEC;
if (ptrace(PTRACE_SETOPTIONS, pid, 0, options) < 0 &&
ptrace(PTRACE_OLDSETOPTIONS, pid, 0, options) < 0) {
perror("PTRACE_SETOPTIONS");
return;
}
proc->tracesysgood |= 0x80;
}
void
untrace_pid(pid_t pid) {
debug(DEBUG_PROCESS, "untrace_pid: pid=%d\n", pid);
ptrace(PTRACE_DETACH, pid, 1, 0);
}
void
continue_after_signal(pid_t pid, int signum) {
Process *proc;
debug(DEBUG_PROCESS, "continue_after_signal: pid=%d, signum=%d", pid, signum);
proc = pid2proc(pid);
if (proc && proc->breakpoint_being_enabled) {
#if defined __sparc__ || defined __ia64___ || defined __mips__
ptrace(PTRACE_SYSCALL, pid, 0, signum);
#else
ptrace(PTRACE_SINGLESTEP, pid, 0, signum);
#endif
} else {
ptrace(PTRACE_SYSCALL, pid, 0, signum);
}
}
void
continue_process(pid_t pid) {
/* We always trace syscalls to control fork(), clone(), execve()... */
debug(DEBUG_PROCESS, "continue_process: pid=%d", pid);
ptrace(PTRACE_SYSCALL, pid, 0, 0);
}
void
continue_enabling_breakpoint(pid_t pid, Breakpoint *sbp) {
enable_breakpoint(pid, sbp);
continue_process(pid);
}
void
continue_after_breakpoint(Process *proc, Breakpoint *sbp) {
if (sbp->enabled)
disable_breakpoint(proc->pid, sbp);
set_instruction_pointer(proc, sbp->addr);
if (sbp->enabled == 0) {
continue_process(proc->pid);
} else {
debug(DEBUG_PROCESS, "continue_after_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr);
proc->breakpoint_being_enabled = sbp;
#if defined __sparc__ || defined __ia64___ || defined __mips__
/* we don't want to singlestep here */
continue_process(proc->pid);
#else
ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0);
#endif
}
}
size_t
umovebytes(Process *proc, void *addr, void *laddr, size_t len) {
union {
long a;
char c[sizeof(long)];
} a;
int started = 0;
size_t offset = 0, bytes_read = 0;
while (offset < len) {
a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0);
if (a.a == -1 && errno) {
if (started && errno == EIO)
return bytes_read;
else
return -1;
}
started = 1;
if (len - offset >= sizeof(long)) {
memcpy(laddr + offset, &a.c[0], sizeof(long));
bytes_read += sizeof(long);
}
else {
memcpy(laddr + offset, &a.c[0], len - offset);
bytes_read += (len - offset);
}
offset += sizeof(long);
}
return bytes_read;
}
/* Read a series of bytes starting at the process's memory address
'addr' and continuing until a NUL ('\0') is seen or 'len' bytes
have been read.
*/
int
umovestr(Process *proc, void *addr, int len, void *laddr) {
union {
long a;
char c[sizeof(long)];
} a;
unsigned i;
int offset = 0;
while (offset < len) {
a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0);
for (i = 0; i < sizeof(long); i++) {
if (a.c[i] && offset + (signed)i < len) {
*(char *)(laddr + offset + i) = a.c[i];
} else {
*(char *)(laddr + offset + i) = '\0';
return 0;
}
}
offset += sizeof(long);
}
*(char *)(laddr + offset) = '\0';
return 0;
}