X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=util.c;h=6acdbc2946805629294e2c43586b12240ed670b3;hb=ea0d2a60b1c00b41696ef223df2377809b9e1fa9;hp=c28edf3be538de233a2c21b5c2268f7bb09987fa;hpb=30145dda9d7ff70df1d5ad750a183572c73e8963;p=strace diff --git a/util.c b/util.c index c28edf3b..6acdbc29 100644 --- a/util.c +++ b/util.c @@ -29,80 +29,60 @@ * 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 -#include #include #include #include #if HAVE_SYS_UIO_H -#include -#endif -#ifdef SUNOS4 -#include -#include -#include -#endif /* SUNOS4 */ - -#if defined(linux) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 1)) -#include +# include #endif -#if defined(LINUX) && defined(IA64) +#if defined(IA64) # include # include #endif #ifdef HAVE_SYS_REG_H -#include +# include # 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 +# include # undef ia64_fpreg # undef pt_all_user_regs #endif -#ifdef SUNOS4_KERNEL_ARCH_KLUDGE -#include -#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)) @@ -114,15 +94,13 @@ struct timeval *a, *b; } 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; @@ -133,8 +111,7 @@ struct timeval *tv, *a, *b; } 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; @@ -145,9 +122,7 @@ struct timeval *tv, *a, *b; } 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; @@ -155,9 +130,7 @@ int n; } 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; @@ -173,60 +146,15 @@ xlookup(const struct xlat *xlat, int val) 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. @@ -237,79 +165,83 @@ printxval(const struct xlat *xlat, int val, const char *dflt) 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. */ @@ -317,23 +249,26 @@ const char * 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; @@ -346,7 +281,7 @@ printflags(const struct xlat *xlat, int flags, const char *dflt) const char *sep; if (flags == 0 && xlat->val == 0) { - tprintf("%s", xlat->str); + tprints(xlat->str); return 1; } @@ -372,7 +307,7 @@ printflags(const struct xlat *xlat, int flags, const char *dflt) tprintf(" /* %s */", dflt); } else { if (dflt) - tprintf("0"); + tprints("0"); } } @@ -385,16 +320,16 @@ 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 @@ -403,42 +338,58 @@ 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) { @@ -447,14 +398,18 @@ string_quote(const char *instr, char *outstr, int len, int size) 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; } @@ -468,27 +423,19 @@ string_quote(const char *instr, char *outstr, int len, int size) 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++ = '\\'; @@ -515,15 +462,27 @@ string_quote(const char *instr, char *outstr, int len, int size) *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; } @@ -533,8 +492,21 @@ string_quote(const char *instr, char *outstr, int len, int size) *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; } /* @@ -544,37 +516,40 @@ string_quote(const char *instr, char *outstr, int len, int size) 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); } /* @@ -583,76 +558,79 @@ printpath(struct tcb *tcp, long addr) * 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) @@ -660,12 +638,13 @@ long addr; #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) { @@ -678,7 +657,7 @@ long addr; iov_iov_len(i)); } } - free((char *) iov); + free(iov); #undef sizeof_iov #undef iov_iov_base #undef iov_iov_len @@ -687,62 +666,116 @@ long addr; #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' @@ -751,302 +784,287 @@ int len; 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; } @@ -1054,221 +1072,31 @@ long *res; 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 -# 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; @@ -1297,40 +1125,38 @@ arg_setup(struct tcb *tcp, arg_setup_state *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; @@ -1346,7 +1172,7 @@ set_arg0 (struct tcb *tcp, arg_setup_state *state, long val) } 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; @@ -1364,110 +1190,225 @@ set_arg1 (struct tcb *tcp, arg_setup_state *state, long val) /* 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) @@ -1494,279 +1435,61 @@ 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 */