* 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>
-#endif
-
-#if defined(LINUX) && defined(IA64)
-# include <asm/ptrace_offsets.h>
-# include <asm/rse.h>
-#endif
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-# define PTRACE_PEEKUSR PTRACE_PEEKUSER
-#elif defined(HAVE_LINUX_PTRACE_H)
-#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>
-# undef ia64_fpreg
-# undef pt_all_user_regs
+#if HAVE_SYS_XATTR_H
+# include <sys/xattr.h>
#endif
+#include <sys/uio.h>
-#ifdef SUNOS4_KERNEL_ARCH_KLUDGE
-#include <sys/utsname.h>
-#endif /* SUNOS4_KERNEL_ARCH_KLUDGE */
+#include "regs.h"
+#include "ptrace.h"
-#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(struct timeval *a)
+tv_nz(const struct timeval *a)
{
return a->tv_sec || a->tv_usec;
}
int
-tv_cmp(struct timeval *a, struct timeval *b)
+tv_cmp(const struct timeval *a, const 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(struct timeval *tv)
+tv_float(const struct timeval *tv)
{
return tv->tv_sec + tv->tv_usec/1000000.0;
}
void
-tv_add(struct timeval *tv, struct timeval *a, struct timeval *b)
+tv_add(struct timeval *tv, const struct timeval *a, const struct timeval *b)
{
tv->tv_sec = a->tv_sec + b->tv_sec;
tv->tv_usec = a->tv_usec + b->tv_usec;
}
void
-tv_sub(struct timeval *tv, struct timeval *a, struct timeval *b)
+tv_sub(struct timeval *tv, const struct timeval *a, const struct timeval *b)
{
tv->tv_sec = a->tv_sec - b->tv_sec;
tv->tv_usec = a->tv_usec - b->tv_usec;
}
void
-tv_div(struct timeval *tv, struct timeval *a, int n)
+tv_div(struct timeval *tv, const 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(struct timeval *tv, struct timeval *a, int n)
+tv_mul(struct timeval *tv, const struct timeval *a, int n)
{
tv->tv_usec = a->tv_usec * n;
tv->tv_sec = a->tv_sec * n + tv->tv_usec / 1000000;
}
const char *
-xlookup(const struct xlat *xlat, int val)
+xlookup(const struct xlat *xlat, const unsigned int val)
{
for (; xlat->str != NULL; xlat++)
if (xlat->val == val)
return NULL;
}
+static int
+xlat_bsearch_compare(const void *a, const void *b)
+{
+ const unsigned int val1 = (const unsigned long) a;
+ const unsigned int val2 = ((const struct xlat *) b)->val;
+ return (val1 > val2) ? 1 : (val1 < val2) ? -1 : 0;
+}
+
+const char *
+xlat_search(const struct xlat *xlat, const size_t nmemb, const unsigned int val)
+{
+ const struct xlat *e =
+ bsearch((const void*) (const unsigned long) val,
+ xlat, nmemb, sizeof(*xlat), xlat_bsearch_compare);
+
+ return e ? e->str : NULL;
+}
+
#if !defined HAVE_STPCPY
char *
stpcpy(char *dst, const char *src)
}
#endif
-/*
- * 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.
+/* Find a next bit which is set.
+ * Starts testing at cur_bit.
+ * Returns -1 if no more bits are set.
*
- * 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)
-{
- 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.
+ * We never touch bytes we don't need to.
+ * On big-endian, array is assumed to consist of
+ * current_wordsize wide words: for example, is current_wordsize is 4,
+ * the bytes are walked in 3,2,1,0, 7,6,5,4, 11,10,9,8 ... sequence.
+ * On little-endian machines, word size is immaterial.
*/
int
-ptrace_restart(int op, struct tcb *tcp, int sig)
+next_set_bit(const void *bit_array, unsigned cur_bit, unsigned size_bits)
{
- int err;
- const char *msg;
+ const unsigned endian = 1;
+ int little_endian = *(char*)&endian;
- errno = 0;
- ptrace(op, tcp->pid, (void *) 1, (long) sig);
- err = errno;
- if (!err || err == ESRCH)
- return 0;
+ const uint8_t *array = bit_array;
+ unsigned pos = cur_bit / 8;
+ unsigned pos_xor_mask = little_endian ? 0 : current_wordsize-1;
- tcp->ptrace_errno = err;
- msg = "SYSCALL";
- if (op == PTRACE_CONT)
- msg = "CONT";
- if (op == PTRACE_DETACH)
- msg = "DETACH";
- perror_msg("ptrace(PTRACE_%s,1,%d)", msg, sig);
- return -1;
-}
+ for (;;) {
+ uint8_t bitmask;
+ uint8_t cur_byte;
+ if (cur_bit >= size_bits)
+ return -1;
+ cur_byte = array[pos ^ pos_xor_mask];
+ if (cur_byte == 0) {
+ cur_bit = (cur_bit + 8) & (-8);
+ pos++;
+ continue;
+ }
+ bitmask = 1 << (cur_bit & 7);
+ for (;;) {
+ if (cur_byte & bitmask)
+ return cur_bit;
+ cur_bit++;
+ if (cur_bit >= size_bits)
+ return -1;
+ bitmask <<= 1;
+ /* This check *can't be* optimized out: */
+ if (bitmask == 0)
+ break;
+ }
+ pos++;
+ }
+}
/*
* Print entry in struct xlat table, if there.
*/
void
-printxval(const struct xlat *xlat, int val, const char *dflt)
+printxval(const struct xlat *xlat, const unsigned int val, const char *dflt)
{
const char *str = xlookup(xlat, val);
tprintf("%#x /* %s */", val, dflt);
}
-#if HAVE_LONG_LONG
/*
- * Print 64bit argument at position llarg and return the index of the next
- * argument.
+ * Fetch 64bit argument at position arg_no and
+ * return the index of the next argument.
*/
int
-printllval(struct tcb *tcp, const char *format, int llarg)
+getllval(struct tcb *tcp, unsigned long long *val, int arg_no)
{
-# if defined(FREEBSD) \
- || (defined(LINUX) && defined(POWERPC) && !defined(POWERPC64)) \
- || defined(LINUX_MIPSO32) \
- || defined(__ARM_EABI__)
- /* Align 64bit argument to 64bit boundary. */
- llarg = (llarg + 1) & 0x1e;
+#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)
+ *val = tcp->u_arg[arg_no];
+ arg_no++;
+# if SUPPORTED_PERSONALITIES > 1
+ } else {
+# if defined(AARCH64) || defined(POWERPC64)
+ /* Align arg_no to the next even number. */
+ arg_no = (arg_no + 1) & 0xe;
+# endif /* AARCH64 || POWERPC64 */
+ *val = LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]);
+ arg_no += 2;
+ }
+# endif /* SUPPORTED_PERSONALITIES > 1 */
+#elif SIZEOF_LONG > 4
+# error Unsupported configuration: SIZEOF_LONG > 4 && SIZEOF_LONG_LONG > SIZEOF_LONG
+#elif defined LINUX_MIPSN32
+ *val = tcp->ext_arg[arg_no];
+ arg_no++;
+#elif defined X32
if (current_personality == 0) {
- tprintf(format, tcp->u_arg[llarg]);
- llarg++;
+ *val = tcp->ext_arg[arg_no];
+ arg_no++;
} else {
-# ifdef POWERPC64
- /* Align 64bit argument to 64bit boundary. */
- llarg = (llarg + 1) & 0x1e;
-# endif
- tprintf(format, LONG_LONG(tcp->u_arg[llarg], tcp->u_arg[llarg + 1]));
- llarg += 2;
+ *val = 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;
-}
+ *val = LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]);
+ arg_no += 2;
#endif
+ return arg_no;
+}
+
+/*
+ * Print 64bit argument at position arg_no and
+ * return the index of the next argument.
+ */
+int
+printllval(struct tcb *tcp, const char *format, int arg_no)
+{
+ unsigned long long val = 0;
+
+ arg_no = getllval(tcp, &val, arg_no);
+ tprintf(format, val);
+ return arg_no;
+}
+
/*
* Interpret `xlat' as an array of flags
* print the entries whose bits are on in `flags'
if (found)
*outptr++ = '|';
outptr = stpcpy(outptr, xlat->str);
- flags &= ~xlat->val;
found = 1;
+ flags &= ~xlat->val;
+ if (!flags)
+ break;
}
}
if (flags) {
}
void
-printnum(struct tcb *tcp, long addr, const char *fmt)
+printnum_long(struct tcb *tcp, long addr, const char *fmt)
{
long num;
tprints("]");
}
-void
-printfd(struct tcb *tcp, int fd)
+const char *
+sprinttime(time_t t)
{
- const char *p;
-
- if (show_fd_path && (p = getfdpath(tcp, fd)))
- tprintf("%d<%s>", fd, p);
+ struct tm *tmp;
+ static char buf[sizeof(int) * 3 * 6];
+
+ if (t == 0) {
+ strcpy(buf, "0");
+ return buf;
+ }
+ tmp = localtime(&t);
+ if (tmp)
+ snprintf(buf, sizeof buf, "%02d/%02d/%02d-%02d:%02d:%02d",
+ tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
else
- tprintf("%d", fd);
+ snprintf(buf, sizeof buf, "%lu", (unsigned long) t);
+
+ return buf;
+}
+
+static char *
+getfdproto(struct tcb *tcp, int fd, char *buf, unsigned bufsize)
+{
+#if HAVE_SYS_XATTR_H
+ ssize_t r;
+ char path[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];
+
+ if (fd < 0)
+ return NULL;
+
+ sprintf(path, "/proc/%u/fd/%u", tcp->pid, fd);
+ r = getxattr(path, "system.sockprotoname", buf, bufsize - 1);
+ if (r <= 0)
+ return NULL;
+ else {
+ /*
+ * This is a protection for the case when the kernel
+ * side does not append a null byte to the buffer.
+ */
+ buf[r] = '\0';
+ return buf;
+ }
+#else
+ return NULL;
+#endif
}
void
-printuid(const char *text, unsigned long uid)
+printfd(struct tcb *tcp, int fd)
{
- tprintf((uid == -1) ? "%s%ld" : "%s%lu", text, uid);
+ char path[PATH_MAX + 1];
+ if (show_fd_path && getfdpath(tcp, fd, path, sizeof(path)) >= 0) {
+ static const char socket_prefix[] = "socket:[";
+ const size_t socket_prefix_len = sizeof(socket_prefix) - 1;
+ const size_t path_len = strlen(path);
+
+ tprintf("%d<", fd);
+ if (show_fd_path > 1 &&
+ strncmp(path, socket_prefix, socket_prefix_len) == 0 &&
+ path[path_len - 1] == ']') {
+ unsigned long inodenr;
+#define PROTO_NAME_LEN 32
+ char proto_buf[PROTO_NAME_LEN];
+ const char *proto =
+ getfdproto(tcp, fd, proto_buf, PROTO_NAME_LEN);
+ inodenr = strtoul(path + socket_prefix_len, NULL, 10);
+ if (!print_sockaddr_by_inode(inodenr, proto)) {
+ if (proto)
+ tprintf("%s:[%lu]", proto, inodenr);
+ else
+ tprints(path);
+ }
+ } else {
+ print_quoted_string(path, path_len,
+ QUOTE_OMIT_LEADING_TRAILING_QUOTES);
+ }
+ tprints(">");
+ } else
+ tprintf("%d", fd);
}
/*
* 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
- * and quote at most (`size' - 1) bytes.
*
- * Returns 0 if len < 0 and NUL was seen, 1 otherwise.
- * Note that if len >= 0, always returns 1.
+ * If QUOTE_0_TERMINATED `style' flag is set,
+ * treat `instr' as a NUL-terminated string,
+ * checking up to (`size' + 1) bytes of `instr'.
+ *
+ * If QUOTE_OMIT_LEADING_TRAILING_QUOTES `style' flag is set,
+ * do not add leading and trailing quoting symbols.
+ *
+ * Returns 0 if QUOTE_0_TERMINATED is set and NUL was seen, 1 otherwise.
+ * Note that if QUOTE_0_TERMINATED is not set, always returns 1.
*/
static int
-string_quote(const char *instr, char *outstr, int len, int size)
+string_quote(const char *instr, char *outstr, const unsigned int size,
+ const unsigned int style)
{
const unsigned char *ustr = (const unsigned char *) instr;
char *s = outstr;
- int usehex, c, i, eol;
+ unsigned int i;
+ int usehex, c, eol;
- eol = 0x100; /* this can never match a char */
- if (len < 0) {
- size--;
+ if (style & QUOTE_0_TERMINATED)
eol = '\0';
- }
+ else
+ eol = 0x100; /* this can never match a char */
usehex = 0;
if (xflag > 1)
/* Check for NUL-terminated string. */
if (c == eol)
break;
- if (!isprint(c) && !isspace(c)) {
+
+ /* Force hex unless c is printable or whitespace */
+ if (c > 0x7e) {
+ usehex = 1;
+ break;
+ }
+ /* 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;
}
}
}
- *s++ = '\"';
+ if (!(style & QUOTE_OMIT_LEADING_TRAILING_QUOTES))
+ *s++ = '\"';
if (usehex) {
/* Hex-quote the whole string. */
*s++ = 'v';
break;
default:
- if (isprint(c))
+ if (c >= ' ' && c <= 0x7e)
*s++ = c;
else {
/* Print \octal */
}
}
- *s++ = '\"';
+ if (!(style & QUOTE_OMIT_LEADING_TRAILING_QUOTES))
+ *s++ = '\"';
*s = '\0';
/* Return zero if we printed entire ASCIZ string (didn't truncate it) */
- if (len < 0 && ustr[i] == '\0') {
+ if (style & QUOTE_0_TERMINATED && ustr[i] == '\0') {
/* We didn't see NUL yet (otherwise we'd jump to 'asciz_ended')
* but next char is NUL.
*/
return 1;
asciz_ended:
- *s++ = '\"';
+ if (!(style & QUOTE_OMIT_LEADING_TRAILING_QUOTES))
+ *s++ = '\"';
*s = '\0';
/* Return zero: we printed entire ASCIZ string (didn't truncate it) */
return 0;
}
+#ifndef ALLOCA_CUTOFF
+# define ALLOCA_CUTOFF 4032
+#endif
+#define use_alloca(n) ((n) <= ALLOCA_CUTOFF)
+
+/*
+ * Quote string `str' of length `size' and print the result.
+ *
+ * If QUOTE_0_TERMINATED `style' flag is set,
+ * treat `str' as a NUL-terminated string and
+ * quote at most (`size' - 1) bytes.
+ *
+ * If QUOTE_OMIT_LEADING_TRAILING_QUOTES `style' flag is set,
+ * do not add leading and trailing quoting symbols.
+ *
+ * Returns 0 if QUOTE_0_TERMINATED is set and NUL was seen, 1 otherwise.
+ * Note that if QUOTE_0_TERMINATED is not set, always returns 1.
+ */
+int
+print_quoted_string(const char *str, unsigned int size,
+ const unsigned int style)
+{
+ char *buf;
+ char *outstr;
+ unsigned int alloc_size;
+ int rc;
+
+ if (size && style & QUOTE_0_TERMINATED)
+ --size;
+
+ alloc_size = 4 * size;
+ if (alloc_size / 4 != size) {
+ error_msg("Out of memory");
+ tprints("???");
+ return -1;
+ }
+ alloc_size += 1 + (style & QUOTE_OMIT_LEADING_TRAILING_QUOTES ? 0 : 2);
+
+ if (use_alloca(alloc_size)) {
+ outstr = alloca(alloc_size);
+ buf = NULL;
+ } else {
+ outstr = buf = malloc(alloc_size);
+ if (!buf) {
+ error_msg("Out of memory");
+ tprints("???");
+ return -1;
+ }
+ }
+
+ rc = string_quote(str, outstr, size, style);
+ tprints(outstr);
+
+ free(buf);
+ return rc;
+}
+
/*
* Print path string specified by address `addr' and length `n'.
* If path length exceeds `n', append `...' to the output.
*/
void
-printpathn(struct tcb *tcp, long addr, int n)
+printpathn(struct tcb *tcp, long addr, unsigned int n)
{
- char path[MAXPATHLEN + 1];
+ char path[PATH_MAX + 1];
int nul_seen;
if (!addr) {
if (nul_seen < 0)
tprintf("%#lx", addr);
else {
- 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);
+ path[n++] = '\0';
+ print_quoted_string(path, n, QUOTE_0_TERMINATED);
if (!nul_seen)
tprints("...");
}
printpath(struct tcb *tcp, long addr)
{
/* Size must correspond to char path[] size in printpathn */
- printpathn(tcp, addr, MAXPATHLEN);
+ printpathn(tcp, addr, PATH_MAX);
}
/*
* 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;
+ unsigned int size;
+ unsigned int style;
int ellipsis;
if (!addr) {
}
/* Allocate static buffers if they are not allocated yet. */
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 (!str)
die_out_of_memory();
- outstr = malloc(4 * max_strlen + /*for quotes and NUL:*/ 3);
+ outstr = malloc(outstr_size);
if (!outstr)
die_out_of_memory();
}
- if (len < 0) {
+ size = max_strlen;
+ if (len == -1) {
/*
* Treat as a NUL-terminated string: fetch one byte more
- * because string_quote() quotes one byte less.
+ * because string_quote may look one byte ahead.
*/
- size = max_strlen + 1;
- if (umovestr(tcp, addr, size, str) < 0) {
+ if (umovestr(tcp, addr, size + 1, str) < 0) {
tprintf("%#lx", addr);
return;
}
+ style = QUOTE_0_TERMINATED;
}
else {
- size = MIN(len, max_strlen);
+ if (size > (unsigned long)len)
+ size = (unsigned long)len;
if (umoven(tcp, addr, size, str) < 0) {
tprintf("%#lx", addr);
return;
}
+ style = 0;
}
/* 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));
+ ellipsis = (string_quote(str, outstr, size, style) &&
+ (len < 0 || (unsigned long) len > max_strlen));
tprints(outstr);
if (ellipsis)
tprints("...");
}
-#if HAVE_SYS_UIO_H
void
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)
fprintf(stderr, "Out of memory\n");
return;
}
- if (umoven(tcp, addr, size, (char *) iov) >= 0) {
+ if (umoven(tcp, addr, size, iov) >= 0) {
for (i = 0; i < len; i++) {
/* include the buffer number to make it easy to
* match up the trace with the source */
#undef iov_iov_len
#undef iov
}
-#endif
void
dumpstr(struct tcb *tcp, long addr, int len)
{
static int strsize = -1;
static unsigned char *str;
- char *s;
- int i, j;
- if (strsize < len) {
+ 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);
+ 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)
+ if (umoven(tcp, addr, len, str) < 0)
return;
- for (i = 0; i < len; i += 16) {
- char outstr[80];
-
- 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"
#if defined(__NR_process_vm_readv)
static bool process_vm_readv_not_supported = 0;
-static ssize_t process_vm_readv(pid_t pid,
+/* 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,
{
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
-/* end of hack */
+#endif /* end of hack */
#define PAGMASK (~(PAGSIZ - 1))
/*
* move `len' bytes of data from process `pid'
- * at address `addr' to our space at `laddr'
+ * at address `addr' to our space at `our_addr'
*/
int
-umoven(struct tcb *tcp, long addr, int len, char *laddr)
+umoven(struct tcb *tcp, long addr, unsigned int len, void *our_addr)
{
-#ifdef LINUX
+ char *laddr = our_addr;
int pid = tcp->pid;
- int n, m;
- int started;
+ unsigned 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,
- /*flags:*/ 0
- );
- if (r < 0) {
- if (errno == ENOSYS)
+ r = process_vm_readv(pid, local, 1, remote, 1, 0);
+ if ((unsigned int) r == len)
+ return 0;
+ if (r >= 0) {
+ error_msg("umoven: short read (%u < %u) @0x%lx",
+ (unsigned int) r, len, addr);
+ return -1;
+ }
+ switch (errno) {
+ case ENOSYS:
process_vm_readv_not_supported = 1;
- else if (errno != EINVAL) /* EINVAL is seen if process is gone */
- /* strange... */
- perror("process_vm_readv");
- goto vm_readv_didnt_work;
+ 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;
}
- return r;
}
- vm_readv_didnt_work:
-#if SUPPORTED_PERSONALITIES > 1
- if (personality_wordsize[current_personality] < sizeof(addr))
- addr &= (1ul << 8 * personality_wordsize[current_personality]) - 1;
-#endif
-
- started = 0;
+ nread = 0;
if (addr & (sizeof(long) - 1)) {
/* addr not a multiple of sizeof(long) */
- n = addr - (addr & -sizeof(long)); /* residue */
- addr &= -sizeof(long); /* residue */
+ n = addr & (sizeof(long) - 1); /* residue */
+ addr &= -sizeof(long); /* aligned address */
errno = 0;
u.val = ptrace(PTRACE_PEEKDATA, pid, (char *) addr, 0);
- if (errno) {
- /* 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;
m = MIN(sizeof(long) - n, len);
memcpy(laddr, &u.x[n], m);
- addr += sizeof(long), laddr += m, len -= 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;
+ 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 (%u < %u) @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;
}
- started = 1;
m = MIN(sizeof(long), len);
memcpy(laddr, u.x, m);
- 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;
- }
- len -= n;
- addr += n;
- laddr += n;
+ 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;
}
* we never write past laddr[len-1]).
*/
int
-umovestr(struct tcb *tcp, long addr, int len, char *laddr)
+umovestr(struct tcb *tcp, long addr, unsigned int len, char *laddr)
{
-#ifdef USE_PROCFS
-# ifdef HAVE_MP_PROCFS
- int fd = tcp->pfd_as;
-# else
- int fd = tcp->pfd;
-# 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;
- move = read(fd, laddr, move);
- if (move <= 0)
- return left != len ? 0 : -1;
- if (memchr(laddr, 0, move))
- return 1;
- left -= move;
- laddr += move;
- addr += move;
- move = page;
- }
- return 0;
-#else /* !USE_PROCFS */
- int started;
+#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
+# error SIZEOF_LONG > 8
+#endif
+
int pid = tcp->pid;
- int i, n, m;
+ unsigned int n, m, nread;
union {
- long val;
+ unsigned long val;
char x[sizeof(long)];
} u;
-#if SUPPORTED_PERSONALITIES > 1
- if (personality_wordsize[current_personality] < sizeof(addr))
- addr &= (1ul << 8 * personality_wordsize[current_personality]) - 1;
+#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];
remote[0].iov_base = (void*)addr;
while (len > 0) {
- int end_in_page;
+ unsigned int chunk_len;
+ unsigned int end_in_page;
int r;
- int chunk_len;
/* Don't read kilobytes: most strings are short */
chunk_len = len;
* (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 */
+ if (chunk_len > end_in_page) /* crosses to the next page */
+ chunk_len -= end_in_page;
local[0].iov_len = remote[0].iov_len = chunk_len;
- r = process_vm_readv(pid,
- local, 1,
- remote, 1,
- /*flags:*/ 0
- );
- if (r < 0) {
- if (errno == ENOSYS)
+ 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;
- else if (errno != EINVAL) /* EINVAL is seen if process is gone */
- /* strange... */
- perror("process_vm_readv");
- goto vm_readv_didnt_work;
+ 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;
}
- if (memchr(local[0].iov_base, '\0', r))
- return 1;
- local[0].iov_base += r;
- remote[0].iov_base += r;
- len -= r;
}
return 0;
}
vm_readv_didnt_work:
- started = 0;
if (addr & (sizeof(long) - 1)) {
/* addr not a multiple of sizeof(long) */
- n = addr - (addr & -sizeof(long)); /* residue */
- addr &= -sizeof(long); /* residue */
+ n = addr & (sizeof(long) - 1); /* residue */
+ addr &= -sizeof(long); /* aligned address */
errno = 0;
u.val = ptrace(PTRACE_PEEKDATA, pid, (char *)addr, 0);
- if (errno) {
- 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;
m = MIN(sizeof(long) - n, len);
memcpy(laddr, &u.x[n], m);
while (n & (sizeof(long) - 1))
if (u.x[n++] == '\0')
return 1;
- addr += sizeof(long), laddr += m, len -= 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("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 */
+ 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;
}
- started = 1;
m = MIN(sizeof(long), len);
memcpy(laddr, u.x, m);
- for (i = 0; i < sizeof(long); i++)
- if (u.x[i] == '\0')
- return 1;
- 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(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;
- }
- m = MIN(sizeof(long) - n, len);
- memcpy(&u.x[n], laddr, m);
- 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);
- m = MIN(sizeof(long), len);
- memcpy(u.x, laddr, m);
- if (ptrace(poke, pid, (char *) addr, u.val) < 0) {
- perror("uload: POKE");
- return -1;
- }
- addr += sizeof(long), laddr += m, len -= 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(int pid, int addr, int len, char *laddr)
-{
- return uload(PTRACE_WRITETEXT, pid, addr, len, laddr);
-}
-
-int
-dload(int pid, int addr, int len, char *laddr)
-{
- return uload(PTRACE_WRITEDATA, pid, addr, len, laddr);
-}
-
-#endif /* SUNOS4 */
-
-#ifndef USE_PROCFS
-
-int
-upeek(struct tcb *tcp, long off, long *res)
+upeek(int pid, 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, (pid_t)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)", pid, off);
}
return -1;
}
*res = val;
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) {
- tprints("[????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(ALPHA)
- long pc;
-
- if (upeek(tcp, REG_PC, &pc) < 0) {
- tprints("[????????????????] ");
- 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) {
- tprints("[????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(MIPS)
- long pc;
-
- if (upeek(tcp, REG_EPC, &pc) < 0) {
- tprints("[????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(SH)
- long pc;
-
- if (upeek(tcp, 4*REG_PC, &pc) < 0) {
- tprints("[????????] ");
- return;
- }
- tprintf("[%08lx] ", pc);
-# elif defined(SH64)
- long pc;
-
- if (upeek(tcp, REG_PC, &pc) < 0) {
- tprints("[????????????????] ");
- 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) {
- tprints("[????????] ");
- 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 */
-}
-
-
-/*
- * 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
-
-# ifdef IA64
-
-/* 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
-
-typedef unsigned long *arg_setup_state;
-
-static int
-arg_setup(struct tcb *tcp, arg_setup_state *state)
-{
- unsigned long cfm, sof, sol;
- long bsp;
-
- if (ia32) {
- /* Satisfy a false GCC warning. */
- *state = NULL;
- return 0;
- }
-
- if (upeek(tcp, PT_AR_BSP, &bsp) < 0)
- return -1;
- if (upeek(tcp, PT_CFM, (long *) &cfm) < 0)
- return -1;
-
- sof = (cfm >> 0) & 0x7f;
- sol = (cfm >> 7) & 0x7f;
- bsp = (long) ia64_rse_skip_regs((unsigned long *) bsp, -sof + sol);
-
- *state = (unsigned long *) bsp;
- return 0;
-}
-
-# define arg_finish_change(tcp, state) 0
-
-# ifdef SYS_fork
-static int
-get_arg0(struct tcb *tcp, arg_setup_state *state, long *valp)
-{
- int ret;
-
- if (ia32)
- ret = upeek(tcp, PT_R11, valp);
- else
- 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)
-{
- int ret;
-
- if (ia32)
- ret = upeek(tcp, PT_R9, valp);
- else
- 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)
-{
- int req = PTRACE_POKEDATA;
- void *ap;
-
- if (ia32) {
- ap = (void *) (intptr_t) PT_R11; /* r11 == EBX */
- req = PTRACE_POKEUSER;
- } else
- ap = ia64_rse_skip_regs(*state, 0);
- errno = 0;
- ptrace(req, tcp->pid, ap, val);
- return errno ? -1 : 0;
-}
-
-static int
-set_arg1(struct tcb *tcp, arg_setup_state *state, long val)
-{
- int req = PTRACE_POKEDATA;
- void *ap;
-
- if (ia32) {
- ap = (void *) (intptr_t) PT_R9; /* r9 == ECX */
- req = PTRACE_POKEUSER;
- } else
- ap = ia64_rse_skip_regs(*state, 1);
- errno = 0;
- ptrace(req, tcp->pid, ap, val);
- return errno ? -1 : 0;
-}
-
-/* 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)
-
-# elif defined (SPARC) || defined (SPARC64)
-
-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 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 */
-
-# 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
-
-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)))
-
-static int
-set_arg0(struct tcb *tcp, void *cookie, long val)
-{
- return ptrace(PTRACE_POKEUSER, tcp->pid, (char*)arg0_offset, val);
-}
-
-static int
-set_arg1(struct tcb *tcp, void *cookie, long val)
-{
- return ptrace(PTRACE_POKEUSER, tcp->pid, (char*)arg1_offset, val);
-}
-
-# 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 arg0_index
-# define arg0_index 0
-# define arg1_index 1
-# endif
-
-int
-setbpt(struct tcb *tcp)
-{
- static int clone_scno[SUPPORTED_PERSONALITIES] = { SYS_clone };
- arg_setup_state state;
-
- if (tcp->flags & TCB_BPTSET) {
- fprintf(stderr, "PANIC: TCB already set in pid %u\n", tcp->pid);
- return -1;
- }
-
- /*
- * It's a silly kludge to initialize this with a search at runtime.
- * But it's better than maintaining another magic thing in the
- * godforsaken tables.
- */
- if (clone_scno[current_personality] == 0) {
- int i;
- for (i = 0; i < nsyscalls; ++i)
- if (sysent[i].sys_func == sys_clone) {
- clone_scno[current_personality] = i;
- break;
- }
- }
-
- 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)
- 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
- /* ia64 calls directly `clone (CLONE_VFORK | CLONE_VM)'
- contrary to x86 SYS_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. */
-
- 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];
- return 0;
-
- default:
- fprintf(stderr, "PANIC: setbpt for syscall %ld on %u???\n",
- tcp->scno, tcp->pid);
- break;
- }
-
- return -1;
-}
-
-int
-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(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(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(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)
- 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(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;
- }
- strtab = malloc((unsigned)ld.ld_symb_size);
- if (!strtab)
- die_out_of_memory();
- 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);
- return 0;
-
-err:
- free(strtab);
- return -1;
-}
-
-#endif /* SUNOS4 */