* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $Id$
*/
#include "defs.h"
-
-#include <signal.h>
-#include <sys/syscall.h>
#include <sys/user.h>
#include <sys/param.h>
#include <fcntl.h>
#if HAVE_SYS_UIO_H
-#include <sys/uio.h>
-#endif
-#ifdef SUNOS4
-#include <machine/reg.h>
-#include <a.out.h>
-#include <link.h>
-#endif /* SUNOS4 */
-
-#if defined(linux) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 1))
-#include <linux/ptrace.h>
+# include <sys/uio.h>
#endif
-#if defined(LINUX) && defined(IA64)
+#if defined(IA64)
# include <asm/ptrace_offsets.h>
# include <asm/rse.h>
#endif
#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
+# include <sys/reg.h>
# define PTRACE_PEEKUSR PTRACE_PEEKUSER
#elif defined(HAVE_LINUX_PTRACE_H)
-#undef PTRACE_SYSCALL
+# undef PTRACE_SYSCALL
# ifdef HAVE_STRUCT_IA64_FPREG
# define ia64_fpreg XXX_ia64_fpreg
# endif
# ifdef HAVE_STRUCT_PT_ALL_USER_REGS
# define pt_all_user_regs XXX_pt_all_user_regs
# endif
-#include <linux/ptrace.h>
+# include <linux/ptrace.h>
# undef ia64_fpreg
# undef pt_all_user_regs
#endif
-#ifdef SUNOS4_KERNEL_ARCH_KLUDGE
-#include <sys/utsname.h>
-#endif /* SUNOS4_KERNEL_ARCH_KLUDGE */
-
-#if defined(LINUXSPARC) && defined (SPARC64)
-# undef PTRACE_GETREGS
-# define PTRACE_GETREGS PTRACE_GETREGS64
-# undef PTRACE_SETREGS
-# define PTRACE_SETREGS PTRACE_SETREGS64
-#endif
+int
+string_to_uint(const char *str)
+{
+ char *error;
+ long value;
-/* macros */
-#ifndef MAX
-#define MAX(a,b) (((a) > (b)) ? (a) : (b))
-#endif
-#ifndef MIN
-#define MIN(a,b) (((a) < (b)) ? (a) : (b))
-#endif
+ if (!*str)
+ return -1;
+ errno = 0;
+ value = strtol(str, &error, 10);
+ if (errno || *error || value < 0 || (long)(int)value != value)
+ return -1;
+ return (int)value;
+}
int
-tv_nz(a)
-struct timeval *a;
+tv_nz(struct timeval *a)
{
return a->tv_sec || a->tv_usec;
}
int
-tv_cmp(a, b)
-struct timeval *a, *b;
+tv_cmp(struct timeval *a, struct timeval *b)
{
if (a->tv_sec < b->tv_sec
|| (a->tv_sec == b->tv_sec && a->tv_usec < b->tv_usec))
}
double
-tv_float(tv)
-struct timeval *tv;
+tv_float(struct timeval *tv)
{
return tv->tv_sec + tv->tv_usec/1000000.0;
}
void
-tv_add(tv, a, b)
-struct timeval *tv, *a, *b;
+tv_add(struct timeval *tv, struct timeval *a, struct timeval *b)
{
tv->tv_sec = a->tv_sec + b->tv_sec;
tv->tv_usec = a->tv_usec + b->tv_usec;
}
void
-tv_sub(tv, a, b)
-struct timeval *tv, *a, *b;
+tv_sub(struct timeval *tv, struct timeval *a, struct timeval *b)
{
tv->tv_sec = a->tv_sec - b->tv_sec;
tv->tv_usec = a->tv_usec - b->tv_usec;
}
void
-tv_div(tv, a, n)
-struct timeval *tv, *a;
-int n;
+tv_div(struct timeval *tv, struct timeval *a, int n)
{
tv->tv_usec = (a->tv_sec % n * 1000000 + a->tv_usec + n / 2) / n;
tv->tv_sec = a->tv_sec / n + tv->tv_usec / 1000000;
}
void
-tv_mul(tv, a, n)
-struct timeval *tv, *a;
-int n;
+tv_mul(struct timeval *tv, struct timeval *a, int n)
{
tv->tv_usec = a->tv_usec * n;
tv->tv_sec = a->tv_sec * n + tv->tv_usec / 1000000;
return NULL;
}
-/*
- * Generic ptrace wrapper which tracks ESRCH errors
- * by setting tcp->ptrace_errno to ESRCH.
- *
- * We assume that ESRCH indicates likely process death (SIGKILL?),
- * modulo bugs where process somehow ended up not stopped.
- * Unfortunately kernel uses ESRCH for that case too. Oh well.
- *
- * Currently used by upeek() only.
- * TODO: use this in all other ptrace() calls while decoding.
- */
-long
-do_ptrace(int request, struct tcb *tcp, void *addr, void *data)
+#if !defined HAVE_STPCPY
+char *
+stpcpy(char *dst, const char *src)
{
- long l;
-
- errno = 0;
- l = ptrace(request, tcp->pid, addr, (long) data);
- /* Non-ESRCH errors might be our invalid reg/mem accesses,
- * we do not record them. */
- if (errno == ESRCH)
- tcp->ptrace_errno = ESRCH;
- return l;
-}
-
-/*
- * Used when we want to unblock stopped traced process.
- * Should be only used with PTRACE_CONT, PTRACE_DETACH and PTRACE_SYSCALL.
- * Returns 0 on success or if error was ESRCH
- * (presumably process was killed while we talk to it).
- * Otherwise prints error message and returns -1.
- */
-int
-ptrace_restart(int op, struct tcb *tcp, int sig)
-{
- int err;
- const char *msg;
-
- errno = 0;
- ptrace(op, tcp->pid, (void *) 1, (long) sig);
- err = errno;
- if (!err || err == ESRCH)
- return 0;
-
- tcp->ptrace_errno = err;
- msg = "SYSCALL";
- if (op == PTRACE_CONT)
- msg = "CONT";
- if (op == PTRACE_DETACH)
- msg = "DETACH";
- fprintf(stderr, "strace: ptrace(PTRACE_%s,1,%d): %s\n",
- msg, sig, strerror(err));
- return -1;
+ while ((*dst = *src++) != '\0')
+ dst++;
+ return dst;
}
+#endif
/*
* Print entry in struct xlat table, if there.
const char *str = xlookup(xlat, val);
if (str)
- tprintf("%s", str);
+ tprints(str);
else
tprintf("%#x /* %s */", val, dflt);
}
-#if HAVE_LONG_LONG
/*
- * Print 64bit argument at position llarg and return the index of the next
+ * Print 64bit argument at position arg_no and return the index of the next
* argument.
*/
int
-printllval(struct tcb *tcp, const char *format, int llarg)
+printllval(struct tcb *tcp, const char *format, int arg_no)
{
-# if defined(FREEBSD) \
- || (defined(LINUX) && defined(POWERPC) && !defined(POWERPC64)) \
- || defined (LINUX_MIPSO32)
- /* Align 64bit argument to 64bit boundary. */
- if (llarg % 2) llarg++;
+#if SIZEOF_LONG > 4 && SIZEOF_LONG == SIZEOF_LONG_LONG
+# if SUPPORTED_PERSONALITIES > 1
+ if (current_wordsize > 4) {
# endif
-# if defined LINUX && (defined X86_64 || defined POWERPC64)
- if (current_personality == 0) {
- tprintf(format, tcp->u_arg[llarg]);
- llarg++;
+ tprintf(format, tcp->u_arg[arg_no]);
+ arg_no++;
+# if SUPPORTED_PERSONALITIES > 1
} else {
-# ifdef POWERPC64
- /* Align 64bit argument to 64bit boundary. */
- if (llarg % 2) llarg++;
+# if defined(AARCH64) || defined(POWERPC64)
+ /* Align arg_no to the next even number. */
+ arg_no = (arg_no + 1) & 0xe;
# endif
- tprintf(format, LONG_LONG(tcp->u_arg[llarg], tcp->u_arg[llarg + 1]));
- llarg += 2;
+ tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
+ arg_no += 2;
+ }
+# endif /* SUPPORTED_PERSONALITIES */
+#elif SIZEOF_LONG > 4
+# error Unsupported configuration: SIZEOF_LONG > 4 && SIZEOF_LONG_LONG > SIZEOF_LONG
+#elif defined LINUX_MIPSN32
+ tprintf(format, tcp->ext_arg[arg_no]);
+ arg_no++;
+#elif defined X32
+ if (current_personality == 0) {
+ tprintf(format, tcp->ext_arg[arg_no]);
+ arg_no++;
+ } else {
+ tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
+ arg_no += 2;
}
-# elif defined IA64 || defined ALPHA
- tprintf(format, tcp->u_arg[llarg]);
- llarg++;
-# elif defined LINUX_MIPSN32
- tprintf(format, tcp->ext_arg[llarg]);
- llarg++;
-# else
- tprintf(format, LONG_LONG(tcp->u_arg[llarg], tcp->u_arg[llarg + 1]));
- llarg += 2;
+#else
+# if defined __ARM_EABI__ || \
+ defined LINUX_MIPSO32 || \
+ defined POWERPC || \
+ defined XTENSA
+ /* Align arg_no to the next even number. */
+ arg_no = (arg_no + 1) & 0xe;
# endif
- return llarg;
-}
+ tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
+ arg_no += 2;
#endif
+ return arg_no;
+}
+
/*
* Interpret `xlat' as an array of flags
* print the entries whose bits are on in `flags'
* return # of flags printed.
*/
-int
-addflags(xlat, flags)
-const struct xlat *xlat;
-int flags;
+void
+addflags(const struct xlat *xlat, int flags)
{
- int n;
-
- for (n = 0; xlat->str; xlat++) {
+ for (; xlat->str; xlat++) {
if (xlat->val && (flags & xlat->val) == xlat->val) {
tprintf("|%s", xlat->str);
flags &= ~xlat->val;
- n++;
}
}
if (flags) {
tprintf("|%#x", flags);
- n++;
}
- return n;
}
/*
- * Interpret `xlat' as an array of flags/
+ * Interpret `xlat' as an array of flags.
* Print to static string the entries whose bits are on in `flags'
* Return static string.
*/
sprintflags(const char *prefix, const struct xlat *xlat, int flags)
{
static char outstr[1024];
+ char *outptr;
int found = 0;
- strcpy(outstr, prefix);
+ outptr = stpcpy(outstr, prefix);
for (; xlat->str; xlat++) {
if ((flags & xlat->val) == xlat->val) {
if (found)
- strcat(outstr, "|");
- strcat(outstr, xlat->str);
- flags &= ~xlat->val;
+ *outptr++ = '|';
+ outptr = stpcpy(outptr, xlat->str);
found = 1;
+ flags &= ~xlat->val;
+ if (!flags)
+ break;
}
}
if (flags) {
if (found)
- strcat(outstr, "|");
- sprintf(outstr + strlen(outstr), "%#x", flags);
+ *outptr++ = '|';
+ outptr += sprintf(outptr, "%#x", flags);
}
return outstr;
}
int
-printflags(xlat, flags, dflt)
-const struct xlat *xlat;
-int flags;
-const char *dflt;
+printflags(const struct xlat *xlat, int flags, const char *dflt)
{
int n;
- char *sep;
+ const char *sep;
if (flags == 0 && xlat->val == 0) {
- tprintf("%s", xlat->str);
+ tprints(xlat->str);
return 1;
}
tprintf(" /* %s */", dflt);
} else {
if (dflt)
- tprintf("0");
+ tprints("0");
}
}
}
void
-printnum(tcp, addr, fmt)
-struct tcb *tcp;
-long addr;
-char *fmt;
+printnum(struct tcb *tcp, long addr, const char *fmt)
{
long num;
if (!addr) {
- tprintf("NULL");
+ tprints("NULL");
return;
}
if (umove(tcp, addr, &num) < 0) {
tprintf("%#lx", addr);
return;
}
- tprintf("[");
+ tprints("[");
tprintf(fmt, num);
- tprintf("]");
+ tprints("]");
}
void
-printnum_int(tcp, addr, fmt)
-struct tcb *tcp;
-long addr;
-char *fmt;
+printnum_int(struct tcb *tcp, long addr, const char *fmt)
{
int num;
if (!addr) {
- tprintf("NULL");
+ tprints("NULL");
return;
}
if (umove(tcp, addr, &num) < 0) {
tprintf("%#lx", addr);
return;
}
- tprintf("[");
+ tprints("[");
tprintf(fmt, num);
- tprintf("]");
+ tprints("]");
}
void
-printuid(text, uid)
-const char *text;
-unsigned long uid;
+printfd(struct tcb *tcp, int fd)
{
- tprintf("%s", text);
- tprintf((uid == -1) ? "%ld" : "%lu", uid);
+ char path[PATH_MAX + 1];
+
+ if (show_fd_path && getfdpath(tcp, fd, path, sizeof(path)) >= 0)
+ tprintf("%d<%s>", fd, path);
+ else
+ tprintf("%d", fd);
}
-static char path[MAXPATHLEN + 1];
+void
+printuid(const char *text, unsigned long uid)
+{
+ tprintf((uid == -1) ? "%s%ld" : "%s%lu", text, uid);
+}
/*
* Quote string `instr' of length `size'
* Write up to (3 + `size' * 4) bytes to `outstr' buffer.
- * If `len' < 0, treat `instr' as a NUL-terminated string
+ * If `len' is -1, treat `instr' as a NUL-terminated string
* and quote at most (`size' - 1) bytes.
+ *
+ * Returns 0 if len == -1 and NUL was seen, 1 otherwise.
+ * Note that if len >= 0, always returns 1.
*/
-static int
-string_quote(const char *instr, char *outstr, int len, int size)
+int
+string_quote(const char *instr, char *outstr, long len, int size)
{
const unsigned char *ustr = (const unsigned char *) instr;
char *s = outstr;
- int usehex = 0, c, i;
+ int usehex, c, i, eol;
+ eol = 0x100; /* this can never match a char */
+ if (len == -1) {
+ size--;
+ eol = '\0';
+ }
+
+ usehex = 0;
if (xflag > 1)
usehex = 1;
else if (xflag) {
for (i = 0; i < size; ++i) {
c = ustr[i];
/* Check for NUL-terminated string. */
- if (len < 0) {
- if (c == '\0')
- break;
- /* Quote at most size - 1 bytes. */
- if (i == size - 1)
- continue;
+ if (c == eol)
+ break;
+
+ /* Force hex unless c is printable or whitespace */
+ if (c > 0x7e) {
+ usehex = 1;
+ break;
}
- if (!isprint(c) && !isspace(c)) {
+ /* In ASCII isspace is only these chars: "\t\n\v\f\r".
+ * They happen to have ASCII codes 9,10,11,12,13.
+ */
+ if (c < ' ' && (unsigned)(c - 9) >= 5) {
usehex = 1;
break;
}
for (i = 0; i < size; ++i) {
c = ustr[i];
/* Check for NUL-terminated string. */
- if (len < 0) {
- if (c == '\0')
- break;
- /* Quote at most size - 1 bytes. */
- if (i == size - 1)
- continue;
- }
- sprintf(s, "\\x%02x", c);
- s += 4;
+ if (c == eol)
+ goto asciz_ended;
+ *s++ = '\\';
+ *s++ = 'x';
+ *s++ = "0123456789abcdef"[c >> 4];
+ *s++ = "0123456789abcdef"[c & 0xf];
}
} else {
for (i = 0; i < size; ++i) {
c = ustr[i];
/* Check for NUL-terminated string. */
- if (len < 0) {
- if (c == '\0')
- break;
- /* Quote at most size - 1 bytes. */
- if (i == size - 1)
- continue;
- }
+ if (c == eol)
+ goto asciz_ended;
switch (c) {
case '\"': case '\\':
*s++ = '\\';
*s++ = 'v';
break;
default:
- if (isprint(c))
+ if (c >= ' ' && c <= 0x7e)
*s++ = c;
- else if (i + 1 < size
- && isdigit(ustr[i + 1])) {
- sprintf(s, "\\%03o", c);
- s += 4;
- } else {
- sprintf(s, "\\%o", c);
- s += strlen(s);
+ else {
+ /* Print \octal */
+ *s++ = '\\';
+ if (i + 1 < size
+ && ustr[i + 1] >= '0'
+ && ustr[i + 1] <= '9'
+ ) {
+ /* Print \ooo */
+ *s++ = '0' + (c >> 6);
+ *s++ = '0' + ((c >> 3) & 0x7);
+ } else {
+ /* Print \[[o]o]o */
+ if ((c >> 3) != 0) {
+ if ((c >> 6) != 0)
+ *s++ = '0' + (c >> 6);
+ *s++ = '0' + ((c >> 3) & 0x7);
+ }
+ }
+ *s++ = '0' + (c & 0x7);
}
break;
}
*s++ = '\"';
*s = '\0';
- /* Return nonzero if the string was unterminated. */
- return i == size;
+ /* Return zero if we printed entire ASCIZ string (didn't truncate it) */
+ if (len == -1 && ustr[i] == '\0') {
+ /* We didn't see NUL yet (otherwise we'd jump to 'asciz_ended')
+ * but next char is NUL.
+ */
+ return 0;
+ }
+
+ return 1;
+
+ asciz_ended:
+ *s++ = '\"';
+ *s = '\0';
+ /* Return zero: we printed entire ASCIZ string (didn't truncate it) */
+ return 0;
}
/*
void
printpathn(struct tcb *tcp, long addr, int n)
{
+ char path[MAXPATHLEN + 1];
+ int nul_seen;
+
if (!addr) {
- tprintf("NULL");
+ tprints("NULL");
return;
}
- /* Cap path length to the path buffer size,
- and NUL-terminate the buffer. */
+ /* Cap path length to the path buffer size */
if (n > sizeof path - 1)
n = sizeof path - 1;
- path[n] = '\0';
/* Fetch one byte more to find out whether path length > n. */
- if (umovestr(tcp, addr, n + 1, path) < 0)
+ nul_seen = umovestr(tcp, addr, n + 1, path);
+ if (nul_seen < 0)
tprintf("%#lx", addr);
else {
- static char outstr[4*(sizeof path - 1) + sizeof "\"...\""];
- int trunc = (path[n] != '\0');
-
- if (trunc)
- path[n] = '\0';
- (void) string_quote(path, outstr, -1, n + 1);
- if (trunc)
- strcat(outstr, "...");
- tprintf("%s", outstr);
+ char *outstr;
+
+ path[n] = '\0';
+ n++;
+ outstr = alloca(4 * n); /* 4*(n-1) + 3 for quotes and NUL */
+ string_quote(path, outstr, -1, n);
+ tprints(outstr);
+ if (!nul_seen)
+ tprints("...");
}
}
void
printpath(struct tcb *tcp, long addr)
{
- printpathn(tcp, addr, sizeof path - 1);
+ /* Size must correspond to char path[] size in printpathn */
+ printpathn(tcp, addr, MAXPATHLEN);
}
/*
* If string length exceeds `max_strlen', append `...' to the output.
*/
void
-printstr(struct tcb *tcp, long addr, int len)
+printstr(struct tcb *tcp, long addr, long len)
{
static char *str = NULL;
static char *outstr;
int size;
+ int ellipsis;
if (!addr) {
- tprintf("NULL");
+ tprints("NULL");
return;
}
/* Allocate static buffers if they are not allocated yet. */
- if (!str)
+ if (!str) {
+ unsigned int outstr_size = 4 * max_strlen + /*for quotes and NUL:*/ 3;
+
+ if (outstr_size / 4 != max_strlen)
+ die_out_of_memory();
str = malloc(max_strlen + 1);
- if (!outstr)
- outstr = malloc(4 * max_strlen + sizeof "\"...\"");
- if (!str || !outstr) {
- fprintf(stderr, "out of memory\n");
- tprintf("%#lx", addr);
- return;
+ if (!str)
+ die_out_of_memory();
+ outstr = malloc(outstr_size);
+ if (!outstr)
+ die_out_of_memory();
}
- if (len < 0) {
+ if (len == -1) {
/*
* Treat as a NUL-terminated string: fetch one byte more
* because string_quote() quotes one byte less.
*/
size = max_strlen + 1;
- str[max_strlen] = '\0';
if (umovestr(tcp, addr, size, str) < 0) {
tprintf("%#lx", addr);
return;
}
}
else {
- size = MIN(len, max_strlen);
+ size = max_strlen;
+ if (size > (unsigned long)len)
+ size = (unsigned long)len;
if (umoven(tcp, addr, size, str) < 0) {
tprintf("%#lx", addr);
return;
}
}
- if (string_quote(str, outstr, len, size) &&
- (len < 0 || len > max_strlen))
- strcat(outstr, "...");
+ /* If string_quote didn't see NUL and (it was supposed to be ASCIZ str
+ * or we were requested to print more than -s NUM chars)...
+ */
+ ellipsis = (string_quote(str, outstr, len, size) &&
+ (len < 0 || len > max_strlen));
- tprintf("%s", outstr);
+ tprints(outstr);
+ if (ellipsis)
+ tprints("...");
}
#if HAVE_SYS_UIO_H
void
-dumpiov(tcp, len, addr)
-struct tcb * tcp;
-int len;
-long addr;
+dumpiov(struct tcb *tcp, int len, long addr)
{
-#if defined(LINUX) && SUPPORTED_PERSONALITIES > 1
+#if SUPPORTED_PERSONALITIES > 1
union {
struct { u_int32_t base; u_int32_t len; } *iov32;
struct { u_int64_t base; u_int64_t len; } *iov64;
} iovu;
#define iov iovu.iov64
#define sizeof_iov \
- (personality_wordsize[current_personality] == 4 \
- ? sizeof(*iovu.iov32) : sizeof(*iovu.iov64))
+ (current_wordsize == 4 ? sizeof(*iovu.iov32) : sizeof(*iovu.iov64))
#define iov_iov_base(i) \
- (personality_wordsize[current_personality] == 4 \
- ? (u_int64_t) iovu.iov32[i].base : iovu.iov64[i].base)
+ (current_wordsize == 4 ? (uint64_t) iovu.iov32[i].base : iovu.iov64[i].base)
#define iov_iov_len(i) \
- (personality_wordsize[current_personality] == 4 \
- ? (u_int64_t) iovu.iov32[i].len : iovu.iov64[i].len)
+ (current_wordsize == 4 ? (uint64_t) iovu.iov32[i].len : iovu.iov64[i].len)
#else
struct iovec *iov;
#define sizeof_iov sizeof(*iov)
#define iov_iov_len(i) iov[i].iov_len
#endif
int i;
- unsigned long size;
+ unsigned size;
- size = sizeof_iov * (unsigned long) len;
- if (size / sizeof_iov != len
+ size = sizeof_iov * len;
+ /* Assuming no sane program has millions of iovs */
+ if ((unsigned)len > 1024*1024 /* insane or negative size? */
|| (iov = malloc(size)) == NULL) {
- fprintf(stderr, "out of memory\n");
+ fprintf(stderr, "Out of memory\n");
return;
}
if (umoven(tcp, addr, size, (char *) iov) >= 0) {
iov_iov_len(i));
}
}
- free((char *) iov);
+ free(iov);
#undef sizeof_iov
#undef iov_iov_base
#undef iov_iov_len
#endif
void
-dumpstr(tcp, addr, len)
-struct tcb *tcp;
-long addr;
-int len;
+dumpstr(struct tcb *tcp, long addr, int len)
{
static int strsize = -1;
static unsigned char *str;
- static char outstr[80];
- char *s;
- int i, j;
-
- if (strsize < len) {
- if (str)
- free(str);
- if ((str = malloc(len)) == NULL) {
- fprintf(stderr, "out of memory\n");
+
+ char outbuf[
+ (
+ (sizeof(
+ "xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx "
+ "1234567890123456") + /*in case I'm off by few:*/ 4)
+ /*align to 8 to make memset easier:*/ + 7) & -8
+ ];
+ const unsigned char *src;
+ int i;
+
+ memset(outbuf, ' ', sizeof(outbuf));
+
+ if (strsize < len + 16) {
+ free(str);
+ str = malloc(len + 16);
+ if (!str) {
+ strsize = -1;
+ fprintf(stderr, "Out of memory\n");
return;
}
- strsize = len;
+ strsize = len + 16;
}
if (umoven(tcp, addr, len, (char *) str) < 0)
return;
- for (i = 0; i < len; i += 16) {
- s = outstr;
- sprintf(s, " | %05x ", i);
- s += 9;
- for (j = 0; j < 16; j++) {
- if (j == 8)
- *s++ = ' ';
- if (i + j < len) {
- sprintf(s, " %02x", str[i + j]);
- s += 3;
+ /* Space-pad to 16 bytes */
+ i = len;
+ while (i & 0xf)
+ str[i++] = ' ';
+
+ i = 0;
+ src = str;
+ while (i < len) {
+ char *dst = outbuf;
+ /* Hex dump */
+ do {
+ if (i < len) {
+ *dst++ = "0123456789abcdef"[*src >> 4];
+ *dst++ = "0123456789abcdef"[*src & 0xf];
}
else {
- *s++ = ' '; *s++ = ' '; *s++ = ' ';
- }
- }
- *s++ = ' '; *s++ = ' ';
- for (j = 0; j < 16; j++) {
- if (j == 8)
- *s++ = ' ';
- if (i + j < len) {
- if (isprint(str[i + j]))
- *s++ = str[i + j];
- else
- *s++ = '.';
+ *dst++ = ' ';
+ *dst++ = ' ';
}
+ dst++; /* space is there by memset */
+ i++;
+ if ((i & 7) == 0)
+ dst++; /* space is there by memset */
+ src++;
+ } while (i & 0xf);
+ /* ASCII dump */
+ i -= 16;
+ src -= 16;
+ do {
+ if (*src >= ' ' && *src < 0x7f)
+ *dst++ = *src;
else
- *s++ = ' ';
- }
- tprintf("%s |\n", outstr);
+ *dst++ = '.';
+ src++;
+ } while (++i & 0xf);
+ *dst = '\0';
+ tprintf(" | %05x %s |\n", i - 16, outbuf);
}
}
+#ifdef HAVE_PROCESS_VM_READV
+/* C library supports this, but the kernel might not. */
+static bool process_vm_readv_not_supported = 0;
+#else
+
+/* Need to do this since process_vm_readv() is not yet available in libc.
+ * When libc is be updated, only "static bool process_vm_readv_not_supported"
+ * line should remain.
+ */
+#if !defined(__NR_process_vm_readv)
+# if defined(I386)
+# define __NR_process_vm_readv 347
+# elif defined(X86_64)
+# define __NR_process_vm_readv 310
+# elif defined(POWERPC)
+# define __NR_process_vm_readv 351
+# endif
+#endif
+
+#if defined(__NR_process_vm_readv)
+static bool process_vm_readv_not_supported = 0;
+/* Have to avoid duplicating with the C library headers. */
+static ssize_t strace_process_vm_readv(pid_t pid,
+ const struct iovec *lvec,
+ unsigned long liovcnt,
+ const struct iovec *rvec,
+ unsigned long riovcnt,
+ unsigned long flags)
+{
+ return syscall(__NR_process_vm_readv, (long)pid, lvec, liovcnt, rvec, riovcnt, flags);
+}
+#define process_vm_readv strace_process_vm_readv
+#else
+static bool process_vm_readv_not_supported = 1;
+# define process_vm_readv(...) (errno = ENOSYS, -1)
+#endif
+
+#endif /* end of hack */
+
#define PAGMASK (~(PAGSIZ - 1))
/*
* move `len' bytes of data from process `pid'
int
umoven(struct tcb *tcp, long addr, int len, char *laddr)
{
-#ifdef LINUX
int pid = tcp->pid;
- int n, m;
- int started = 0;
+ int n, m, nread;
union {
long val;
char x[sizeof(long)];
} u;
+#if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4
+ if (current_wordsize < sizeof(addr))
+ addr &= (1ul << 8 * current_wordsize) - 1;
+#endif
+
+ if (!process_vm_readv_not_supported) {
+ struct iovec local[1], remote[1];
+ int r;
+
+ local[0].iov_base = laddr;
+ remote[0].iov_base = (void*)addr;
+ local[0].iov_len = remote[0].iov_len = len;
+ r = process_vm_readv(pid, local, 1, remote, 1, 0);
+ if (r == len)
+ return 0;
+ if (r >= 0) {
+ error_msg("umoven: short read (%d < %d) @0x%lx",
+ r, len, addr);
+ return -1;
+ }
+ switch (errno) {
+ case ENOSYS:
+ process_vm_readv_not_supported = 1;
+ break;
+ case ESRCH:
+ /* the process is gone */
+ return -1;
+ case EFAULT: case EIO: case EPERM:
+ /* address space is inaccessible */
+ return -1;
+ default:
+ /* all the rest is strange and should be reported */
+ perror_msg("process_vm_readv");
+ return -1;
+ }
+ }
+
+ nread = 0;
if (addr & (sizeof(long) - 1)) {
/* addr not a multiple of sizeof(long) */
n = addr - (addr & -sizeof(long)); /* residue */
addr &= -sizeof(long); /* residue */
errno = 0;
u.val = ptrace(PTRACE_PEEKDATA, pid, (char *) addr, 0);
- if (errno) {
- if (started && (errno==EPERM || errno==EIO)) {
- /* Ran into 'end of memory' - stupid "printpath" */
- return 0;
- }
- /* But if not started, we had a bogus address. */
- if (addr != 0 && errno != EIO && errno != ESRCH)
- perror("ptrace: umoven");
- return -1;
+ switch (errno) {
+ case 0:
+ break;
+ case ESRCH: case EINVAL:
+ /* these could be seen if the process is gone */
+ return -1;
+ case EFAULT: case EIO: case EPERM:
+ /* address space is inaccessible */
+ return -1;
+ default:
+ /* all the rest is strange and should be reported */
+ perror_msg("umoven: PTRACE_PEEKDATA pid:%d @0x%lx",
+ pid, addr);
+ return -1;
}
- started = 1;
- memcpy(laddr, &u.x[n], m = MIN(sizeof(long) - n, len));
- addr += sizeof(long), laddr += m, len -= m;
+ m = MIN(sizeof(long) - n, len);
+ memcpy(laddr, &u.x[n], m);
+ addr += sizeof(long);
+ laddr += m;
+ nread += m;
+ len -= m;
}
while (len) {
errno = 0;
u.val = ptrace(PTRACE_PEEKDATA, pid, (char *) addr, 0);
- if (errno) {
- if (started && (errno==EPERM || errno==EIO)) {
- /* Ran into 'end of memory' - stupid "printpath" */
- return 0;
- }
- if (addr != 0 && errno != EIO && errno != ESRCH)
- perror("ptrace: umoven");
- return -1;
- }
- started = 1;
- memcpy(laddr, u.x, m = MIN(sizeof(long), len));
- addr += sizeof(long), laddr += m, len -= m;
- }
-#endif /* LINUX */
-
-#ifdef SUNOS4
- int pid = tcp->pid;
- int n;
-
- while (len) {
- n = MIN(len, PAGSIZ);
- n = MIN(n, ((addr + PAGSIZ) & PAGMASK) - addr);
- if (ptrace(PTRACE_READDATA, pid,
- (char *) addr, len, laddr) < 0) {
- if (errno != ESRCH) {
- perror("umoven: ptrace(PTRACE_READDATA, ...)");
- abort();
- }
- return -1;
+ switch (errno) {
+ case 0:
+ break;
+ case ESRCH: case EINVAL:
+ /* these could be seen if the process is gone */
+ return -1;
+ case EFAULT: case EIO: case EPERM:
+ /* address space is inaccessible */
+ if (nread) {
+ perror_msg("umoven: short read (%d < %d) @0x%lx",
+ nread, nread + len, addr - nread);
+ }
+ return -1;
+ default:
+ /* all the rest is strange and should be reported */
+ perror_msg("umoven: PTRACE_PEEKDATA pid:%d @0x%lx",
+ pid, addr);
+ return -1;
}
- len -= n;
- addr += n;
- laddr += n;
+ m = MIN(sizeof(long), len);
+ memcpy(laddr, u.x, m);
+ addr += sizeof(long);
+ laddr += m;
+ nread += m;
+ len -= m;
}
-#endif /* SUNOS4 */
-
-#ifdef USE_PROCFS
-#ifdef HAVE_MP_PROCFS
- int fd = tcp->pfd_as;
-#else
- int fd = tcp->pfd;
-#endif
- lseek(fd, addr, SEEK_SET);
- if (read(fd, laddr, len) == -1)
- return -1;
-#endif /* USE_PROCFS */
return 0;
}
/*
- * like `umove' but make the additional effort of looking
+ * Like `umove' but make the additional effort of looking
* for a terminating zero byte.
+ *
+ * Returns < 0 on error, > 0 if NUL was seen,
+ * (TODO if useful: return count of bytes including NUL),
+ * else 0 if len bytes were read but no NUL byte seen.
+ *
+ * Note: there is no guarantee we won't overwrite some bytes
+ * in laddr[] _after_ terminating NUL (but, of course,
+ * we never write past laddr[len-1]).
*/
int
umovestr(struct tcb *tcp, long addr, int len, char *laddr)
{
-#ifdef USE_PROCFS
-#ifdef HAVE_MP_PROCFS
- int fd = tcp->pfd_as;
+#if SIZEOF_LONG == 4
+ const unsigned long x01010101 = 0x01010101ul;
+ const unsigned long x80808080 = 0x80808080ul;
+#elif SIZEOF_LONG == 8
+ const unsigned long x01010101 = 0x0101010101010101ul;
+ const unsigned long x80808080 = 0x8080808080808080ul;
#else
- int fd = tcp->pfd;
+# error SIZEOF_LONG > 8
#endif
- /* Some systems (e.g. FreeBSD) can be upset if we read off the
- end of valid memory, avoid this by trying to read up
- to page boundaries. But we don't know what a page is (and
- getpagesize(2) (if it exists) doesn't necessarily return
- hardware page size). Assume all pages >= 1024 (a-historical
- I know) */
-
- int page = 1024; /* How to find this? */
- int move = page - (addr & (page - 1));
- int left = len;
-
- lseek(fd, addr, SEEK_SET);
-
- while (left) {
- if (move > left) move = left;
- if ((move = read(fd, laddr, move)) <= 0)
- return left != len ? 0 : -1;
- if (memchr (laddr, 0, move)) break;
- left -= move;
- laddr += move;
- addr += move;
- move = page;
- }
-#else /* !USE_PROCFS */
- int started = 0;
+
int pid = tcp->pid;
- int i, n, m;
+ int n, m, nread;
union {
- long val;
+ unsigned long val;
char x[sizeof(long)];
} u;
+#if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4
+ if (current_wordsize < sizeof(addr))
+ addr &= (1ul << 8 * current_wordsize) - 1;
+#endif
+
+ nread = 0;
+ if (!process_vm_readv_not_supported) {
+ struct iovec local[1], remote[1];
+
+ local[0].iov_base = laddr;
+ remote[0].iov_base = (void*)addr;
+
+ while (len > 0) {
+ int end_in_page;
+ int r;
+ int chunk_len;
+
+ /* Don't read kilobytes: most strings are short */
+ chunk_len = len;
+ if (chunk_len > 256)
+ chunk_len = 256;
+ /* Don't cross pages. I guess otherwise we can get EFAULT
+ * and fail to notice that terminating NUL lies
+ * in the existing (first) page.
+ * (I hope there aren't arches with pages < 4K)
+ */
+ end_in_page = ((addr + chunk_len) & 4095);
+ r = chunk_len - end_in_page;
+ if (r > 0) /* if chunk_len > end_in_page */
+ chunk_len = r; /* chunk_len -= end_in_page */
+
+ local[0].iov_len = remote[0].iov_len = chunk_len;
+ r = process_vm_readv(pid, local, 1, remote, 1, 0);
+ if (r > 0) {
+ if (memchr(local[0].iov_base, '\0', r))
+ return 1;
+ local[0].iov_base += r;
+ remote[0].iov_base += r;
+ len -= r;
+ nread += r;
+ continue;
+ }
+ switch (errno) {
+ case ENOSYS:
+ process_vm_readv_not_supported = 1;
+ goto vm_readv_didnt_work;
+ case ESRCH:
+ /* the process is gone */
+ return -1;
+ case EFAULT: case EIO: case EPERM:
+ /* address space is inaccessible */
+ if (nread) {
+ perror_msg("umovestr: short read (%d < %d) @0x%lx",
+ nread, nread + len, addr);
+ }
+ return -1;
+ default:
+ /* all the rest is strange and should be reported */
+ perror_msg("process_vm_readv");
+ return -1;
+ }
+ }
+ return 0;
+ }
+ vm_readv_didnt_work:
+
if (addr & (sizeof(long) - 1)) {
/* addr not a multiple of sizeof(long) */
n = addr - (addr & -sizeof(long)); /* residue */
addr &= -sizeof(long); /* residue */
errno = 0;
u.val = ptrace(PTRACE_PEEKDATA, pid, (char *)addr, 0);
- if (errno) {
- if (started && (errno==EPERM || errno==EIO)) {
- /* Ran into 'end of memory' - stupid "printpath" */
- return 0;
- }
- if (addr != 0 && errno != EIO && errno != ESRCH)
- perror("umovestr");
- return -1;
+ switch (errno) {
+ case 0:
+ break;
+ case ESRCH: case EINVAL:
+ /* these could be seen if the process is gone */
+ return -1;
+ case EFAULT: case EIO: case EPERM:
+ /* address space is inaccessible */
+ return -1;
+ default:
+ /* all the rest is strange and should be reported */
+ perror_msg("umovestr: PTRACE_PEEKDATA pid:%d @0x%lx",
+ pid, addr);
+ return -1;
}
- started = 1;
- memcpy(laddr, &u.x[n], m = MIN(sizeof(long)-n,len));
+ m = MIN(sizeof(long) - n, len);
+ memcpy(laddr, &u.x[n], m);
while (n & (sizeof(long) - 1))
if (u.x[n++] == '\0')
- return 0;
- addr += sizeof(long), laddr += m, len -= m;
+ return 1;
+ addr += sizeof(long);
+ laddr += m;
+ nread += m;
+ len -= m;
}
+
while (len) {
errno = 0;
u.val = ptrace(PTRACE_PEEKDATA, pid, (char *)addr, 0);
- if (errno) {
- if (started && (errno==EPERM || errno==EIO)) {
- /* Ran into 'end of memory' - stupid "printpath" */
- return 0;
- }
- if (addr != 0 && errno != EIO && errno != ESRCH)
- perror("umovestr");
- return -1;
- }
- started = 1;
- memcpy(laddr, u.x, m = MIN(sizeof(long), len));
- for (i = 0; i < sizeof(long); i++)
- if (u.x[i] == '\0')
- return 0;
-
- addr += sizeof(long), laddr += m, len -= m;
- }
-#endif /* !USE_PROCFS */
- return 0;
-}
-
-#ifdef LINUX
-# if !defined (SPARC) && !defined(SPARC64)
-# define PTRACE_WRITETEXT 101
-# define PTRACE_WRITEDATA 102
-# endif /* !SPARC && !SPARC64 */
-#endif /* LINUX */
-
-#ifdef SUNOS4
-
-static int
-uload(cmd, pid, addr, len, laddr)
-int cmd;
-int pid;
-long addr;
-int len;
-char *laddr;
-{
- int peek, poke;
- int n, m;
- union {
- long val;
- char x[sizeof(long)];
- } u;
-
- if (cmd == PTRACE_WRITETEXT) {
- peek = PTRACE_PEEKTEXT;
- poke = PTRACE_POKETEXT;
- }
- else {
- peek = PTRACE_PEEKDATA;
- poke = PTRACE_POKEDATA;
- }
- if (addr & (sizeof(long) - 1)) {
- /* addr not a multiple of sizeof(long) */
- n = addr - (addr & -sizeof(long)); /* residue */
- addr &= -sizeof(long);
- errno = 0;
- u.val = ptrace(peek, pid, (char *) addr, 0);
- if (errno) {
- perror("uload: POKE");
- return -1;
- }
- memcpy(&u.x[n], laddr, m = MIN(sizeof(long) - n, len));
- if (ptrace(poke, pid, (char *)addr, u.val) < 0) {
- perror("uload: POKE");
- return -1;
- }
- addr += sizeof(long), laddr += m, len -= m;
- }
- while (len) {
- if (len < sizeof(long))
- u.val = ptrace(peek, pid, (char *) addr, 0);
- memcpy(u.x, laddr, m = MIN(sizeof(long), len));
- if (ptrace(poke, pid, (char *) addr, u.val) < 0) {
- perror("uload: POKE");
- return -1;
+ switch (errno) {
+ case 0:
+ break;
+ case ESRCH: case EINVAL:
+ /* these could be seen if the process is gone */
+ return -1;
+ case EFAULT: case EIO: case EPERM:
+ /* address space is inaccessible */
+ if (nread) {
+ perror_msg("umovestr: short read (%d < %d) @0x%lx",
+ nread, nread + len, addr - nread);
+ }
+ return -1;
+ default:
+ /* all the rest is strange and should be reported */
+ perror_msg("umovestr: PTRACE_PEEKDATA pid:%d @0x%lx",
+ pid, addr);
+ return -1;
}
- addr += sizeof(long), laddr += m, len -= m;
+ m = MIN(sizeof(long), len);
+ memcpy(laddr, u.x, m);
+ /* "If a NUL char exists in this word" */
+ if ((u.val - x01010101) & ~u.val & x80808080)
+ return 1;
+ addr += sizeof(long);
+ laddr += m;
+ nread += m;
+ len -= m;
}
return 0;
}
int
-tload(pid, addr, len, laddr)
-int pid;
-int addr, len;
-char *laddr;
-{
- return uload(PTRACE_WRITETEXT, pid, addr, len, laddr);
-}
-
-int
-dload(pid, addr, len, laddr)
-int pid;
-int addr;
-int len;
-char *laddr;
-{
- return uload(PTRACE_WRITEDATA, pid, addr, len, laddr);
-}
-
-#endif /* SUNOS4 */
-
-#ifndef USE_PROCFS
-
-int
-upeek(tcp, off, res)
-struct tcb *tcp;
-long off;
-long *res;
+upeek(struct tcb *tcp, long off, long *res)
{
long val;
-# ifdef SUNOS4_KERNEL_ARCH_KLUDGE
- {
- static int is_sun4m = -1;
- struct utsname name;
-
- /* Round up the usual suspects. */
- if (is_sun4m == -1) {
- if (uname(&name) < 0) {
- perror("upeek: uname?");
- exit(1);
- }
- is_sun4m = strcmp(name.machine, "sun4m") == 0;
- if (is_sun4m) {
- const struct xlat *x;
-
- for (x = struct_user_offsets; x->str; x++)
- x->val += 1024;
- }
- }
- if (is_sun4m)
- off += 1024;
- }
-# endif /* SUNOS4_KERNEL_ARCH_KLUDGE */
errno = 0;
- val = do_ptrace(PTRACE_PEEKUSER, tcp, (char *) off, 0);
+ val = ptrace(PTRACE_PEEKUSER, tcp->pid, (char *) off, 0);
if (val == -1 && errno) {
if (errno != ESRCH) {
- char buf[60];
- sprintf(buf,"upeek: ptrace(PTRACE_PEEKUSER,%d,%lu,0)", tcp->pid, off);
- perror(buf);
+ perror_msg("upeek: PTRACE_PEEKUSER pid:%d @0x%lx)", tcp->pid, off);
}
return -1;
}
return 0;
}
-#endif /* !USE_PROCFS */
-
-void
-printcall(struct tcb *tcp)
-{
-#define PRINTBADPC tprintf(sizeof(long) == 4 ? "[????????] " : \
- sizeof(long) == 8 ? "[????????????????] " : \
- NULL /* crash */)
-
-#ifdef LINUX
-# ifdef I386
- long eip;
-
- if (upeek(tcp, 4*EIP, &eip) < 0) {
- PRINTBADPC;
- return;
- }
- tprintf("[%08lx] ", eip);
-
-# elif defined(S390) || defined(S390X)
- long psw;
- if(upeek(tcp,PT_PSWADDR,&psw) < 0) {
- PRINTBADPC;
- return;
- }
-# ifdef S390
- tprintf("[%08lx] ", psw);
-# elif S390X
- tprintf("[%16lx] ", psw);
-# endif
-
-# elif defined(X86_64)
- long rip;
-
- if (upeek(tcp, 8*RIP, &rip) < 0) {
- PRINTBADPC;
- return;
- }
- tprintf("[%16lx] ", rip);
-# elif defined(IA64)
- long ip;
-
- if (upeek(tcp, PT_B0, &ip) < 0) {
- PRINTBADPC;
- return;
- }
- tprintf("[%08lx] ", ip);
-# elif defined(POWERPC)
- long pc;
-
- if (upeek(tcp, sizeof(unsigned long)*PT_NIP, &pc) < 0) {
- PRINTBADPC;
- return;
- }
-# ifdef POWERPC64
- tprintf("[%016lx] ", pc);
-# else
- tprintf("[%08lx] ", pc);
-# endif
-# elif defined(M68K)
- long pc;
-
- if (upeek(tcp, 4*PT_PC, &pc) < 0) {
- tprintf ("[????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(ALPHA)
- long pc;
-
- if (upeek(tcp, REG_PC, &pc) < 0) {
- tprintf ("[????????????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(SPARC) || defined(SPARC64)
- struct pt_regs regs;
- if (ptrace(PTRACE_GETREGS,tcp->pid,(char *)®s,0) < 0) {
- PRINTBADPC;
- return;
- }
-# if defined(SPARC64)
- tprintf("[%08lx] ", regs.tpc);
-# else
- tprintf("[%08lx] ", regs.pc);
-# endif
-# elif defined(HPPA)
- long pc;
-
- if(upeek(tcp,PT_IAOQ0,&pc) < 0) {
- tprintf ("[????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(MIPS)
- long pc;
-
- if (upeek(tcp, REG_EPC, &pc) < 0) {
- tprintf ("[????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(SH)
- long pc;
-
- if (upeek(tcp, 4*REG_PC, &pc) < 0) {
- tprintf ("[????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(SH64)
- long pc;
-
- if (upeek(tcp, REG_PC, &pc) < 0) {
- tprintf ("[????????????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(ARM)
- long pc;
-
- if (upeek(tcp, 4*15, &pc) < 0) {
- PRINTBADPC;
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(AVR32)
- long pc;
-
- if (upeek(tcp, REG_PC, &pc) < 0) {
- tprintf("[????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(BFIN)
- long pc;
-
- if (upeek(tcp, PT_PC, &pc) < 0) {
- PRINTBADPC;
- return;
- }
- tprintf("[%08lx] ", pc);
-#elif defined(CRISV10)
- long pc;
-
- if (upeek(tcp, 4*PT_IRP, &pc) < 0) {
- PRINTBADPC;
- return;
- }
- tprintf("[%08lx] ", pc);
-#elif defined(CRISV32)
- long pc;
-
- if (upeek(tcp, 4*PT_ERP, &pc) < 0) {
- PRINTBADPC;
- return;
- }
- tprintf("[%08lx] ", pc);
-# endif /* architecture */
-#endif /* LINUX */
-
-#ifdef SUNOS4
- struct regs regs;
-
- if (ptrace(PTRACE_GETREGS, tcp->pid, (char *) ®s, 0) < 0) {
- perror("printcall: ptrace(PTRACE_GETREGS, ...)");
- PRINTBADPC;
- return;
- }
- tprintf("[%08x] ", regs.r_o7);
-#endif /* SUNOS4 */
-
-#ifdef SVR4
- /* XXX */
- PRINTBADPC;
-#endif
-
-#ifdef FREEBSD
- struct reg regs;
- pread(tcp->pfd_reg, ®s, sizeof(regs), 0);
- tprintf("[%08x] ", regs.r_eip);
-#endif /* FREEBSD */
-}
-
+/* Note! On new kernels (about 2.5.46+), we use PTRACE_O_TRACECLONE
+ * and PTRACE_O_TRACE[V]FORK for tracing children.
+ * If you are adding a new arch which is only supported by newer kernels,
+ * you most likely don't need to add any code below
+ * beside a dummy "return 0" block in change_syscall().
+ */
/*
* These #if's are huge, please indent them correctly.
* It's easy to get confused otherwise.
*/
-#ifndef USE_PROCFS
-
-#ifdef LINUX
-
-# include "syscall.h"
-# include <sys/syscall.h>
-# ifndef CLONE_PTRACE
-# define CLONE_PTRACE 0x00002000
-# endif
-# ifndef CLONE_VFORK
-# define CLONE_VFORK 0x00004000
-# endif
-# ifndef CLONE_VM
-# define CLONE_VM 0x00000100
-# endif
-# ifndef CLONE_STOPPED
-# define CLONE_STOPPED 0x02000000
-# endif
+#include "syscall.h"
-# ifdef IA64
+#ifndef CLONE_PTRACE
+# define CLONE_PTRACE 0x00002000
+#endif
+#ifndef CLONE_VFORK
+# define CLONE_VFORK 0x00004000
+#endif
+#ifndef CLONE_VM
+# define CLONE_VM 0x00000100
+#endif
-/* We don't have fork()/vfork() syscalls on ia64 itself, but the ia32
- subsystem has them for x86... */
-# define SYS_fork 2
-# define SYS_vfork 190
+#ifdef IA64
typedef unsigned long *arg_setup_state;
return 0;
}
-# define arg_finish_change(tcp, state) 0
+# define arg_finish_change(tcp, state) 0
-# ifdef SYS_fork
static int
-get_arg0 (struct tcb *tcp, arg_setup_state *state, long *valp)
+get_arg0(struct tcb *tcp, arg_setup_state *state, long *valp)
{
int ret;
if (ia32)
- ret = upeek (tcp, PT_R11, valp);
+ ret = upeek(tcp, PT_R11, valp);
else
- ret = umoven (tcp,
+ ret = umoven(tcp,
(unsigned long) ia64_rse_skip_regs(*state, 0),
sizeof(long), (void *) valp);
return ret;
}
static int
-get_arg1 (struct tcb *tcp, arg_setup_state *state, long *valp)
+get_arg1(struct tcb *tcp, arg_setup_state *state, long *valp)
{
int ret;
if (ia32)
- ret = upeek (tcp, PT_R9, valp);
+ ret = upeek(tcp, PT_R9, valp);
else
- ret = umoven (tcp,
+ ret = umoven(tcp,
(unsigned long) ia64_rse_skip_regs(*state, 1),
sizeof(long), (void *) valp);
return ret;
}
-# endif
static int
-set_arg0 (struct tcb *tcp, arg_setup_state *state, long val)
+set_arg0(struct tcb *tcp, arg_setup_state *state, long val)
{
int req = PTRACE_POKEDATA;
void *ap;
}
static int
-set_arg1 (struct tcb *tcp, arg_setup_state *state, long val)
+set_arg1(struct tcb *tcp, arg_setup_state *state, long val)
{
int req = PTRACE_POKEDATA;
void *ap;
/* ia64 does not return the input arguments from functions (and syscalls)
according to ia64 RSE (Register Stack Engine) behavior. */
-# define restore_arg0(tcp, state, val) ((void) (state), 0)
-# define restore_arg1(tcp, state, val) ((void) (state), 0)
+# define restore_arg0(tcp, state, val) ((void) (state), 0)
+# define restore_arg1(tcp, state, val) ((void) (state), 0)
-# elif defined (SPARC) || defined (SPARC64)
+#elif defined(SPARC) || defined(SPARC64)
+
+# if defined(SPARC64)
+# undef PTRACE_GETREGS
+# define PTRACE_GETREGS PTRACE_GETREGS64
+# undef PTRACE_SETREGS
+# define PTRACE_SETREGS PTRACE_SETREGS64
+# endif
typedef struct pt_regs arg_setup_state;
-# define arg_setup(tcp, state) \
- (ptrace (PTRACE_GETREGS, tcp->pid, (char *) (state), 0))
-# define arg_finish_change(tcp, state) \
- (ptrace (PTRACE_SETREGS, tcp->pid, (char *) (state), 0))
+# define arg_setup(tcp, state) \
+ (ptrace(PTRACE_GETREGS, (tcp)->pid, (char *) (state), 0))
+# define arg_finish_change(tcp, state) \
+ (ptrace(PTRACE_SETREGS, (tcp)->pid, (char *) (state), 0))
-# define get_arg0(tcp, state, valp) (*(valp) = (state)->u_regs[U_REG_O0], 0)
-# define get_arg1(tcp, state, valp) (*(valp) = (state)->u_regs[U_REG_O1], 0)
-# define set_arg0(tcp, state, val) ((state)->u_regs[U_REG_O0] = (val), 0)
-# define set_arg1(tcp, state, val) ((state)->u_regs[U_REG_O1] = (val), 0)
-# define restore_arg0(tcp, state, val) 0
+# define get_arg0(tcp, state, valp) (*(valp) = (state)->u_regs[U_REG_O0], 0)
+# define get_arg1(tcp, state, valp) (*(valp) = (state)->u_regs[U_REG_O1], 0)
+# define set_arg0(tcp, state, val) ((state)->u_regs[U_REG_O0] = (val), 0)
+# define set_arg1(tcp, state, val) ((state)->u_regs[U_REG_O1] = (val), 0)
+# define restore_arg0(tcp, state, val) 0
-# else /* other architectures */
+#else /* other architectures */
-# if defined S390 || defined S390X
+# if defined S390 || defined S390X
/* Note: this is only true for the `clone' system call, which handles
arguments specially. We could as well say that its first two arguments
are swapped relative to other architectures, but that would just be
another #ifdef in the calls. */
-# define arg0_offset PT_GPR3
-# define arg1_offset PT_ORIGGPR2
-# define restore_arg0(tcp, state, val) ((void) (state), 0)
-# define restore_arg1(tcp, state, val) ((void) (state), 0)
-# define arg0_index 1
-# define arg1_index 0
-# elif defined (ALPHA) || defined (MIPS)
-# define arg0_offset REG_A0
-# define arg1_offset (REG_A0+1)
-# elif defined (AVR32)
-# define arg0_offset (REG_R12)
-# define arg1_offset (REG_R11)
-# elif defined (POWERPC)
-# define arg0_offset (sizeof(unsigned long)*PT_R3)
-# define arg1_offset (sizeof(unsigned long)*PT_R4)
-# define restore_arg0(tcp, state, val) ((void) (state), 0)
-# elif defined (HPPA)
-# define arg0_offset PT_GR26
-# define arg1_offset (PT_GR26-4)
-# elif defined (X86_64)
-# define arg0_offset ((long)(8*(current_personality ? RBX : RDI)))
-# define arg1_offset ((long)(8*(current_personality ? RCX : RSI)))
-# elif defined (SH)
-# define arg0_offset (4*(REG_REG0+4))
-# define arg1_offset (4*(REG_REG0+5))
-# elif defined (SH64)
- /* ABI defines arg0 & 1 in r2 & r3 */
-# define arg0_offset (REG_OFFSET+16)
-# define arg1_offset (REG_OFFSET+24)
-# define restore_arg0(tcp, state, val) 0
-# elif defined CRISV10 || defined CRISV32
-# define arg0_offset (4*PT_R11)
-# define arg1_offset (4*PT_ORIG_R10)
-# define restore_arg0(tcp, state, val) 0
-# define restore_arg1(tcp, state, val) 0
-# define arg0_index 1
-# define arg1_index 0
-# else
-# define arg0_offset 0
-# define arg1_offset 4
-# if defined ARM
-# define restore_arg0(tcp, state, val) 0
-# endif
-# endif
+# define arg0_offset PT_GPR3
+# define arg1_offset PT_ORIGGPR2
+# define restore_arg0(tcp, state, val) ((void) (state), 0)
+# define restore_arg1(tcp, state, val) ((void) (state), 0)
+# define arg0_index 1
+# define arg1_index 0
+# elif defined(ALPHA) || defined(MIPS)
+# define arg0_offset REG_A0
+# define arg1_offset (REG_A0+1)
+# elif defined(POWERPC)
+# define arg0_offset (sizeof(unsigned long)*PT_R3)
+# define arg1_offset (sizeof(unsigned long)*PT_R4)
+# define restore_arg0(tcp, state, val) ((void) (state), 0)
+# elif defined(HPPA)
+# define arg0_offset PT_GR26
+# define arg1_offset (PT_GR26-4)
+# elif defined(X86_64) || defined(X32)
+# define arg0_offset ((long)(8*(current_personality ? RBX : RDI)))
+# define arg1_offset ((long)(8*(current_personality ? RCX : RSI)))
+# elif defined(SH)
+# define arg0_offset (4*(REG_REG0+4))
+# define arg1_offset (4*(REG_REG0+5))
+# elif defined(SH64)
+ /* ABI defines arg0 & 1 in r2 & r3 */
+# define arg0_offset (REG_OFFSET+16)
+# define arg1_offset (REG_OFFSET+24)
+# define restore_arg0(tcp, state, val) 0
+# elif defined CRISV10 || defined CRISV32
+# define arg0_offset (4*PT_R11)
+# define arg1_offset (4*PT_ORIG_R10)
+# define restore_arg0(tcp, state, val) 0
+# define restore_arg1(tcp, state, val) 0
+# define arg0_index 1
+# define arg1_index 0
+# else
+# define arg0_offset 0
+# define arg1_offset 4
+# if defined ARM
+# define restore_arg0(tcp, state, val) 0
+# endif
+# endif
typedef int arg_setup_state;
-# define arg_setup(tcp, state) (0)
-# define arg_finish_change(tcp, state) 0
-# define get_arg0(tcp, cookie, valp) \
- (upeek ((tcp), arg0_offset, (valp)))
-# define get_arg1(tcp, cookie, valp) \
- (upeek ((tcp), arg1_offset, (valp)))
+# define arg_setup(tcp, state) (0)
+# define arg_finish_change(tcp, state) 0
+# define get_arg0(tcp, cookie, valp) (upeek((tcp), arg0_offset, (valp)))
+# define get_arg1(tcp, cookie, valp) (upeek((tcp), arg1_offset, (valp)))
static int
-set_arg0 (struct tcb *tcp, void *cookie, long val)
+set_arg0(struct tcb *tcp, void *cookie, long val)
{
- return ptrace (PTRACE_POKEUSER, tcp->pid, (char*)arg0_offset, val);
+ return ptrace(PTRACE_POKEUSER, tcp->pid, (char*)arg0_offset, val);
}
static int
-set_arg1 (struct tcb *tcp, void *cookie, long val)
+set_arg1(struct tcb *tcp, void *cookie, long val)
{
- return ptrace (PTRACE_POKEUSER, tcp->pid, (char*)arg1_offset, val);
+ return ptrace(PTRACE_POKEUSER, tcp->pid, (char*)arg1_offset, val);
}
-# endif /* architectures */
+#endif /* architectures */
-# ifndef restore_arg0
-# define restore_arg0(tcp, state, val) set_arg0((tcp), (state), (val))
-# endif
-# ifndef restore_arg1
-# define restore_arg1(tcp, state, val) set_arg1((tcp), (state), (val))
-# endif
+#ifndef restore_arg0
+# define restore_arg0(tcp, state, val) set_arg0((tcp), (state), (val))
+#endif
+#ifndef restore_arg1
+# define restore_arg1(tcp, state, val) set_arg1((tcp), (state), (val))
+#endif
-# ifndef arg0_index
-# define arg0_index 0
-# define arg1_index 1
-# endif
+#ifndef arg0_index
+# define arg0_index 0
+# define arg1_index 1
+#endif
+
+static int
+change_syscall(struct tcb *tcp, arg_setup_state *state, int new)
+{
+#if defined(I386)
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(ORIG_EAX * 4), new) < 0)
+ return -1;
+ return 0;
+#elif defined(X86_64)
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(ORIG_RAX * 8), new) < 0)
+ return -1;
+ return 0;
+#elif defined(X32)
+ /* setbpt/clearbpt never used: */
+ /* X32 is only supported since about linux-3.0.30 */
+#elif defined(POWERPC)
+ if (ptrace(PTRACE_POKEUSER, tcp->pid,
+ (char*)(sizeof(unsigned long)*PT_R0), new) < 0)
+ return -1;
+ return 0;
+#elif defined(S390) || defined(S390X)
+ /* s390 linux after 2.4.7 has a hook in entry.S to allow this */
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(PT_GPR2), new) < 0)
+ return -1;
+ return 0;
+#elif defined(M68K)
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(4*PT_ORIG_D0), new) < 0)
+ return -1;
+ return 0;
+#elif defined(SPARC) || defined(SPARC64)
+ state->u_regs[U_REG_G1] = new;
+ return 0;
+#elif defined(MIPS)
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(REG_V0), new) < 0)
+ return -1;
+ return 0;
+#elif defined(ALPHA)
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(REG_A3), new) < 0)
+ return -1;
+ return 0;
+#elif defined(AVR32)
+ /* setbpt/clearbpt never used: */
+ /* AVR32 is only supported since about linux-2.6.19 */
+#elif defined(BFIN)
+ /* setbpt/clearbpt never used: */
+ /* Blackfin is only supported since about linux-2.6.23 */
+#elif defined(IA64)
+ if (ia32) {
+ switch (new) {
+ case 2:
+ break; /* x86 SYS_fork */
+ case SYS_clone:
+ new = 120;
+ break;
+ default:
+ fprintf(stderr, "%s: unexpected syscall %d\n",
+ __FUNCTION__, new);
+ return -1;
+ }
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(PT_R1), new) < 0)
+ return -1;
+ } else if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(PT_R15), new) < 0)
+ return -1;
+ return 0;
+#elif defined(HPPA)
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(PT_GR20), new) < 0)
+ return -1;
+ return 0;
+#elif defined(SH)
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(4*(REG_REG0+3)), new) < 0)
+ return -1;
+ return 0;
+#elif defined(SH64)
+ /* Top half of reg encodes the no. of args n as 0x1n.
+ Assume 0 args as kernel never actually checks... */
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(REG_SYSCALL),
+ 0x100000 | new) < 0)
+ return -1;
+ return 0;
+#elif defined(CRISV10) || defined(CRISV32)
+ if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(4*PT_R9), new) < 0)
+ return -1;
+ return 0;
+#elif defined(ARM)
+ /* Some kernels support this, some (pre-2.6.16 or so) don't. */
+# ifndef PTRACE_SET_SYSCALL
+# define PTRACE_SET_SYSCALL 23
+# endif
+ if (ptrace(PTRACE_SET_SYSCALL, tcp->pid, 0, new & 0xffff) != 0)
+ return -1;
+ return 0;
+#elif defined(AARCH64)
+ /* setbpt/clearbpt never used: */
+ /* AARCH64 is only supported since about linux-3.0.31 */
+#elif defined(TILE)
+ /* setbpt/clearbpt never used: */
+ /* Tilera CPUs are only supported since about linux-2.6.34 */
+#elif defined(MICROBLAZE)
+ /* setbpt/clearbpt never used: */
+ /* microblaze is only supported since about linux-2.6.30 */
+#elif defined(OR1K)
+ /* never reached; OR1K is only supported by kernels since 3.1.0. */
+#elif defined(METAG)
+ /* setbpt/clearbpt never used: */
+ /* Meta is only supported since linux-3.7 */
+#elif defined(XTENSA)
+ /* setbpt/clearbpt never used: */
+ /* Xtensa is only supported since linux 2.6.13 */
+#else
+#warning Do not know how to handle change_syscall for this architecture
+#endif /* architecture */
+ return -1;
+}
int
setbpt(struct tcb *tcp)
}
}
- switch (known_scno(tcp)) {
-# ifdef SYS_vfork
- case SYS_vfork:
-# endif
-# ifdef SYS_fork
- case SYS_fork:
-# endif
-# if defined SYS_fork || defined SYS_vfork
- if (arg_setup (tcp, &state) < 0
- || get_arg0 (tcp, &state, &tcp->inst[0]) < 0
- || get_arg1 (tcp, &state, &tcp->inst[1]) < 0
- || change_syscall(tcp, clone_scno[current_personality]) < 0
- || set_arg0 (tcp, &state, CLONE_PTRACE|SIGCHLD) < 0
- || set_arg1 (tcp, &state, 0) < 0
- || arg_finish_change (tcp, &state) < 0)
+ if (tcp->s_ent->sys_func == sys_fork ||
+ tcp->s_ent->sys_func == sys_vfork) {
+ if (arg_setup(tcp, &state) < 0
+ || get_arg0(tcp, &state, &tcp->inst[0]) < 0
+ || get_arg1(tcp, &state, &tcp->inst[1]) < 0
+ || change_syscall(tcp, &state,
+ clone_scno[current_personality]) < 0
+ || set_arg0(tcp, &state, CLONE_PTRACE|SIGCHLD) < 0
+ || set_arg1(tcp, &state, 0) < 0
+ || arg_finish_change(tcp, &state) < 0)
return -1;
tcp->u_arg[arg0_index] = CLONE_PTRACE|SIGCHLD;
tcp->u_arg[arg1_index] = 0;
tcp->flags |= TCB_BPTSET;
return 0;
-# endif
+ }
- case SYS_clone:
-# ifdef SYS_clone2
- case SYS_clone2:
-# endif
+ if (tcp->s_ent->sys_func == sys_clone) {
/* ia64 calls directly `clone (CLONE_VFORK | CLONE_VM)'
- contrary to x86 SYS_vfork above. Even on x86 we turn the
+ contrary to x86 vfork above. Even on x86 we turn the
vfork semantics into plain fork - each application must not
depend on the vfork specifics according to POSIX. We would
hang waiting for the parent resume otherwise. We need to
clear also CLONE_VM but only in the CLONE_VFORK case as
otherwise we would break pthread_create. */
- if ((arg_setup (tcp, &state) < 0
- || set_arg0 (tcp, &state,
- (tcp->u_arg[arg0_index] | CLONE_PTRACE)
- & ~(tcp->u_arg[arg0_index] & CLONE_VFORK
- ? CLONE_VFORK | CLONE_VM : 0)) < 0
- || arg_finish_change (tcp, &state) < 0))
+ long new_arg0 = (tcp->u_arg[arg0_index] | CLONE_PTRACE);
+ if (new_arg0 & CLONE_VFORK)
+ new_arg0 &= ~(unsigned long)(CLONE_VFORK | CLONE_VM);
+ if (arg_setup(tcp, &state) < 0
+ || set_arg0(tcp, &state, new_arg0) < 0
+ || arg_finish_change(tcp, &state) < 0)
return -1;
- tcp->flags |= TCB_BPTSET;
tcp->inst[0] = tcp->u_arg[arg0_index];
tcp->inst[1] = tcp->u_arg[arg1_index];
+ tcp->flags |= TCB_BPTSET;
return 0;
-
- default:
- fprintf(stderr, "PANIC: setbpt for syscall %ld on %u???\n",
- tcp->scno, tcp->pid);
- break;
}
+ fprintf(stderr, "PANIC: setbpt for syscall %ld on %u???\n",
+ tcp->scno, tcp->pid);
return -1;
}
int
-clearbpt(tcp)
-struct tcb *tcp;
+clearbpt(struct tcb *tcp)
{
arg_setup_state state;
- if (arg_setup (tcp, &state) < 0
- || restore_arg0 (tcp, &state, tcp->inst[0]) < 0
- || restore_arg1 (tcp, &state, tcp->inst[1]) < 0
- || arg_finish_change (tcp, &state))
- if (errno != ESRCH) return -1;
- tcp->flags &= ~TCB_BPTSET;
- return 0;
-}
-
-# else /* !defined LINUX */
-
-int
-setbpt(tcp)
-struct tcb *tcp;
-{
-# ifdef SUNOS4
-# ifdef SPARC /* This code is slightly sparc specific */
-
- struct regs regs;
-# define BPT 0x91d02001 /* ta 1 */
-# define LOOP 0x10800000 /* ba 0 */
-# define LOOPA 0x30800000 /* ba,a 0 */
-# define NOP 0x01000000
-# if LOOPA
- static int loopdeloop[1] = {LOOPA};
-# else
- static int loopdeloop[2] = {LOOP, NOP};
-# endif
-
- if (tcp->flags & TCB_BPTSET) {
- fprintf(stderr, "PANIC: TCB already set in pid %u\n", tcp->pid);
- return -1;
- }
- if (ptrace(PTRACE_GETREGS, tcp->pid, (char *)®s, 0) < 0) {
- perror("setbpt: ptrace(PTRACE_GETREGS, ...)");
- return -1;
- }
- tcp->baddr = regs.r_o7 + 8;
- if (ptrace(PTRACE_READTEXT, tcp->pid, (char *)tcp->baddr,
- sizeof tcp->inst, (char *)tcp->inst) < 0) {
- perror("setbpt: ptrace(PTRACE_READTEXT, ...)");
- return -1;
- }
-
- /*
- * XXX - BRUTAL MODE ON
- * We cannot set a real BPT in the child, since it will not be
- * traced at the moment it will reach the trap and would probably
- * die with a core dump.
- * Thus, we are force our way in by taking out two instructions
- * and insert an eternal loop in stead, in expectance of the SIGSTOP
- * generated by out PTRACE_ATTACH.
- * Of cause, if we evaporate ourselves in the middle of all this...
- */
- if (ptrace(PTRACE_WRITETEXT, tcp->pid, (char *) tcp->baddr,
- sizeof loopdeloop, (char *) loopdeloop) < 0) {
- perror("setbpt: ptrace(PTRACE_WRITETEXT, ...)");
- return -1;
- }
- tcp->flags |= TCB_BPTSET;
-
-# endif /* SPARC */
-# endif /* SUNOS4 */
-
- return 0;
-}
-
-int
-clearbpt(tcp)
-struct tcb *tcp;
-{
-# ifdef SUNOS4
-# ifdef SPARC
-
-# if !LOOPA
- struct regs regs;
-# endif
-
- if (!(tcp->flags & TCB_BPTSET)) {
- fprintf(stderr, "PANIC: TCB not set in pid %u\n", tcp->pid);
- return -1;
- }
- if (ptrace(PTRACE_WRITETEXT, tcp->pid, (char *) tcp->baddr,
- sizeof tcp->inst, (char *) tcp->inst) < 0) {
- perror("clearbtp: ptrace(PTRACE_WRITETEXT, ...)");
- return -1;
- }
- tcp->flags &= ~TCB_BPTSET;
-
-# if !LOOPA
- /*
- * Since we don't have a single instruction breakpoint, we may have
- * to adjust the program counter after removing our `breakpoint'.
- */
- if (ptrace(PTRACE_GETREGS, tcp->pid, (char *)®s, 0) < 0) {
- perror("clearbpt: ptrace(PTRACE_GETREGS, ...)");
- return -1;
- }
- if ((regs.r_pc < tcp->baddr) ||
- (regs.r_pc > tcp->baddr + 4)) {
- /* The breakpoint has not been reached yet */
- if (debug)
- fprintf(stderr,
- "NOTE: PC not at bpt (pc %#x baddr %#x)\n",
- regs.r_pc, tcp->baddr);
- return 0;
- }
- if (regs.r_pc != tcp->baddr)
- if (debug)
- fprintf(stderr, "NOTE: PC adjusted (%#x -> %#x\n",
- regs.r_pc, tcp->baddr);
-
- regs.r_pc = tcp->baddr;
- if (ptrace(PTRACE_SETREGS, tcp->pid, (char *)®s, 0) < 0) {
- perror("clearbpt: ptrace(PTRACE_SETREGS, ...)");
- return -1;
- }
-# endif /* LOOPA */
-# endif /* SPARC */
-# endif /* SUNOS4 */
-
- return 0;
-}
-
-# endif /* !defined LINUX */
-
-#endif /* !USE_PROCFS */
-
-
-#ifdef SUNOS4
-
-static int
-getex(tcp, hdr)
-struct tcb *tcp;
-struct exec *hdr;
-{
- int n;
-
- for (n = 0; n < sizeof *hdr; n += 4) {
- long res;
- if (upeek(tcp, uoff(u_exdata) + n, &res) < 0)
+ if (arg_setup(tcp, &state) < 0
+ || change_syscall(tcp, &state, tcp->scno) < 0
+ || restore_arg0(tcp, &state, tcp->inst[0]) < 0
+ || restore_arg1(tcp, &state, tcp->inst[1]) < 0
+ || arg_finish_change(tcp, &state))
+ if (errno != ESRCH)
return -1;
- memcpy(((char *) hdr) + n, &res, 4);
- }
- if (debug) {
- fprintf(stderr, "[struct exec: magic: %o version %u Mach %o\n",
- hdr->a_magic, hdr->a_toolversion, hdr->a_machtype);
- fprintf(stderr, "Text %lu Data %lu Bss %lu Syms %lu Entry %#lx]\n",
- hdr->a_text, hdr->a_data, hdr->a_bss, hdr->a_syms, hdr->a_entry);
- }
- return 0;
-}
-
-int
-fixvfork(tcp)
-struct tcb *tcp;
-{
- int pid = tcp->pid;
- /*
- * Change `vfork' in a freshly exec'ed dynamically linked
- * executable's (internal) symbol table to plain old `fork'
- */
-
- struct exec hdr;
- struct link_dynamic dyn;
- struct link_dynamic_2 ld;
- char *strtab, *cp;
-
- if (getex(tcp, &hdr) < 0)
- return -1;
- if (!hdr.a_dynamic)
- return -1;
-
- if (umove(tcp, (int) N_DATADDR(hdr), &dyn) < 0) {
- fprintf(stderr, "Cannot read DYNAMIC\n");
- return -1;
- }
- if (umove(tcp, (int) dyn.ld_un.ld_2, &ld) < 0) {
- fprintf(stderr, "Cannot read link_dynamic_2\n");
- return -1;
- }
- if ((strtab = malloc((unsigned)ld.ld_symb_size)) == NULL) {
- fprintf(stderr, "out of memory\n");
- return -1;
- }
- if (umoven(tcp, (int)ld.ld_symbols+(int)N_TXTADDR(hdr),
- (int)ld.ld_symb_size, strtab) < 0)
- goto err;
-
- for (cp = strtab; cp < strtab + ld.ld_symb_size; ) {
- if (strcmp(cp, "_vfork") == 0) {
- if (debug)
- fprintf(stderr, "fixvfork: FOUND _vfork\n");
- strcpy(cp, "_fork");
- break;
- }
- cp += strlen(cp)+1;
- }
- if (cp < strtab + ld.ld_symb_size)
- /*
- * Write entire symbol table back to avoid
- * memory alignment bugs in ptrace
- */
- if (tload(pid, (int)ld.ld_symbols+(int)N_TXTADDR(hdr),
- (int)ld.ld_symb_size, strtab) < 0)
- goto err;
-
- free(strtab);
+ tcp->flags &= ~TCB_BPTSET;
return 0;
-
-err:
- free(strtab);
- return -1;
}
-
-#endif /* SUNOS4 */