Index: stable/10/usr.bin/truss/amd64-fbsd.c =================================================================== --- stable/10/usr.bin/truss/amd64-fbsd.c (revision 290051) +++ stable/10/usr.bin/truss/amd64-fbsd.c (revision 290052) @@ -1,339 +1,131 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/amd64-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ +/* FreeBSD/amd64-specific system call handling. */ -#include #include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) +static int +amd64_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in amd64/amd64/trap.c - * is ever changed these functions need to keep up. - */ - -void -amd64_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, reg, syscall_num; + u_int i, reg; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over %rax if it contains + * either of these values. */ reg = 0; - syscall_num = regs.r_rax; - switch (syscall_num) { + switch (regs.r_rax) { case SYS_syscall: case SYS___syscall: - syscall_num = regs.r_rdi; reg++; break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - for (i = 0; i < nargs && reg < 6; i++, reg++) { + for (i = 0; i < narg && reg < 6; i++, reg++) { switch (reg) { - case 0: fsc->args[i] = regs.r_rdi; break; - case 1: fsc->args[i] = regs.r_rsi; break; - case 2: fsc->args[i] = regs.r_rdx; break; - case 3: fsc->args[i] = regs.r_rcx; break; - case 4: fsc->args[i] = regs.r_r8; break; - case 5: fsc->args[i] = regs.r_r9; break; + case 0: cs->args[i] = regs.r_rdi; break; + case 1: cs->args[i] = regs.r_rsi; break; + case 2: cs->args[i] = regs.r_rdx; break; + case 3: cs->args[i] = regs.r_rcx; break; + case 4: cs->args[i] = regs.r_r8; break; + case 5: cs->args[i] = regs.r_r9; break; } } - if (nargs > i) { + if (narg > i) { iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.r_rsp + sizeof(register_t)); - iorequest.piod_addr = &fsc->args[i]; - iorequest.piod_len = (nargs - i) * sizeof(register_t); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(register_t); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; + return (-1); } - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%lx%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -amd64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +amd64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_rax; - errorp = !!(regs.r_rflags & PSL_C); + retval[0] = regs.r_rax; + retval[1] = regs.r_rdx; + *errorp = !!(regs.r_rflags & PSL_C); + return (0); +} - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ +static struct procabi amd64_fbsd = { + "FreeBSD ELF64", + syscallnames, + nitems(syscallnames), + amd64_fetch_args, + amd64_fetch_retval +}; - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(amd64_fbsd); Index: stable/10/usr.bin/truss/amd64-fbsd32.c =================================================================== --- stable/10/usr.bin/truss/amd64-fbsd32.c (revision 290051) +++ stable/10/usr.bin/truss/amd64-fbsd32.c (revision 290052) @@ -1,340 +1,137 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/i386-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ +/* FreeBSD/i386-specific system call handling. */ -#include #include #include #include #include -#include -#include -#include #include #include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "freebsd32_syscalls.h" -static int nsyscalls = sizeof(freebsd32_syscallnames) / - sizeof(freebsd32_syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd32_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - unsigned int *args32; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd32_syscall * -alloc_fsc(void) +static int +amd64_fbsd32_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct freebsd32_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd32_syscall *fsc) -{ - int i; - - free(fsc->args); - free(fsc->args32); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -amd64_fbsd32_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd32_syscall *fsc; - struct syscall *sc; - lwpid_t tid; + struct current_syscall *cs; + unsigned int args32[narg]; unsigned long parm_offset; - int i, syscall_num; + lwpid_t tid; + u_int i; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } parm_offset = regs.r_rsp + sizeof(int); /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - syscall_num = regs.r_rax; - switch (syscall_num) { + switch (regs.r_rax) { case SYS_syscall: - syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); parm_offset += sizeof(int); break; case SYS___syscall: - syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); parm_offset += sizeof(quad_t); break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : freebsd32_syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args32 = malloc((1 + nargs) * sizeof(unsigned int)); iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)parm_offset; - iorequest.piod_addr = fsc->args32; - iorequest.piod_len = (1 + nargs) * sizeof(unsigned int); + iorequest.piod_addr = args32; + iorequest.piod_len = sizeof(args32); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); - if (iorequest.piod_len == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - for (i = 0; i < nargs + 1; i++) - fsc->args[i] = fsc->args32[i]; - - sc = NULL; - if (fsc->name) - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; + if (iorequest.piod_len == 0) { + return (-1); } - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "freebsd32_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "freebsd32_execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + for (i = 0; i < narg; i++) + cs->args[i] = args32[i]; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -amd64_fbsd32_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +amd64_fbsd32_fetch_retval(struct trussinfo *trussinfo, long *retval, + int *errorp) { struct reg regs; - struct freebsd32_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_rax; - errorp = !!(regs.r_rflags & PSL_C); + retval[0] = regs.r_rax & 0xffffffff; + retval[1] = regs.r_rdx & 0xffffffff; + *errorp = !!(regs.r_rflags & PSL_C); + return (0); +} - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ +static struct procabi amd64_fbsd32 = { + "FreeBSD ELF32", + freebsd32_syscallnames, + nitems(freebsd32_syscallnames), + amd64_fbsd32_fetch_args, + amd64_fbsd32_fetch_retval +}; - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } +PROCABI(amd64_fbsd32); - if (fsc->name != NULL && (strcmp(fsc->name, "freebsd32_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; +static struct procabi amd64_fbsd32_aout = { + "FreeBSD a.out", + freebsd32_syscallnames, + nitems(freebsd32_syscallnames), + amd64_fbsd32_fetch_args, + amd64_fbsd32_fetch_retval +}; - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(amd64_fbsd32_aout); Index: stable/10/usr.bin/truss/amd64-linux32.c =================================================================== --- stable/10/usr.bin/truss/amd64-linux32.c (revision 290051) +++ stable/10/usr.bin/truss/amd64-linux32.c (revision 290052) @@ -1,329 +1,141 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * Linux/i386-specific system call handling. Given how much of this code - * is taken from the freebsd equivalent, I can probably put even more of - * it in support routines that can be used by any personality support. - */ +/* Linux/i386-specific system call handling. */ -#include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "linux32_syscalls.h" -static int nsyscalls = - sizeof(linux32_syscallnames) / sizeof(linux32_syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct linux_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long args[5]; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct linux_syscall * -alloc_fsc(void) +static int +amd64_linux32_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct linux_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct linux_syscall *fsc) -{ - int i; - - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -amd64_linux32_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct reg regs; - struct linux_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } - syscall_num = regs.r_rax; - - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : linux32_syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "linux_fork") == 0 || - strcmp(fsc->name, "linux_vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - /* * Linux passes syscall arguments in registers, not * on the stack. Fortunately, we've got access to the * register set. Note that we don't bother checking the * number of arguments. And what does linux do for syscalls * that have more than five arguments? */ - - fsc->args[0] = regs.r_rbx; - fsc->args[1] = regs.r_rcx; - fsc->args[2] = regs.r_rdx; - fsc->args[3] = regs.r_rsi; - fsc->args[4] = regs.r_rdi; - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; + switch (narg) { + default: + cs->args[5] = regs.r_rbp; /* Unconfirmed */ + case 5: + cs->args[4] = regs.r_rdi; + case 4: + cs->args[3] = regs.r_rsi; + case 3: + cs->args[2] = regs.r_rdx; + case 2: + cs->args[1] = regs.r_rcx; + case 1: + cs->args[0] = regs.r_rbx; } - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "linux_execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } /* * Linux syscalls return negative errno's, we do positive and map them */ static const int bsd_to_linux_errno[] = { -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -35, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -11,-115,-114, -88, -89, -90, -91, -92, -93, -94, -95, -96, -97, -98, -99, -100,-101,-102,-103,-104,-105,-106,-107,-108,-109, -110,-111, -40, -36,-112,-113, -39, -11, -87,-122, -116, -66, -6, -6, -6, -6, -6, -37, -38, -9, -6, }; -long -amd64_linux32_syscall_exit(struct trussinfo *trussinfo, - int syscall_num __unused) +static int +amd64_linux32_fetch_retval(struct trussinfo *trussinfo, long *retval, + int *errorp) { struct reg regs; - struct linux_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; + size_t i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_rax; - errorp = !!(regs.r_rflags & PSL_C); + retval[0] = regs.r_rax & 0xffffffff; + retval[1] = regs.r_rdx & 0xffffffff; + *errorp = !!(regs.r_rflags & PSL_C); - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; + if (*errorp) { + for (i = 0; i < nitems(bsd_to_linux_errno); i++) { + if (retval[0] == bsd_to_linux_errno[i]) { + retval[0] = i; + return (0); } } - } - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - if (errorp) { - for (i = 0; - (size_t)i < sizeof(bsd_to_linux_errno) / sizeof(int); i++) { - if (retval == bsd_to_linux_errno[i]) - break; - } + /* XXX: How to handle unknown errors? */ } + return (0); +} - if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; +static struct procabi amd64_linux32 = { + "Linux ELF32", + linux32_syscallnames, + nitems(linux32_syscallnames), + amd64_linux32_fetch_args, + amd64_linux32_fetch_retval +}; - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - errorp ? i : retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(amd64_linux32); Index: stable/10/usr.bin/truss/arm-fbsd.c =================================================================== --- stable/10/usr.bin/truss/arm-fbsd.c (revision 290051) +++ stable/10/usr.bin/truss/arm-fbsd.c (revision 290052) @@ -1,370 +1,138 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -/* - * FreeBSD/arm-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ - #include __FBSDID("$FreeBSD$"); -#include + +/* FreeBSD/arm-specific system call handling. */ + #include #include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" - -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) +static int +arm_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -arm_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; - register_t *ap; + u_int i, reg, syscall_num; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } - ap = ®s.r[0]; /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ #ifdef __ARM_EABI__ syscall_num = regs.r[7]; #else - if ((syscall_num = ptrace(PT_READ_I, tid, + if ((syscall_num = ptrace(PT_READ_I, tid, (caddr_t)(regs.r[_REG_PC] - INSN_SIZE), 0)) == -1) { fprintf(trussinfo->outfile, "-- CANNOT READ PC --\n"); - return; + return (-1); } syscall_num = syscall_num & 0x000fffff; #endif + + reg = 0; switch (syscall_num) { case SYS_syscall: - syscall_num = *ap++; - nargs--; + reg = 1; break; case SYS___syscall: - syscall_num = ap[_QUAD_LOWWORD]; - ap += 2; - nargs -= 2; + reg = 2; break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - switch (nargs) { - default: - /* - * The OS doesn't seem to allow more than 10 words of - * parameters (yay!). So we shouldn't be here. - */ - warn("More than 10 words (%d) of arguments!\n", nargs); - break; - case 10: - case 9: - case 8: - case 7: - case 6: - case 5: - /* - * If there are 7-10 words of arguments, they are placed - * on the stack, as is normal for other processors. - * The fall-through for all of these is deliberate!!! - */ - // XXX BAD constant used here + for (i = 0; i < narg && reg < 4; i++, reg++) + cs->args[i] = regs.r[reg]; + if (narg > i) { iorequest.piod_op = PIOD_READ_D; - iorequest.piod_offs = (void *)(regs.r[_REG_SP] + + iorequest.piod_offs = (void *)(regs.r_sp + 4 * sizeof(uint32_t)); - iorequest.piod_addr = &fsc->args[4]; - iorequest.piod_len = (nargs - 4) * sizeof(fsc->args[0]); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - case 4: fsc->args[3] = ap[3]; - case 3: fsc->args[2] = ap[2]; - case 2: fsc->args[1] = ap[1]; - case 1: fsc->args[0] = ap[0]; - case 0: break; + return (-1); } - sc = NULL; - if (fsc->name) - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -arm_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +arm_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r[0]; - errorp = !!(regs.r_cpsr & PSR_C); + /* XXX: Does not have the __ARMEB__ handling for __syscall(). */ + retval[0] = regs.r[0]; + retval[1] = regs.r[1]; + *errorp = !!(regs.r_cpsr & PSR_C); + return (0); +} - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ +static struct procabi arm_fbsd = { + "FreeBSD ELF32", + syscallnames, + nitems(syscallnames), + arm_fetch_args, + arm_fetch_retval +}; - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(arm_fbsd); Index: stable/10/usr.bin/truss/extern.h =================================================================== --- stable/10/usr.bin/truss/extern.h (revision 290051) +++ stable/10/usr.bin/truss/extern.h (revision 290052) @@ -1,76 +1,39 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. * * $FreeBSD$ */ -extern int setup_and_wait(char **); -extern int start_tracing(pid_t); +extern void setup_and_wait(struct trussinfo *, char **); +extern void start_tracing(struct trussinfo *, pid_t); extern void restore_proc(int); -extern void waitevent(struct trussinfo *); +extern void eventloop(struct trussinfo *); extern const char *ioctlname(unsigned long val); extern char *strsig(int sig); -#ifdef __arm__ -extern void arm_syscall_entry(struct trussinfo *, int); -extern long arm_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __amd64__ -extern void amd64_syscall_entry(struct trussinfo *, int); -extern long amd64_syscall_exit(struct trussinfo *, int); -extern void amd64_linux32_syscall_entry(struct trussinfo *, int); -extern long amd64_linux32_syscall_exit(struct trussinfo *, int); -extern void amd64_fbsd32_syscall_entry(struct trussinfo *, int); -extern long amd64_fbsd32_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __i386__ -extern void i386_syscall_entry(struct trussinfo *, int); -extern long i386_syscall_exit(struct trussinfo *, int); -extern void i386_linux_syscall_entry(struct trussinfo *, int); -extern long i386_linux_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __ia64__ -extern void ia64_syscall_entry(struct trussinfo *, int); -extern long ia64_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __powerpc__ -extern void powerpc_syscall_entry(struct trussinfo *, int); -extern long powerpc_syscall_exit(struct trussinfo *, int); -extern void powerpc64_syscall_entry(struct trussinfo *, int); -extern long powerpc64_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __sparc64__ -extern void sparc64_syscall_entry(struct trussinfo *, int); -extern long sparc64_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __mips__ -extern void mips_syscall_entry(struct trussinfo *, int); -extern long mips_syscall_exit(struct trussinfo *, int); -#endif - Index: stable/10/usr.bin/truss/i386-fbsd.c =================================================================== --- stable/10/usr.bin/truss/i386-fbsd.c (revision 290051) +++ stable/10/usr.bin/truss/i386-fbsd.c (revision 290052) @@ -1,333 +1,131 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/i386-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ +/* FreeBSD/i386-specific system call handling. */ -#include #include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) +static int +i386_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -i386_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; unsigned int parm_offset; - int i, syscall_num; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } parm_offset = regs.r_esp + sizeof(int); /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - syscall_num = regs.r_eax; - switch (syscall_num) { + switch (regs.r_eax) { case SYS_syscall: - syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); parm_offset += sizeof(int); break; case SYS___syscall: - syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); parm_offset += sizeof(quad_t); break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)parm_offset; - iorequest.piod_addr = fsc->args; - iorequest.piod_len = (1 + nargs) * sizeof(unsigned long); + iorequest.piod_addr = cs->args; + iorequest.piod_len = narg * sizeof(unsigned long); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; + return (-1); - sc = NULL; - if (fsc->name) - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +i386_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_eax; - errorp = !!(regs.r_eflags & PSL_C); + retval[0] = regs.r_eax; + retval[1] = regs.r_edx; + *errorp = !!(regs.r_eflags & PSL_C); + return (0); +} - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ +static struct procabi i386_fbsd = { + "FreeBSD ELF32", + syscallnames, + nitems(syscallnames), + i386_fetch_args, + i386_fetch_retval +}; - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } +PROCABI(i386_fbsd); - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; +static struct procabi i386_fbsd_aout = { + "FreeBSD a.out", + syscallnames, + nitems(syscallnames), + i386_fetch_args, + i386_fetch_retval +}; - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ +PROCABI(i386_fbsd_aout); - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} Index: stable/10/usr.bin/truss/i386-linux.c =================================================================== --- stable/10/usr.bin/truss/i386-linux.c (revision 290051) +++ stable/10/usr.bin/truss/i386-linux.c (revision 290052) @@ -1,328 +1,140 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * Linux/i386-specific system call handling. Given how much of this code - * is taken from the freebsd equivalent, I can probably put even more of - * it in support routines that can be used by any personality support. - */ +/* Linux/i386-specific system call handling. */ -#include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "linux_syscalls.h" -static int nsyscalls = - sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct linux_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long args[5]; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct linux_syscall * -alloc_fsc(void) +static int +i386_linux_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct linux_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct linux_syscall *fsc) -{ - int i; - - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -i386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct reg regs; - struct linux_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } - syscall_num = regs.r_eax; - - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : linux_syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "linux_fork") == 0 || - strcmp(fsc->name, "linux_vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - /* * Linux passes syscall arguments in registers, not * on the stack. Fortunately, we've got access to the * register set. Note that we don't bother checking the * number of arguments. And what does linux do for syscalls * that have more than five arguments? */ - - fsc->args[0] = regs.r_ebx; - fsc->args[1] = regs.r_ecx; - fsc->args[2] = regs.r_edx; - fsc->args[3] = regs.r_esi; - fsc->args[4] = regs.r_edi; - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; + switch (narg) { + default: + cs->args[5] = regs.r_ebp; /* Unconfirmed */ + case 5: + cs->args[4] = regs.r_edi; + case 4: + cs->args[3] = regs.r_esi; + case 3: + cs->args[2] = regs.r_edx; + case 2: + cs->args[1] = regs.r_ecx; + case 1: + cs->args[0] = regs.r_ebx; } - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "linux_execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } /* * Linux syscalls return negative errno's, we do positive and map them */ static const int bsd_to_linux_errno[] = { -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -35, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -11,-115,-114, -88, -89, -90, -91, -92, -93, -94, -95, -96, -97, -98, -99, -100,-101,-102,-103,-104,-105,-106,-107,-108,-109, -110,-111, -40, -36,-112,-113, -39, -11, -87,-122, -116, -66, -6, -6, -6, -6, -6, -37, -38, -9, -6, }; -long -i386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +i386_linux_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct linux_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; + size_t i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_eax; - errorp = !!(regs.r_eflags & PSL_C); + retval[0] = regs.r_eax; + retval[1] = regs.r_edx; + *errorp = !!(regs.r_eflags & PSL_C); - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; + if (*errorp) { + for (i = 0; i < nitems(bsd_to_linux_errno); i++) { + if (retval[0] == bsd_to_linux_errno[i]) { + retval[0] = i; + return (0); } } - } - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - if (errorp) { - for (i = 0; - (size_t)i < sizeof(bsd_to_linux_errno) / sizeof(int); i++) { - if (retval == bsd_to_linux_errno[i]) - break; - } + /* XXX: How to handle unknown errors? */ } + return (0); +} - if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; +static struct procabi i386_linux = { + "Linux ELF32", + linux_syscallnames, + nitems(linux_syscallnames), + i386_linux_fetch_args, + i386_linux_fetch_retval +}; - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - errorp ? i : retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(i386_linux); Index: stable/10/usr.bin/truss/ia64-fbsd.c =================================================================== --- stable/10/usr.bin/truss/ia64-fbsd.c (revision 290051) +++ stable/10/usr.bin/truss/ia64-fbsd.c (revision 290052) @@ -1,314 +1,112 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/ia64-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ +/* FreeBSD/ia64-specific system call handling. */ -#include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) +static int +ia64_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in ia64/ia64/trap.c - * is ever changed these functions need to keep up. - */ - -void -ia64_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; unsigned long *parm_offset; lwpid_t tid; - int i, syscall_num; + int i; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return; } parm_offset = ®s.r_scratch.gr16; /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over %rax if it contains + * either of these values. */ - syscall_num = regs.r_scratch.gr15; /* XXX double-check. */ - if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) - syscall_num = (int)*parm_offset++; - - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); + switch (regs.r_scratch.gr15) { + case SYS_syscall: + case SYS___syscall: + parm_offset++; + break; } - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; + memcpy(cs->args, parm_offset, narg * sizeof(long)); - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - memcpy(fsc->args, parm_offset, nargs * sizeof(long)); - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -ia64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +ia64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_scratch.gr8; - errorp = (regs.r_scratch.gr10 != 0) ? 1 : 0; + retval[0] = regs.r_scratch.gr8; + retval[1] = regs.r_scratch.gr9; + *errorp = (regs.r_scratch.gr10 != 0) ? 1 : 0; + return (0); +} - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ +static struct procabi ia64_fbsd = { + "FreeBSD ELF64", + syscallnames, + nitems(syscallnames), + ia64_fetch_args, + ia64_fetch_retval +}; - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(ia64_fbsd); Index: stable/10/usr.bin/truss/main.c =================================================================== --- stable/10/usr.bin/truss/main.c (revision 290051) +++ stable/10/usr.bin/truss/main.c (revision 290052) @@ -1,388 +1,211 @@ /*- * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ #include __FBSDID("$FreeBSD$"); /* * The main module for truss. Surprisingly simple, but, then, the other * files handle the bulk of the work. And, of course, the kernel has to * do a lot of the work :). */ -#include -#include -#include -#include -#include -#include +#include #include -#include -#include #include #include #include -#include #include #include #include "truss.h" #include "extern.h" #include "syscall.h" -#define MAXARGS 6 - static void usage(void) { fprintf(stderr, "%s\n%s\n", "usage: truss [-cfaedDS] [-o file] [-s strsize] -p pid", " truss [-cfaedDS] [-o file] [-s strsize] command [args]"); exit(1); } -/* - * WARNING! "FreeBSD a.out" must be first, or set_etype will not - * work correctly. - */ -static struct ex_types { - const char *type; - void (*enter_syscall)(struct trussinfo *, int); - long (*exit_syscall)(struct trussinfo *, int); -} ex_types[] = { -#ifdef __arm__ - { "FreeBSD ELF32", arm_syscall_entry, arm_syscall_exit }, -#endif -#ifdef __amd64__ - { "FreeBSD ELF64", amd64_syscall_entry, amd64_syscall_exit }, - { "FreeBSD ELF32", amd64_fbsd32_syscall_entry, amd64_fbsd32_syscall_exit }, - { "Linux ELF32", amd64_linux32_syscall_entry, amd64_linux32_syscall_exit }, -#endif -#ifdef __i386__ - { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, - { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, - { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, - { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, -#endif -#ifdef __ia64__ - { "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit }, -#endif -#ifdef __powerpc__ - { "FreeBSD ELF", powerpc_syscall_entry, powerpc_syscall_exit }, - { "FreeBSD ELF32", powerpc_syscall_entry, powerpc_syscall_exit }, -#ifdef __powerpc64__ - { "FreeBSD ELF64", powerpc64_syscall_entry, powerpc64_syscall_exit }, -#endif -#endif -#ifdef __sparc64__ - { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, -#endif -#ifdef __mips__ - { "FreeBSD ELF", mips_syscall_entry, mips_syscall_exit }, - { "FreeBSD ELF32", mips_syscall_entry, mips_syscall_exit }, - { "FreeBSD ELF64", mips_syscall_entry, mips_syscall_exit }, // XXX -#endif - { 0, 0, 0 }, -}; - -/* - * Set the execution type. This is called after every exec, and when - * a process is first monitored. - */ - -static struct ex_types * -set_etype(struct trussinfo *trussinfo) -{ - struct ex_types *funcs; - size_t len; - int error; - int mib[4]; - char progt[32]; - - len = sizeof(progt); - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_SV_NAME; - mib[3] = trussinfo->pid; - error = sysctl(mib, 4, progt, &len, NULL, 0); - if (error != 0) - err(2, "can not get etype"); - - for (funcs = ex_types; funcs->type; funcs++) - if (strcmp(funcs->type, progt) == 0) - break; - - if (funcs->type == NULL) { - funcs = &ex_types[0]; - warn("execution type %s is not supported -- using %s", - progt, funcs->type); - } - return (funcs); -} - char * strsig(int sig) { - char *ret; + static char tmp[64]; - ret = NULL; if (sig > 0 && sig < NSIG) { - asprintf(&ret, "SIG%s", sys_signame[sig]); - if (ret == NULL) - return (NULL); + snprintf(tmp, sizeof(tmp), "SIG%s", sys_signame[sig]); + return (tmp); } - return (ret); + return (NULL); } int main(int ac, char **av) { - struct timespec timediff; struct sigaction sa; - struct ex_types *funcs; struct trussinfo *trussinfo; char *fname; - char *signame; char **command; - pid_t childpid; - int c, initial_open, status; + pid_t pid; + int c; fname = NULL; - initial_open = 1; /* Initialize the trussinfo struct */ trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo)); if (trussinfo == NULL) errx(1, "calloc() failed"); + pid = 0; trussinfo->outfile = stderr; trussinfo->strsize = 32; - trussinfo->pr_why = S_NONE; trussinfo->curthread = NULL; - SLIST_INIT(&trussinfo->threadlist); + LIST_INIT(&trussinfo->proclist); + init_syscalls(); while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) { switch (c) { case 'p': /* specified pid */ - trussinfo->pid = atoi(optarg); + pid = atoi(optarg); /* make sure i don't trace me */ - if (trussinfo->pid == getpid()) { - fprintf(stderr, "attempt to grab self.\n"); - exit(2); + if (pid == getpid()) { + errx(2, "attempt to grab self."); } break; case 'f': /* Follow fork()'s */ trussinfo->flags |= FOLLOWFORKS; break; case 'a': /* Print execve() argument strings. */ trussinfo->flags |= EXECVEARGS; break; case 'c': /* Count number of system calls and time. */ - trussinfo->flags |= COUNTONLY; + trussinfo->flags |= (COUNTONLY | NOSIGS); break; case 'e': /* Print execve() environment strings. */ trussinfo->flags |= EXECVEENVS; break; case 'd': /* Absolute timestamps */ trussinfo->flags |= ABSOLUTETIMESTAMPS; break; case 'D': /* Relative timestamps */ trussinfo->flags |= RELATIVETIMESTAMPS; break; case 'o': /* Specified output file */ fname = optarg; break; case 's': /* Specified string size */ trussinfo->strsize = atoi(optarg); break; case 'S': /* Don't trace signals */ trussinfo->flags |= NOSIGS; break; default: usage(); } } ac -= optind; av += optind; - if ((trussinfo->pid == 0 && ac == 0) || - (trussinfo->pid != 0 && ac != 0)) + if ((pid == 0 && ac == 0) || + (pid != 0 && ac != 0)) usage(); if (fname != NULL) { /* Use output file */ /* * Set close-on-exec ('e'), so that the output file is not * shared with the traced process. */ if ((trussinfo->outfile = fopen(fname, "we")) == NULL) err(1, "cannot open %s", fname); } /* * If truss starts the process itself, it will ignore some signals -- * they should be passed off to the process, which may or may not * exit. If, however, we are examining an already-running process, * then we restore the event mask on these same signals. */ - - if (trussinfo->pid == 0) { /* Start a command ourselves */ + if (pid == 0) { + /* Start a command ourselves */ command = av; - trussinfo->pid = setup_and_wait(command); + setup_and_wait(trussinfo, command); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); } else { sa.sa_handler = restore_proc; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); - start_tracing(trussinfo->pid); + start_tracing(trussinfo, pid); } - /* * At this point, if we started the process, it is stopped waiting to * be woken up, either in exit() or in execve(). */ + if (LIST_FIRST(&trussinfo->proclist)->abi == NULL) { + /* + * If we are not able to handle this ABI, detach from the + * process and exit. If we just created a new process to + * run a command, kill the new process rather than letting + * it run untraced. + */ + if (pid == 0) + kill(LIST_FIRST(&trussinfo->proclist)->pid, SIGKILL); + ptrace(PT_DETACH, LIST_FIRST(&trussinfo->proclist)->pid, NULL, + 0); + return (1); + } + ptrace(PT_SYSCALL, LIST_FIRST(&trussinfo->proclist)->pid, (caddr_t)1, + 0); -START_TRACE: - funcs = set_etype(trussinfo); - - initial_open = 0; /* * At this point, it's a simple loop, waiting for the process to * stop, finding out why, printing out why, and then continuing it. * All of the grunt work is done in the support routines. */ - clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); - do { - waitevent(trussinfo); - - switch (trussinfo->pr_why) { - case S_SCE: - funcs->enter_syscall(trussinfo, MAXARGS); - clock_gettime(CLOCK_REALTIME, - &trussinfo->curthread->before); - break; - case S_SCX: - clock_gettime(CLOCK_REALTIME, - &trussinfo->curthread->after); - - if (trussinfo->curthread->in_fork && - (trussinfo->flags & FOLLOWFORKS)) { - trussinfo->curthread->in_fork = 0; - childpid = funcs->exit_syscall(trussinfo, - trussinfo->pr_data); - - /* - * Fork a new copy of ourself to trace - * the child of the original traced - * process. - */ - if (fork() == 0) { - trussinfo->pid = childpid; - start_tracing(trussinfo->pid); - goto START_TRACE; - } - break; - } - funcs->exit_syscall(trussinfo, MAXARGS); - break; - case S_SIG: - if (trussinfo->flags & NOSIGS) - break; - if (trussinfo->flags & FOLLOWFORKS) - fprintf(trussinfo->outfile, "%5d: ", - trussinfo->pid); - if (trussinfo->flags & ABSOLUTETIMESTAMPS) { - timespecsubt(&trussinfo->curthread->after, - &trussinfo->start_time, &timediff); - fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, - timediff.tv_nsec); - } - if (trussinfo->flags & RELATIVETIMESTAMPS) { - timespecsubt(&trussinfo->curthread->after, - &trussinfo->curthread->before, &timediff); - fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, - timediff.tv_nsec); - } - signame = strsig(trussinfo->pr_data); - fprintf(trussinfo->outfile, - "SIGNAL %u (%s)\n", trussinfo->pr_data, - signame == NULL ? "?" : signame); - free(signame); - break; - case S_EXIT: - if (trussinfo->flags & COUNTONLY) - break; - if (trussinfo->flags & FOLLOWFORKS) - fprintf(trussinfo->outfile, "%5d: ", - trussinfo->pid); - if (trussinfo->flags & ABSOLUTETIMESTAMPS) { - timespecsubt(&trussinfo->curthread->after, - &trussinfo->start_time, &timediff); - fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, - timediff.tv_nsec); - } - if (trussinfo->flags & RELATIVETIMESTAMPS) { - timespecsubt(&trussinfo->curthread->after, - &trussinfo->curthread->before, &timediff); - fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, timediff.tv_nsec); - } - fprintf(trussinfo->outfile, - "process exit, rval = %u\n", trussinfo->pr_data); - break; - default: - break; - } - } while (trussinfo->pr_why != S_EXIT && - trussinfo->pr_why != S_DETACHED); - - if (trussinfo->flags & FOLLOWFORKS) { - do { - childpid = wait(&status); - } while (childpid != -1); - } + eventloop(trussinfo); if (trussinfo->flags & COUNTONLY) print_summary(trussinfo); fflush(trussinfo->outfile); return (0); } Index: stable/10/usr.bin/truss/mips-fbsd.c =================================================================== --- stable/10/usr.bin/truss/mips-fbsd.c (revision 290051) +++ stable/10/usr.bin/truss/mips-fbsd.c (revision 290052) @@ -1,367 +1,141 @@ /* * Copyright 1998 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/sparc64-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - * - * This file is almost nothing more than a slightly-edited i386-fbsd.c. - */ +/* FreeBSD/mips-specific system call handling. */ -#include #include #include #include #include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) +static int +mips_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in sparc64/sparc64/trap.c - * is ever changed these functions need to keep up. - */ - -void -mips_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; - int indir; /* indirect system call */ + u_int i, reg; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } - indir = 0; - syscall_num = regs.r_regs[V0]; - if (syscall_num == SYS_syscall) { - indir = 1; - syscall_num = regs.r_regs[A0]; + /* + * FreeBSD has two special kinds of system call redirections -- + * SYS_syscall, and SYS___syscall. The former is the old syscall() + * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. + */ + reg = A0; + switch (regs.r_regs[V0]) { + case SYS_syscall: + reg = A1; + break; + case SYS___syscall: +#if defined(__mips_n32) || defined(__mips_n64) + reg = A1; +#else + reg = A2; +#endif + break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); -#if 0 // XXX - iorequest.piod_op = PIOD_READ_D; - iorequest.piod_offs = (void *)parm_offset; - iorequest.piod_addr = fsc->args; - iorequest.piod_len = (1 + nargs) * sizeof(unsigned long); - ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); - if (iorequest.piod_len == 0) - return; +#if defined(__mips_n32) || defined(__mips_n64) +#define MAXREG A7 #else - iorequest.piod_op = PIOD_READ_D; +#define MAXREG A3 #endif - switch (nargs) { - default: - /* - * The OS doesn't seem to allow more than 10 words of - * parameters (yay!). So we shouldn't be here. - */ - warn("More than 10 words (%d) of arguments!\n", nargs); - break; - case 10: - case 9: - case 8: - case 7: - case 6: - case 5: - /* - * If there are 7-10 words of arguments, they are placed - * on the stack, as is normal for other processors. - * The fall-through for all of these is deliberate!!! - */ - // XXX BAD constant used here + for (i = 0; i < narg && reg <= MAXREG; i++, reg++) + cs->args[i] = regs.r_regs[reg]; + if (narg > i) { iorequest.piod_op = PIOD_READ_D; - iorequest.piod_offs = (void *)(regs.r_regs[SP] + - 4 * sizeof(uint32_t)); - iorequest.piod_addr = &fsc->args[4]; - iorequest.piod_len = (nargs - 4) * sizeof(fsc->args[0]); + iorequest.piod_offs = (void *)((uintptr_t)regs.r_regs[SP] + + 4 * sizeof(cs->args[0])); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - case 4: fsc->args[3] = regs.r_regs[A3]; - case 3: fsc->args[2] = regs.r_regs[A2]; - case 2: fsc->args[1] = regs.r_regs[A1]; - case 1: fsc->args[0] = regs.r_regs[A0]; - case 0: break; + return (-1); } - if (indir) { - memmove(&fsc->args[0], &fsc->args[1], - (nargs - 1) * sizeof(fsc->args[0])); - } - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -mips_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +mips_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { - fprintf(trussinfo->outfile, "\n"); + fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_regs[V0]; - errorp = !!regs.r_regs[A3]; + /* XXX: Does not have special handling for __syscall(). */ + retval[0] = regs.r_regs[V0]; + retval[1] = regs.r_regs[V1]; + *errorp = !!regs.r_regs[A3]; + return (0); +} - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } +static struct procabi mips_fbsd = { +#ifdef __mips_n64 + "FreeBSD ELF64", +#else + "FreeBSD ELF32", +#endif + syscallnames, + nitems(syscallnames), + mips_fetch_args, + mips_fetch_retval +}; - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(mips_fbsd); Index: stable/10/usr.bin/truss/powerpc-fbsd.c =================================================================== --- stable/10/usr.bin/truss/powerpc-fbsd.c (revision 290051) +++ stable/10/usr.bin/truss/powerpc-fbsd.c (revision 290052) @@ -1,352 +1,150 @@ /* * Copyright 2006 Peter Grehan * Copyright 2005 Orlando Bassotto * Copyright 1998 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/powerpc-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - * - * This file is almost nothing more than a slightly-edited i386-fbsd.c. - */ +/* FreeBSD/powerpc-specific system call handling. */ -#include #include #include #include #include -#include -#include -#include -#include #include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #ifdef __powerpc64__ /* 32-bit compatibility */ #include "freebsd32_syscalls.h" #define syscallnames freebsd32_syscallnames #else /* native 32-bit */ #include "syscalls.h" #endif -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) +static int +powerpc_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in powerpc/powerpc/trap.c - * is ever changed these functions need to keep up. - */ - -void -powerpc_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; - void *args; + struct current_syscall *cs; lwpid_t tid; - int i, regargs, syscall_num; + u_int i, reg; - /* Account for a 64-bit argument with corresponding alignment. */ - nargs += 2; - tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - regargs = NARGREG; - syscall_num = regs.fixreg[0]; - args = ®s.fixreg[3]; - if (syscall_num == SYS_syscall) { - args = ®s.fixreg[4]; - regargs -= 1; - syscall_num = regs.fixreg[3]; - } else if (syscall_num == SYS___syscall) { - args = ®s.fixreg[5]; - regargs -= 2; - syscall_num = regs.fixreg[4]; + reg = 0; + switch (regs.fixreg[0]) { + case SYS_syscall: + reg += 1; + break; + case SYS___syscall: + reg += 2; + break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); + for (i = 0; i < narg && reg < NARGREG; i++, reg++) { +#ifdef __powerpc64__ + cs->args[i] = regs.fixreg[FIRSTARG + reg] & 0xffffffff; +#else + cs->args[i] = regs.fixreg[FIRSTARG + reg]; +#endif } + if (narg > i) { +#ifdef __powerpc64__ + uint32_t args32[narg - i]; + u_int j; - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - - if (nargs > regargs) { - memmove(&fsc->args[0], args, regargs * sizeof(fsc->args[0])); - +#endif iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.fixreg[1] + 8); - iorequest.piod_addr = &fsc->args[regargs]; - iorequest.piod_len = (nargs - regargs) * sizeof(fsc->args[0]); +#ifdef __powerpc64__ + iorequest.piod_addr = args32; + iorequest.piod_len = sizeof(args32); +#else + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); +#endif ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - } else - memmove(&fsc->args[0], args, nargs * sizeof(fsc->args[0])); - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); + return (-1); +#ifdef __powerpc64__ + for (j = 0; j < narg - i; j++) + cs->args[i + j] = args32[j]; #endif - fsc->nargs = nargs; } - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -powerpc_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +powerpc_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { - fprintf(trussinfo->outfile, "\n"); + fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.fixreg[3]; - errorp = !!(regs.cr & 0x10000000); + /* XXX: Does not have fixup for __syscall(). */ +#ifdef __powerpc64__ + retval[0] = regs.fixreg[3] & 0xffffffff; + retval[1] = regs.fixreg[4] & 0xffffffff; +#else + retval[0] = regs.fixreg[3]; + retval[1] = regs.fixreg[4]; +#endif + *errorp = !!(regs.cr & 0x10000000); + return (0); +} - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ +static struct procabi powerpc_fbsd = { + "FreeBSD ELF32", + syscallnames, + nitems(syscallnames), + powerpc_fetch_args, + powerpc_fetch_retval +}; - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * On 32-bit big-endian, the low word of a 64-bit return is - * in the greater address. Switch to this. XXX note that - * print_syscall_ret can't handle 64-bit return values (llseek) - */ - if (sc->ret_type == 2) - retval = regs.fixreg[4]; - - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(powerpc_fbsd); Index: stable/10/usr.bin/truss/powerpc64-fbsd.c =================================================================== --- stable/10/usr.bin/truss/powerpc64-fbsd.c (revision 290051) +++ stable/10/usr.bin/truss/powerpc64-fbsd.c (revision 290052) @@ -1,332 +1,118 @@ /* * Copyright 2006 Peter Grehan * Copyright 2005 Orlando Bassotto * Copyright 1998 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/powerpc-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - * - * This file is almost nothing more than a slightly-edited i386-fbsd.c. - */ +/* FreeBSD/powerpc64-specific system call handling. */ -#include #include #include #include #include -#include -#include -#include -#include #include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) +static int +powerpc64_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in powerpc/powerpc/trap.c - * is ever changed these functions need to keep up. - */ - -void -powerpc64_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; - void *args; + struct current_syscall *cs; lwpid_t tid; - int i, regargs, syscall_num; + u_int i, reg; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - regargs = NARGREG; - syscall_num = regs.fixreg[0]; - args = ®s.fixreg[3]; - if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) { - args = ®s.fixreg[4]; - regargs -= 1; - syscall_num = regs.fixreg[3]; + reg = 0; + switch (regs.fixreg[0]) { + case SYS_syscall: + case SYS___syscall: + reg += 1; + break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - - if (nargs > regargs) { - memmove(&fsc->args[0], args, regargs * sizeof(fsc->args[0])); - + for (i = 0; i < narg && reg < NARGREG; i++, reg++) + cs->args[i] = regs.fixreg[FIRSTARG + reg]; + if (narg > i) { iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.fixreg[1] + 48); - iorequest.piod_addr = &fsc->args[regargs]; - iorequest.piod_len = (nargs - regargs) * sizeof(fsc->args[0]); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - } else - memmove(&fsc->args[0], args, nargs * sizeof(fsc->args[0])); - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; + return (-1); } - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -powerpc64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +powerpc64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { - fprintf(trussinfo->outfile, "\n"); + fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.fixreg[3]; - errorp = !!(regs.cr & 0x10000000); + retval[0] = regs.fixreg[3]; + retval[1] = regs.fixreg[4]; + *errorp = !!(regs.cr & 0x10000000); + return (0); +} - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ +static struct procabi powerpc64_fbsd = { + "FreeBSD ELF64", + syscallnames, + nitems(syscallnames), + powerpc64_fetch_args, + powerpc64_fetch_retval +}; - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(powerpc64_fbsd); Index: stable/10/usr.bin/truss/setup.c =================================================================== --- stable/10/usr.bin/truss/setup.c (revision 290051) +++ stable/10/usr.bin/truss/setup.c (revision 290052) @@ -1,239 +1,592 @@ /*- * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ #include __FBSDID("$FreeBSD$"); /* * Various setup functions for truss. Not the cleanest-written code, * I'm afraid. */ -#include -#include #include +#include #include +#include #include #include -#include #include +#include #include #include #include #include #include -#include - #include "truss.h" +#include "syscall.h" #include "extern.h" +SET_DECLARE(procabi, struct procabi); + static sig_atomic_t detaching; +static void new_proc(struct trussinfo *, pid_t); + /* * setup_and_wait() is called to start a process. All it really does - * is fork(), set itself up to stop on exec or exit, and then exec - * the given command. At that point, the child process stops, and - * the parent can wake up and deal with it. + * is fork(), enable tracing in the child, and then exec the given + * command. At that point, the child process stops, and the parent + * can wake up and deal with it. */ - -int -setup_and_wait(char *command[]) +void +setup_and_wait(struct trussinfo *info, char *command[]) { pid_t pid; pid = vfork(); if (pid == -1) err(1, "fork failed"); if (pid == 0) { /* Child */ ptrace(PT_TRACE_ME, 0, 0, 0); execvp(command[0], command); err(1, "execvp %s", command[0]); } /* Only in the parent here */ if (waitpid(pid, NULL, 0) < 0) err(1, "unexpect stop in waitpid"); - return (pid); + new_proc(info, pid); } /* - * start_tracing picks up where setup_and_wait() dropped off -- namely, - * it sets the event mask for the given process id. Called for both - * monitoring an existing process and when we create our own. + * start_tracing is called to attach to an existing process. */ - -int -start_tracing(pid_t pid) +void +start_tracing(struct trussinfo *info, pid_t pid) { int ret, retry; retry = 10; do { ret = ptrace(PT_ATTACH, pid, NULL, 0); usleep(200); } while (ret && retry-- > 0); if (ret) err(1, "can not attach to target process"); if (waitpid(pid, NULL, 0) < 0) err(1, "Unexpect stop in waitpid"); - return (0); + new_proc(info, pid); } /* * Restore a process back to it's pre-truss state. * Called for SIGINT, SIGTERM, SIGQUIT. This only * applies if truss was told to monitor an already-existing * process. */ - void restore_proc(int signo __unused) { detaching = 1; } -static int +static void detach_proc(pid_t pid) { - int waitval; /* stop the child so that we can detach */ kill(pid, SIGSTOP); - if (waitpid(pid, &waitval, 0) < 0) + if (waitpid(pid, NULL, 0) < 0) err(1, "Unexpected stop in waitpid"); if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0) err(1, "Can not detach the process"); kill(pid, SIGCONT); +} - return (waitval); +/* + * Determine the ABI. This is called after every exec, and when + * a process is first monitored. + */ +static struct procabi * +find_abi(pid_t pid) +{ + struct procabi **pabi; + size_t len; + int error; + int mib[4]; + char progt[32]; + + len = sizeof(progt); + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_SV_NAME; + mib[3] = pid; + error = sysctl(mib, 4, progt, &len, NULL, 0); + if (error != 0) + err(2, "can not get sysvec name"); + + SET_FOREACH(pabi, procabi) { + if (strcmp((*pabi)->type, progt) == 0) + return (*pabi); + } + warnx("ABI %s for pid %ld is not supported", progt, (long)pid); + return (NULL); } +static void +new_proc(struct trussinfo *info, pid_t pid) +{ + struct procinfo *np; + + /* + * If this happens it means there is a bug in truss. Unfortunately + * this will kill any processes are attached to. + */ + LIST_FOREACH(np, &info->proclist, entries) { + if (np->pid == pid) + errx(1, "Duplicate process for pid %ld", (long)pid); + } + + if (info->flags & FOLLOWFORKS) + if (ptrace(PT_FOLLOW_FORK, pid, NULL, 1) == -1) + err(1, "Unable to follow forks for pid %ld", (long)pid); + np = calloc(1, sizeof(struct procinfo)); + np->pid = pid; + np->abi = find_abi(pid); + SLIST_INIT(&np->threadlist); + LIST_INSERT_HEAD(&info->proclist, np, entries); +} + +static void +free_proc(struct procinfo *p) +{ + struct threadinfo *t, *t2; + + SLIST_FOREACH_SAFE(t, &p->threadlist, entries, t2) { + free(t); + } + LIST_REMOVE(p, entries); + free(p); +} + +static void +detach_all_procs(struct trussinfo *info) +{ + struct procinfo *p, *p2; + + LIST_FOREACH_SAFE(p, &info->proclist, entries, p2) { + detach_proc(p->pid); + free_proc(p); + } +} + +static struct procinfo * +find_proc(struct trussinfo *info, pid_t pid) +{ + struct procinfo *np; + + LIST_FOREACH(np, &info->proclist, entries) { + if (np->pid == pid) + return (np); + } + + return (NULL); +} + /* - * Change curthread member based on lwpid. - * If it is a new thread, create a threadinfo structure + * Change curthread member based on (pid, lwpid). + * If it is a new thread, create a threadinfo structure. */ static void -find_thread(struct trussinfo *info, lwpid_t lwpid) +find_thread(struct trussinfo *info, pid_t pid, lwpid_t lwpid) { - struct threadinfo *np; + struct procinfo *np; + struct threadinfo *nt; - info->curthread = NULL; - SLIST_FOREACH(np, &info->threadlist, entries) { - if (np->tid == lwpid) { - info->curthread = np; + np = find_proc(info, pid); + assert(np != NULL); + + SLIST_FOREACH(nt, &np->threadlist, entries) { + if (nt->tid == lwpid) { + info->curthread = nt; return; } } - np = (struct threadinfo *)calloc(1, sizeof(struct threadinfo)); - if (np == NULL) + nt = calloc(1, sizeof(struct threadinfo)); + if (nt == NULL) err(1, "calloc() failed"); - np->tid = lwpid; - SLIST_INSERT_HEAD(&info->threadlist, np, entries); - info->curthread = np; + nt->proc = np; + nt->tid = lwpid; + SLIST_INSERT_HEAD(&np->threadlist, nt, entries); + info->curthread = nt; } /* - * Start the traced process and wait until it stoped. - * Fill trussinfo structure. - * When this even returns, the traced process is in stop state. + * When a process exits, it no longer has any threads left. However, + * the main loop expects a valid curthread. In cases when a thread + * triggers the termination (e.g. calling exit or triggering a fault) + * we would ideally use that thread. However, if a process is killed + * by a signal sent from another process then there is no "correct" + * thread. We just punt and use the first thread. */ -void -waitevent(struct trussinfo *info) +static void +find_exit_thread(struct trussinfo *info, pid_t pid) { - struct ptrace_lwpinfo lwpinfo; - static int pending_signal = 0; - int waitval; + struct procinfo *np; + struct threadinfo *nt; - ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal); - pending_signal = 0; + np = find_proc(info, pid); + assert(np != NULL); -detach: - if (detaching) { - waitval = detach_proc(info->pid); - info->pr_why = S_DETACHED; - info->pr_data = WEXITSTATUS(waitval); + if (SLIST_EMPTY(&np->threadlist)) { + /* + * If an existing process exits right after we attach + * to it but before it posts any events, there won't + * be any threads. Create a dummy thread and set its + * "before" time to the global start time. + */ + nt = calloc(1, sizeof(struct threadinfo)); + if (nt == NULL) + err(1, "calloc() failed"); + nt->proc = np; + nt->tid = 0; + SLIST_INSERT_HEAD(&np->threadlist, nt, entries); + nt->before = info->start_time; + } + info->curthread = SLIST_FIRST(&np->threadlist); +} + +static void +alloc_syscall(struct threadinfo *t, struct ptrace_lwpinfo *pl) +{ + u_int i; + + assert(t->in_syscall == 0); + assert(t->cs.number == 0); + assert(t->cs.name == NULL); + assert(t->cs.nargs == 0); + for (i = 0; i < nitems(t->cs.s_args); i++) + assert(t->cs.s_args[i] == NULL); + memset(t->cs.args, 0, sizeof(t->cs.args)); + t->cs.number = pl->pl_syscall_code; + t->in_syscall = 1; +} + +static void +free_syscall(struct threadinfo *t) +{ + u_int i; + + for (i = 0; i < t->cs.nargs; i++) + free(t->cs.s_args[i]); + memset(&t->cs, 0, sizeof(t->cs)); + t->in_syscall = 0; +} + +static void +enter_syscall(struct trussinfo *info, struct ptrace_lwpinfo *pl) +{ + struct threadinfo *t; + struct syscall *sc; + u_int i, narg; + + t = info->curthread; + alloc_syscall(t, pl); + narg = MIN(pl->pl_syscall_narg, nitems(t->cs.args)); + if (narg != 0 && t->proc->abi->fetch_args(info, narg) != 0) { + free_syscall(t); return; } - if (waitpid(info->pid, &waitval, 0) == -1) { - if (errno == EINTR) - goto detach; - err(1, "Unexpected stop in waitpid"); + if (t->cs.number >= 0 && t->cs.number < t->proc->abi->nsyscalls) + t->cs.name = t->proc->abi->syscallnames[t->cs.number]; + if (t->cs.name == NULL) + fprintf(info->outfile, "-- UNKNOWN %s SYSCALL %d --\n", + t->proc->abi->type, t->cs.number); + + sc = get_syscall(t->cs.name, narg); + t->cs.nargs = sc->nargs; + assert(sc->nargs <= nitems(t->cs.s_args)); + + t->cs.sc = sc; + + /* + * At this point, we set up the system call arguments. + * We ignore any OUT ones, however -- those are arguments that + * are set by the system call, and so are probably meaningless + * now. This doesn't currently support arguments that are + * passed in *and* out, however. + */ + if (t->cs.name != NULL) { +#if DEBUG + fprintf(stderr, "syscall %s(", t->cs.name); +#endif + for (i = 0; i < t->cs.nargs; i++) { +#if DEBUG + fprintf(stderr, "0x%lx%s", sc ? + t->cs.args[sc->args[i].offset] : t->cs.args[i], + i < (t->cs.nargs - 1) ? "," : ""); +#endif + if (!(sc->args[i].type & OUT)) { + t->cs.s_args[i] = print_arg(&sc->args[i], + t->cs.args, 0, info); + } + } +#if DEBUG + fprintf(stderr, ")\n"); +#endif } - if (WIFCONTINUED(waitval)) { - info->pr_why = S_NONE; + clock_gettime(CLOCK_REALTIME, &t->before); +} + +static void +exit_syscall(struct trussinfo *info, struct ptrace_lwpinfo *pl) +{ + struct threadinfo *t; + struct procinfo *p; + struct syscall *sc; + long retval[2]; + u_int i; + int errorp; + + t = info->curthread; + if (!t->in_syscall) return; - } - if (WIFEXITED(waitval)) { - info->pr_why = S_EXIT; - info->pr_data = WEXITSTATUS(waitval); + + clock_gettime(CLOCK_REALTIME, &t->after); + p = t->proc; + if (p->abi->fetch_retval(info, retval, &errorp) < 0) { + free_syscall(t); return; } - if (WIFSTOPPED(waitval)) { - ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo, - sizeof(lwpinfo)); - find_thread(info, lwpinfo.pl_lwpid); - switch (WSTOPSIG(waitval)) { - case SIGTRAP: - if (lwpinfo.pl_flags & PL_FLAG_SCE) { - info->pr_why = S_SCE; - info->curthread->in_syscall = 1; - break; - } else if (lwpinfo.pl_flags & PL_FLAG_SCX) { - info->pr_why = S_SCX; - info->curthread->in_syscall = 0; - break; + + sc = t->cs.sc; + /* + * Here, we only look for arguments that have OUT masked in -- + * otherwise, they were handled in enter_syscall(). + */ + for (i = 0; i < sc->nargs; i++) { + char *temp; + + if (sc->args[i].type & OUT) { + /* + * If an error occurred, then don't bother + * getting the data; it may not be valid. + */ + if (errorp) { + asprintf(&temp, "0x%lx", + t->cs.args[sc->args[i].offset]); + } else { + temp = print_arg(&sc->args[i], + t->cs.args, retval, info); } - /* We didn't send the SIGTRAP, just forward it. */ - /* FALLTHROUGH */ - default: - info->pr_why = S_SIG; - info->pr_data = WSTOPSIG(waitval); - pending_signal = info->pr_data; - break; + t->cs.s_args[i] = temp; } } - if (WIFSIGNALED(waitval)) { - info->pr_why = S_EXIT; - info->pr_data = 0; - return; + + print_syscall_ret(info, t->cs.name, t->cs.nargs, t->cs.s_args, + errorp, retval, sc); + free_syscall(t); + + /* + * If the process executed a new image, check the ABI. If the + * new ABI isn't supported, stop tracing this process. + */ + if (pl->pl_flags & PL_FLAG_EXEC) { + p->abi = find_abi(p->pid); + if (p->abi == NULL) { + if (ptrace(PT_DETACH, p->pid, (caddr_t)1, 0) < 0) + err(1, "Can not detach the process"); + free_proc(p); + } + } +} + +static void +report_exit(struct trussinfo *info, siginfo_t *si) +{ + struct timespec timediff; + + if (info->flags & FOLLOWFORKS) + fprintf(info->outfile, "%5d: ", si->si_pid); + clock_gettime(CLOCK_REALTIME, &info->curthread->after); + if (info->flags & ABSOLUTETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->start_time, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + if (info->flags & RELATIVETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->curthread->before, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + if (si->si_code == CLD_EXITED) + fprintf(info->outfile, "process exit, rval = %u\n", + si->si_status); + else + fprintf(info->outfile, "process killed, signal = %u%s\n", + si->si_status, si->si_code == CLD_DUMPED ? + " (core dumped)" : ""); +} + +static void +report_new_child(struct trussinfo *info, pid_t pid) +{ + struct timespec timediff; + + clock_gettime(CLOCK_REALTIME, &info->curthread->after); + assert(info->flags & FOLLOWFORKS); + fprintf(info->outfile, "%5d: ", pid); + if (info->flags & ABSOLUTETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->start_time, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + if (info->flags & RELATIVETIMESTAMPS) { + timediff.tv_sec = 0; + timediff.tv_nsec = 0; + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + fprintf(info->outfile, "\n"); +} + +static void +report_signal(struct trussinfo *info, siginfo_t *si) +{ + struct timespec timediff; + char *signame; + + if (info->flags & FOLLOWFORKS) + fprintf(info->outfile, "%5d: ", si->si_pid); + if (info->flags & ABSOLUTETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->start_time, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + if (info->flags & RELATIVETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->curthread->before, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + signame = strsig(si->si_status); + fprintf(info->outfile, "SIGNAL %u (%s)\n", si->si_status, + signame == NULL ? "?" : signame); +} + +/* + * Wait for events until all the processes have exited or truss has been + * asked to stop. + */ +void +eventloop(struct trussinfo *info) +{ + struct ptrace_lwpinfo pl; + siginfo_t si; + int pending_signal; + + while (!LIST_EMPTY(&info->proclist)) { + if (detaching) { + detach_all_procs(info); + return; + } + + if (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) { + if (errno == EINTR) + continue; + err(1, "Unexpected error from waitid"); + } + + assert(si.si_signo == SIGCHLD); + + switch (si.si_code) { + case CLD_EXITED: + case CLD_KILLED: + case CLD_DUMPED: + find_exit_thread(info, si.si_pid); + if ((info->flags & COUNTONLY) == 0) + report_exit(info, &si); + free_proc(info->curthread->proc); + info->curthread = NULL; + break; + case CLD_TRAPPED: + if (ptrace(PT_LWPINFO, si.si_pid, (caddr_t)&pl, + sizeof(pl)) == -1) + err(1, "ptrace(PT_LWPINFO)"); + + if (pl.pl_flags & PL_FLAG_CHILD) { + new_proc(info, si.si_pid); + assert(LIST_FIRST(&info->proclist)->abi != + NULL); + } + find_thread(info, si.si_pid, pl.pl_lwpid); + + if (si.si_status == SIGTRAP && + (pl.pl_flags & (PL_FLAG_SCE|PL_FLAG_SCX)) != 0) { + if (pl.pl_flags & PL_FLAG_SCE) + enter_syscall(info, &pl); + else if (pl.pl_flags & PL_FLAG_SCX) + exit_syscall(info, &pl); + pending_signal = 0; + } else if (pl.pl_flags & PL_FLAG_CHILD) { + if ((info->flags & COUNTONLY) == 0) + report_new_child(info, si.si_pid); + pending_signal = 0; + } else { + if ((info->flags & NOSIGS) == 0) + report_signal(info, &si); + pending_signal = si.si_status; + } + ptrace(PT_SYSCALL, si.si_pid, (caddr_t)1, + pending_signal); + break; + case CLD_STOPPED: + errx(1, "waitid reported CLD_STOPPED"); + case CLD_CONTINUED: + break; + } } } Index: stable/10/usr.bin/truss/sparc64-fbsd.c =================================================================== --- stable/10/usr.bin/truss/sparc64-fbsd.c (revision 290051) +++ stable/10/usr.bin/truss/sparc64-fbsd.c (revision 290052) @@ -1,361 +1,125 @@ /* * Copyright 1998 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/sparc64-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - * - * This file is almost nothing more than a slightly-edited i386-fbsd.c. - */ +/* FreeBSD/sparc64-specific system call handling. */ -#include #include #include #include #include #include -#include -#include -#include -#include #include #include -#include -#include -#include -#include #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) +static int +sparc64_fetch_args(struct trussinfo *trussinfo, u_int narg) { - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in sparc64/sparc64/trap.c - * is ever changed these functions need to keep up. - */ - -void -sparc64_syscall_entry(struct trussinfo *trussinfo, int nargs) -{ struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; - int indir; /* indirect system call */ + u_int i, reg; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - indir = 0; - syscall_num = regs.r_global[1]; - if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) { - indir = 1; - syscall_num = regs.r_out[0]; + reg = 0; + switch (regs.r_global[1]) { + case SYS_syscall: + case SYS___syscall: + reg = 1; + break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - switch (nargs) { - default: - /* - * The OS doesn't seem to allow more than 10 words of - * parameters (yay!). So we shouldn't be here. - */ - warn("More than 10 words (%d) of arguments!\n", nargs); - break; - case 10: - case 9: - case 8: - case 7: - /* - * If there are 7-10 words of arguments, they are placed - * on the stack, as is normal for other processors. - * The fall-through for all of these is deliberate!!! - */ + for (i = 0; i < narg && reg < 6; i++, reg++) + cs->args[i] = regs.r_out[reg]; + if (narg > i) { iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.r_out[6] + SPOFF + offsetof(struct frame, fr_pad[6])); - iorequest.piod_addr = &fsc->args[6]; - iorequest.piod_len = (nargs - 6) * sizeof(fsc->args[0]); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - case 6: fsc->args[5] = regs.r_out[5]; - case 5: fsc->args[4] = regs.r_out[4]; - case 4: fsc->args[3] = regs.r_out[3]; - case 3: fsc->args[2] = regs.r_out[2]; - case 2: fsc->args[1] = regs.r_out[1]; - case 1: fsc->args[0] = regs.r_out[0]; - case 0: - break; + return (-1); } - if (indir) - memmove(&fsc->args[0], &fsc->args[1], (nargs - 1) * - sizeof(fsc->args[0])); - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -sparc64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +sparc64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - if (trussinfo->curthread->fsc == NULL) - return (-1); - tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { - fprintf(trussinfo->outfile, "\n"); + fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_out[0]; - errorp = !!(regs.r_tstate & TSTATE_XCC_C); + retval[0] = regs.r_out[0]; + retval[1] = regs.r_out[1]; + *errorp = !!(regs.r_tstate & TSTATE_XCC_C); + return (0); +} - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ +static struct procabi sparc64_fbsd = { + "FreeBSD ELF64", + syscallnames, + nitems(syscallnames), + sparc64_fetch_args, + sparc64_fetch_retval +}; - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); - - return (retval); -} +PROCABI(sparc64_fbsd); Index: stable/10/usr.bin/truss/syscall.h =================================================================== --- stable/10/usr.bin/truss/syscall.h (revision 290051) +++ stable/10/usr.bin/truss/syscall.h (revision 290052) @@ -1,113 +1,117 @@ /* * See i386-fbsd.c for copyright and license terms. * * System call arguments come in several flavours: * Hex -- values that should be printed in hex (addresses) * Octal -- Same as above, but octal * Int -- normal integer values (file descriptors, for example) * LongHex -- long value that should be printed in hex * Name -- pointer to a NULL-terminated string. * BinString -- pointer to an array of chars, printed via strvisx(). * Ptr -- pointer to some unspecified structure. Just print as hex for now. * Stat -- a pointer to a stat buffer. Prints a couple fields. + * StatFs -- a pointer to a statfs buffer. Prints a few fields. * Ioctl -- an ioctl command. Woefully limited. * Quad -- a double-word value. e.g., lseek(int, offset_t, int) * Signal -- a signal number. Prints the signal name (SIGxxx) * Sockaddr -- a pointer to a struct sockaddr. Prints symbolic AF, and IP:Port * StringArray -- a pointer to an array of string pointers. * Timespec -- a pointer to a struct timespec. Prints both elements. * Timeval -- a pointer to a struct timeval. Prints both elements. * Timeval2 -- a pointer to two struct timevals. Prints both elements of both. * Itimerval -- a pointer to a struct itimerval. Prints all elements. * Pollfd -- a pointer to an array of struct pollfd. Prints .fd and .events. * Fd_set -- a pointer to an array of fd_set. Prints the fds that are set. * Sigaction -- a pointer to a struct sigaction. Prints all elements. * Umtx -- a pointer to a struct umtx. Prints the value of owner. * Sigset -- a pointer to a sigset_t. Prints the signals that are set. * Sigprocmask -- the first argument to sigprocmask(). Prints the name. * Kevent -- a pointer to an array of struct kevents. Prints all elements. * Pathconf -- the 2nd argument of pathconf(). * * In addition, the pointer types (String, Ptr) may have OUT masked in -- * this means that the data is set on *return* from the system call -- or * IN (meaning that the data is passed *into* the system call). */ /* * $FreeBSD$ */ enum Argtype { None = 1, Hex, Octal, Int, LongHex, Name, Ptr, Stat, Ioctl, Quad, Signal, Sockaddr, StringArray, Timespec, Timeval, Itimerval, Pollfd, Fd_set, Sigaction, Fcntl, Mprot, Mmapflags, Whence, Readlinkres, - Umtx, Sigset, Sigprocmask, Kevent, Sockdomain, Socktype, Open, + Umtx, Sigset, Sigprocmask, StatFs, Kevent, Sockdomain, Socktype, Open, Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2, Pathconf, Rforkflags, ExitStatus, Waitoptions, Idtype, Procctl, - LinuxSockArgs, Umtxop }; + LinuxSockArgs, Umtxop, Atfd, Atflags, Accessmode, Long, + Sysarch, ExecArgs, ExecEnv, PipeFds, QuadHex }; #define ARG_MASK 0xff #define OUT 0x100 #define IN /*0x20*/0 struct syscall_args { enum Argtype type; int offset; }; struct syscall { + STAILQ_ENTRY(syscall) entries; const char *name; - int ret_type; /* 0, 1, or 2 return values */ - int nargs; /* actual number of meaningful arguments */ + u_int ret_type; /* 0, 1, or 2 return values */ + u_int nargs; /* actual number of meaningful arguments */ /* Hopefully, no syscalls with > 10 args */ struct syscall_args args[10]; struct timespec time; /* Time spent for this call */ int ncalls; /* Number of calls */ int nerror; /* Number of calls that returned with error */ }; -struct syscall *get_syscall(const char*); -char *print_arg(struct syscall_args *, unsigned long*, long, struct trussinfo *); +struct syscall *get_syscall(const char *, int nargs); +char *print_arg(struct syscall_args *, unsigned long*, long *, struct trussinfo *); /* * Linux Socket defines */ #define LINUX_SOCKET 1 #define LINUX_BIND 2 #define LINUX_CONNECT 3 #define LINUX_LISTEN 4 #define LINUX_ACCEPT 5 #define LINUX_GETSOCKNAME 6 #define LINUX_GETPEERNAME 7 #define LINUX_SOCKETPAIR 8 #define LINUX_SEND 9 #define LINUX_RECV 10 #define LINUX_SENDTO 11 #define LINUX_RECVFROM 12 #define LINUX_SHUTDOWN 13 #define LINUX_SETSOCKOPT 14 #define LINUX_GETSOCKOPT 15 #define LINUX_SENDMSG 16 -#define LINUX_RECVMSG 17 +#define LINUX_RECVMSG 17 #define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \ 0 : sizeof(register_t) - sizeof(t)) - + #if BYTE_ORDER == LITTLE_ENDIAN #define PADL_(t) 0 #define PADR_(t) PAD_(t) #else #define PADL_(t) PAD_(t) #define PADR_(t) 0 #endif typedef int l_int; typedef uint32_t l_ulong; struct linux_socketcall_args { char what_l_[PADL_(l_int)]; l_int what; char what_r_[PADR_(l_int)]; char args_l_[PADL_(l_ulong)]; l_ulong args; char args_r_[PADR_(l_ulong)]; }; +void init_syscalls(void); void print_syscall(struct trussinfo *, const char *, int, char **); void print_syscall_ret(struct trussinfo *, const char *, int, char **, int, - long, struct syscall *); + long *, struct syscall *); void print_summary(struct trussinfo *trussinfo); Index: stable/10/usr.bin/truss/syscalls.c =================================================================== --- stable/10/usr.bin/truss/syscalls.c (revision 290051) +++ stable/10/usr.bin/truss/syscalls.c (revision 290052) @@ -1,1429 +1,1701 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include +__FBSDID("$FreeBSD$"); /* * This file has routines used to print out system calls and their * arguments. */ #include +#include +#include #include +#include #include #include +#include #include -#include +#include +#include +#include +#include #include #include +#include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include "truss.h" #include "extern.h" #include "syscall.h" /* 64-bit alignment on 32-bit platforms. */ -#ifdef __powerpc__ +#if !defined(__LP64__) && defined(__powerpc__) #define QUAD_ALIGN 1 #else #define QUAD_ALIGN 0 #endif /* Number of slots needed for a 64-bit argument. */ #ifdef __LP64__ #define QUAD_SLOTS 1 #else #define QUAD_SLOTS 2 #endif /* * This should probably be in its own file, sorted alphabetically. */ -static struct syscall syscalls[] = { - { .name = "fcntl", .ret_type = 1, .nargs = 3, - .args = { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 } } }, - { .name = "fork", .ret_type = 1, .nargs = 0 }, - { .name = "vfork", .ret_type = 1, .nargs = 0 }, - { .name = "rfork", .ret_type = 1, .nargs = 1, - .args = { { Rforkflags, 0 } } }, - { .name = "getegid", .ret_type = 1, .nargs = 0 }, - { .name = "geteuid", .ret_type = 1, .nargs = 0 }, - { .name = "linux_readlink", .ret_type = 1, .nargs = 3, - .args = { { Name, 0 } , { Name | OUT, 1 }, { Int, 2 }}}, - { .name = "linux_socketcall", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 } , { LinuxSockArgs, 1 }}}, - { .name = "getgid", .ret_type = 1, .nargs = 0 }, - { .name = "getpid", .ret_type = 1, .nargs = 0 }, - { .name = "getpgid", .ret_type = 1, .nargs = 1, - .args = { { Int, 0 } } }, - { .name = "getpgrp", .ret_type = 1, .nargs = 0 }, - { .name = "getppid", .ret_type = 1, .nargs = 0 }, - { .name = "getsid", .ret_type = 1, .nargs = 1, - .args = { { Int, 0 } } }, - { .name = "getuid", .ret_type = 1, .nargs = 0 }, - { .name = "readlink", .ret_type = 1, .nargs = 3, - .args = { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 } } }, - { .name = "lseek", .ret_type = 2, .nargs = 3, - .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } }, - { .name = "linux_lseek", .ret_type = 2, .nargs = 3, - .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, - { .name = "mmap", .ret_type = 2, .nargs = 6, - .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } }, - { .name = "linux_mkdir", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0} , {Int, 1}}}, - { .name = "mprotect", .ret_type = 1, .nargs = 3, - .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, - { .name = "open", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 } , { Open, 1 }, { Octal, 2 } } }, - { .name = "mkdir", .ret_type = 1, .nargs = 2, - .args = { { Name, 0 } , { Octal, 1 } } }, - { .name = "linux_open", .ret_type = 1, .nargs = 3, - .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, - { .name = "close", .ret_type = 1, .nargs = 1, - .args = { { Int, 0 } } }, - { .name = "link", .ret_type = 0, .nargs = 2, - .args = { { Name, 0 }, { Name, 1 } } }, - { .name = "unlink", .ret_type = 0, .nargs = 1, - .args = { { Name, 0 } } }, - { .name = "chdir", .ret_type = 0, .nargs = 1, - .args = { { Name, 0 } } }, - { .name = "chroot", .ret_type = 0, .nargs = 1, - .args = { { Name, 0 } } }, - { .name = "mknod", .ret_type = 0, .nargs = 3, - .args = { { Name, 0 }, { Octal, 1 }, { Int, 3 } } }, - { .name = "chmod", .ret_type = 0, .nargs = 2, - .args = { { Name, 0 }, { Octal, 1 } } }, - { .name = "chown", .ret_type = 0, .nargs = 3, - .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, - { .name = "linux_stat64", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 }}}, - { .name = "mount", .ret_type = 0, .nargs = 4, - .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, - { .name = "umount", .ret_type = 0, .nargs = 2, - .args = { { Name, 0 }, { Int, 2 } } }, - { .name = "fstat", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Stat | OUT , 1 } } }, - { .name = "stat", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, - { .name = "lstat", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, - { .name = "linux_newstat", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, - { .name = "linux_access", .ret_type = 1, .nargs = 2, - .args = { { Name, 0 }, { Int, 1 }}}, - { .name = "linux_newfstat", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Ptr | OUT, 1 } } }, - { .name = "write", .ret_type = 1, .nargs = 3, - .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, - { .name = "ioctl", .ret_type = 1, .nargs = 3, - .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, - { .name = "break", .ret_type = 1, .nargs = 1, - .args = { { Ptr, 0 } } }, - { .name = "exit", .ret_type = 0, .nargs = 1, - .args = { { Hex, 0 } } }, - { .name = "access", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Int, 1 } } }, - { .name = "sigaction", .ret_type = 1, .nargs = 3, - .args = { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } }, +static struct syscall decoded_syscalls[] = { + /* Native ABI */ + { .name = "__getcwd", .ret_type = 1, .nargs = 2, + .args = { { Name | OUT, 0 }, { Int, 1 } } }, + { .name = "_umtx_lock", .ret_type = 1, .nargs = 1, + .args = { { Umtx, 0 } } }, + { .name = "_umtx_op", .ret_type = 1, .nargs = 5, + .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 }, + { Ptr, 4 } } }, + { .name = "_umtx_unlock", .ret_type = 1, .nargs = 1, + .args = { { Umtx, 0 } } }, { .name = "accept", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, + { .name = "access", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Accessmode, 1 } } }, { .name = "bind", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, + { .name = "bindat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 }, + { Int, 3 } } }, + { .name = "break", .ret_type = 1, .nargs = 1, + .args = { { Ptr, 0 } } }, + { .name = "chdir", .ret_type = 1, .nargs = 1, + .args = { { Name, 0 } } }, + { .name = "chflags", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Hex, 1 } } }, + { .name = "chmod", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Octal, 1 } } }, + { .name = "chown", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, + { .name = "chroot", .ret_type = 1, .nargs = 1, + .args = { { Name, 0 } } }, + { .name = "clock_gettime", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Timespec | OUT, 1 } } }, + { .name = "close", .ret_type = 1, .nargs = 1, + .args = { { Int, 0 } } }, { .name = "connect", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, + { .name = "connectat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 }, + { Int, 3 } } }, + { .name = "eaccess", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Accessmode, 1 } } }, + { .name = "execve", .ret_type = 1, .nargs = 3, + .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 }, + { ExecEnv | IN, 2 } } }, + { .name = "exit", .ret_type = 0, .nargs = 1, + .args = { { Hex, 0 } } }, + { .name = "faccessat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 }, + { Atflags, 3 } } }, + { .name = "fchmod", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Octal, 1 } } }, + { .name = "fchmodat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } }, + { .name = "fchown", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } }, + { .name = "fchownat", .ret_type = 1, .nargs = 5, + .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 }, + { Atflags, 4 } } }, + { .name = "fcntl", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } }, + { .name = "fstat", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Stat | OUT, 1 } } }, + { .name = "fstatat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 }, + { Atflags, 3 } } }, + { .name = "fstatfs", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { StatFs | OUT, 1 } } }, + { .name = "ftruncate", .ret_type = 1, .nargs = 2, + .args = { { Int | IN, 0 }, { QuadHex | IN, 1 + QUAD_ALIGN } } }, + { .name = "futimes", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Timeval2 | IN, 1 } } }, + { .name = "futimesat", .ret_type = 1, .nargs = 3, + .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } }, + { .name = "getitimer", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Itimerval | OUT, 2 } } }, { .name = "getpeername", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, + { .name = "getpgid", .ret_type = 1, .nargs = 1, + .args = { { Int, 0 } } }, + { .name = "getrlimit", .ret_type = 1, .nargs = 2, + .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } }, + { .name = "getrusage", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Rusage | OUT, 1 } } }, + { .name = "getsid", .ret_type = 1, .nargs = 1, + .args = { { Int, 0 } } }, { .name = "getsockname", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, - { .name = "recvfrom", .ret_type = 1, .nargs = 6, - .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, - { .name = "sendto", .ret_type = 1, .nargs = 6, - .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, - { .name = "execve", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, - { .name = "linux_execve", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, - { .name = "kldload", .ret_type = 0, .nargs = 1, + { .name = "gettimeofday", .ret_type = 1, .nargs = 2, + .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } }, + { .name = "ioctl", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, + { .name = "kevent", .ret_type = 1, .nargs = 6, + .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, + { Int, 4 }, { Timespec, 5 } } }, + { .name = "kill", .ret_type = 1, .nargs = 2, + .args = { { Int | IN, 0 }, { Signal | IN, 1 } } }, + { .name = "kldfind", .ret_type = 1, .nargs = 1, .args = { { Name | IN, 0 } } }, - { .name = "kldunload", .ret_type = 0, .nargs = 1, + { .name = "kldfirstmod", .ret_type = 1, .nargs = 1, .args = { { Int, 0 } } }, - { .name = "kldfind", .ret_type = 0, .nargs = 1, + { .name = "kldload", .ret_type = 1, .nargs = 1, .args = { { Name | IN, 0 } } }, - { .name = "kldnext", .ret_type = 0, .nargs = 1, + { .name = "kldnext", .ret_type = 1, .nargs = 1, .args = { { Int, 0 } } }, - { .name = "kldstat", .ret_type = 0, .nargs = 2, + { .name = "kldstat", .ret_type = 1, .nargs = 2, .args = { { Int, 0 }, { Ptr, 1 } } }, - { .name = "kldfirstmod", .ret_type = 0, .nargs = 1, + { .name = "kldunload", .ret_type = 1, .nargs = 1, .args = { { Int, 0 } } }, - { .name = "nanosleep", .ret_type = 0, .nargs = 1, - .args = { { Timespec, 0 } } }, - { .name = "select", .ret_type = 1, .nargs = 5, - .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 } } }, - { .name = "poll", .ret_type = 1, .nargs = 3, - .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, - { .name = "gettimeofday", .ret_type = 1, .nargs = 2, - .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } }, - { .name = "clock_gettime", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Timespec | OUT, 1 } } }, - { .name = "getitimer", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Itimerval | OUT, 2 } } }, - { .name = "setitimer", .ret_type = 1, .nargs = 3, - .args = { { Int, 0 }, { Itimerval, 1 } , { Itimerval | OUT, 2 } } }, { .name = "kse_release", .ret_type = 0, .nargs = 1, .args = { { Timespec, 0 } } }, - { .name = "kevent", .ret_type = 0, .nargs = 6, - .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 } } }, - { .name = "_umtx_lock", .ret_type = 0, .nargs = 1, - .args = { { Umtx, 0 } } }, - { .name = "_umtx_unlock", .ret_type = 0, .nargs = 1, - .args = { { Umtx, 0 } } }, - { .name = "sigprocmask", .ret_type = 0, .nargs = 3, - .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, - { .name = "unmount", .ret_type = 1, .nargs = 2, - .args = { { Name, 0 }, { Int, 1 } } }, - { .name = "socket", .ret_type = 1, .nargs = 3, - .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, - { .name = "getrusage", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Rusage | OUT, 1 } } }, - { .name = "__getcwd", .ret_type = 1, .nargs = 2, - .args = { { Name | OUT, 0 }, { Int, 1 } } }, - { .name = "shutdown", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Shutdown, 1 } } }, - { .name = "getrlimit", .ret_type = 1, .nargs = 2, - .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } }, - { .name = "setrlimit", .ret_type = 1, .nargs = 2, - .args = { { Resource, 0 }, { Rlimit | IN, 1 } } }, - { .name = "utimes", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, - { .name = "lutimes", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, - { .name = "futimes", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Timeval | IN, 1 } } }, - { .name = "chflags", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Hex, 1 } } }, { .name = "lchflags", .ret_type = 1, .nargs = 2, .args = { { Name | IN, 0 }, { Hex, 1 } } }, + { .name = "lchmod", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Octal, 1 } } }, + { .name = "lchown", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, + { .name = "link", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Name, 1 } } }, + { .name = "linkat", .ret_type = 1, .nargs = 5, + .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 }, + { Atflags, 4 } } }, + { .name = "lseek", .ret_type = 2, .nargs = 3, + .args = { { Int, 0 }, { QuadHex, 1 + QUAD_ALIGN }, + { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } }, + { .name = "lstat", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, + { .name = "lutimes", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, + { .name = "mkdir", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Octal, 1 } } }, + { .name = "mkdirat", .ret_type = 1, .nargs = 3, + .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } }, + { .name = "mkfifo", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Octal, 1 } } }, + { .name = "mkfifoat", .ret_type = 1, .nargs = 3, + .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } }, + { .name = "mknod", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } }, + { .name = "mknodat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } }, + { .name = "mmap", .ret_type = 1, .nargs = 6, + .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, + { Int, 4 }, { QuadHex, 5 + QUAD_ALIGN } } }, + { .name = "modfind", .ret_type = 1, .nargs = 1, + .args = { { Name | IN, 0 } } }, + { .name = "mount", .ret_type = 1, .nargs = 4, + .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, + { .name = "mprotect", .ret_type = 1, .nargs = 3, + .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, + { .name = "munmap", .ret_type = 1, .nargs = 2, + .args = { { Ptr, 0 }, { Int, 1 } } }, + { .name = "nanosleep", .ret_type = 1, .nargs = 1, + .args = { { Timespec, 0 } } }, + { .name = "open", .ret_type = 1, .nargs = 3, + .args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } }, + { .name = "openat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 }, + { Octal, 3 } } }, { .name = "pathconf", .ret_type = 1, .nargs = 2, .args = { { Name | IN, 0 }, { Pathconf, 1 } } }, { .name = "pipe", .ret_type = 1, .nargs = 1, - .args = { { Ptr, 0 } } }, - { .name = "truncate", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, - { .name = "ftruncate", .ret_type = 1, .nargs = 3, - .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, - { .name = "kill", .ret_type = 1, .nargs = 2, - .args = { { Int | IN, 0 }, { Signal | IN, 1 } } }, - { .name = "munmap", .ret_type = 1, .nargs = 2, - .args = { { Ptr, 0 }, { Int, 1 } } }, + .args = { { PipeFds | OUT, 0 } } }, + { .name = "pipe2", .ret_type = 1, .nargs = 2, + .args = { { Ptr, 0 }, { Open, 1 } } }, + { .name = "poll", .ret_type = 1, .nargs = 3, + .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, + { .name = "posix_openpt", .ret_type = 1, .nargs = 1, + .args = { { Open, 0 } } }, + { .name = "procctl", .ret_type = 1, .nargs = 4, + .args = { { Idtype, 0 }, { Quad, 1 + QUAD_ALIGN }, + { Procctl, 1 + QUAD_ALIGN + QUAD_SLOTS }, + { Ptr, 2 + QUAD_ALIGN + QUAD_SLOTS } } }, { .name = "read", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } }, + { .name = "readlink", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } }, + { .name = "readlinkat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name, 1 }, { Readlinkres | OUT, 2 }, + { Int, 3 } } }, + { .name = "recvfrom", .ret_type = 1, .nargs = 6, + .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, + { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, { .name = "rename", .ret_type = 1, .nargs = 2, - .args = { { Name , 0 } , { Name, 1 } } }, + .args = { { Name, 0 }, { Name, 1 } } }, + { .name = "renameat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } }, + { .name = "rfork", .ret_type = 1, .nargs = 1, + .args = { { Rforkflags, 0 } } }, + { .name = "select", .ret_type = 1, .nargs = 5, + .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, + { Timeval, 4 } } }, + { .name = "sendto", .ret_type = 1, .nargs = 6, + .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, + { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, + { .name = "setitimer", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Itimerval, 1 }, { Itimerval | OUT, 2 } } }, + { .name = "setrlimit", .ret_type = 1, .nargs = 2, + .args = { { Resource, 0 }, { Rlimit | IN, 1 } } }, + { .name = "shutdown", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Shutdown, 1 } } }, + { .name = "sigaction", .ret_type = 1, .nargs = 3, + .args = { { Signal, 0 }, { Sigaction | IN, 1 }, + { Sigaction | OUT, 2 } } }, + { .name = "sigpending", .ret_type = 1, .nargs = 1, + .args = { { Sigset | OUT, 0 } } }, + { .name = "sigprocmask", .ret_type = 1, .nargs = 3, + .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, + { .name = "sigqueue", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } }, + { .name = "sigreturn", .ret_type = 1, .nargs = 1, + .args = { { Ptr, 0 } } }, + { .name = "sigsuspend", .ret_type = 1, .nargs = 1, + .args = { { Sigset | IN, 0 } } }, + { .name = "sigtimedwait", .ret_type = 1, .nargs = 3, + .args = { { Sigset | IN, 0 }, { Ptr, 1 }, { Timespec | IN, 2 } } }, + { .name = "sigwait", .ret_type = 1, .nargs = 2, + .args = { { Sigset | IN, 0 }, { Ptr, 1 } } }, + { .name = "sigwaitinfo", .ret_type = 1, .nargs = 2, + .args = { { Sigset | IN, 0 }, { Ptr, 1 } } }, + { .name = "socket", .ret_type = 1, .nargs = 3, + .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, + { .name = "stat", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, + { .name = "statfs", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { StatFs | OUT, 1 } } }, { .name = "symlink", .ret_type = 1, .nargs = 2, - .args = { { Name , 0 } , { Name, 1 } } }, - { .name = "posix_openpt", .ret_type = 1, .nargs = 1, - .args = { { Open, 0 } } }, + .args = { { Name, 0 }, { Name, 1 } } }, + { .name = "symlinkat", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } }, + { .name = "sysarch", .ret_type = 1, .nargs = 2, + .args = { { Sysarch, 0 }, { Ptr, 1 } } }, + { .name = "thr_kill", .ret_type = 1, .nargs = 2, + .args = { { Long, 0 }, { Signal, 1 } } }, + { .name = "thr_self", .ret_type = 1, .nargs = 1, + .args = { { Ptr, 0 } } }, + { .name = "truncate", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { QuadHex | IN, 1 + QUAD_ALIGN } } }, +#if 0 + /* Does not exist */ + { .name = "umount", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Int, 2 } } }, +#endif + { .name = "unlink", .ret_type = 1, .nargs = 1, + .args = { { Name, 0 } } }, + { .name = "unlinkat", .ret_type = 1, .nargs = 3, + .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } }, + { .name = "unmount", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Int, 1 } } }, + { .name = "utimes", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, { .name = "wait4", .ret_type = 1, .nargs = 4, .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 }, { Rusage | OUT, 3 } } }, { .name = "wait6", .ret_type = 1, .nargs = 6, - .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 }, - { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } }, - { .name = "procctl", .ret_type = 1, .nargs = 4, - .args = { { Idtype, 0 }, { Int, 1 }, { Procctl, 2 }, { Ptr, 3 } } }, - { .name = "_umtx_op", .ret_type = 1, .nargs = 5, - .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 }, - { Ptr, 4 } } }, + .args = { { Idtype, 0 }, { Quad, 1 + QUAD_ALIGN }, + { ExitStatus | OUT, 1 + QUAD_ALIGN + QUAD_SLOTS }, + { Waitoptions, 2 + QUAD_ALIGN + QUAD_SLOTS }, + { Rusage | OUT, 3 + QUAD_ALIGN + QUAD_SLOTS }, + { Ptr, 4 + QUAD_ALIGN + QUAD_SLOTS } } }, + { .name = "write", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, + + /* Linux ABI */ + { .name = "linux_access", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Accessmode, 1 } } }, + { .name = "linux_execve", .ret_type = 1, .nargs = 3, + .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 }, + { ExecEnv | IN, 2 } } }, + { .name = "linux_lseek", .ret_type = 2, .nargs = 3, + .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, + { .name = "linux_mkdir", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Int, 1 } } }, + { .name = "linux_newfstat", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Ptr | OUT, 1 } } }, + { .name = "linux_newstat", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, + { .name = "linux_open", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, + { .name = "linux_readlink", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Name | OUT, 1 }, { Int, 2 } } }, + { .name = "linux_socketcall", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { LinuxSockArgs, 1 } } }, + { .name = "linux_stat64", .ret_type = 1, .nargs = 3, + .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 } } }, + { .name = 0 }, }; +static STAILQ_HEAD(, syscall) syscalls; /* Xlat idea taken from strace */ struct xlat { int val; const char *str; }; #define X(a) { a, #a }, #define XEND { 0, NULL } static struct xlat kevent_filters[] = { X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE) X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER) - X(EVFILT_FS) X(EVFILT_READ) XEND + X(EVFILT_FS) X(EVFILT_LIO) X(EVFILT_USER) XEND }; static struct xlat kevent_flags[] = { X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT) - X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND + X(EV_CLEAR) X(EV_RECEIPT) X(EV_DISPATCH) + X(EV_DROP) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND }; +static struct xlat kevent_user_ffctrl[] = { + X(NOTE_FFNOP) X(NOTE_FFAND) X(NOTE_FFOR) X(NOTE_FFCOPY) + XEND +}; + +static struct xlat kevent_rdwr_fflags[] = { + X(NOTE_LOWAT) XEND +}; + +static struct xlat kevent_vnode_fflags[] = { + X(NOTE_DELETE) X(NOTE_WRITE) X(NOTE_EXTEND) X(NOTE_ATTRIB) + X(NOTE_LINK) X(NOTE_RENAME) X(NOTE_REVOKE) XEND +}; + +static struct xlat kevent_proc_fflags[] = { + X(NOTE_EXIT) X(NOTE_FORK) X(NOTE_EXEC) X(NOTE_TRACK) X(NOTE_TRACKERR) + X(NOTE_CHILD) XEND +}; + +static struct xlat kevent_timer_fflags[] = { + X(NOTE_SECONDS) X(NOTE_MSECONDS) X(NOTE_USECONDS) X(NOTE_NSECONDS) + XEND +}; + static struct xlat poll_flags[] = { X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR) X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND) X(POLLWRBAND) X(POLLINIGNEOF) XEND }; static struct xlat mmap_flags[] = { X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME) X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100) X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON) - X(MAP_NOCORE) X(MAP_PREFAULT_READ) + X(MAP_EXCL) X(MAP_NOCORE) X(MAP_PREFAULT_READ) #ifdef MAP_32BIT X(MAP_32BIT) #endif XEND }; static struct xlat mprot_flags[] = { X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND }; static struct xlat whence_arg[] = { - X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND + X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) X(SEEK_DATA) X(SEEK_HOLE) XEND }; static struct xlat sigaction_flags[] = { X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP) X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND }; static struct xlat fcntl_arg[] = { X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL) - X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND + X(F_GETOWN) X(F_SETOWN) X(F_OGETLK) X(F_OSETLK) X(F_OSETLKW) + X(F_DUP2FD) X(F_GETLK) X(F_SETLK) X(F_SETLKW) X(F_SETLK_REMOTE) + X(F_READAHEAD) X(F_RDAHEAD) X(F_DUPFD_CLOEXEC) X(F_DUP2FD_CLOEXEC) + XEND }; static struct xlat fcntlfd_arg[] = { X(FD_CLOEXEC) XEND }; static struct xlat fcntlfl_arg[] = { X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW) - X(O_DIRECT) XEND + X(FRDAHEAD) X(O_DIRECT) XEND }; static struct xlat sockdomain_arg[] = { X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK) X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI) X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet) X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE) X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX) X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6) X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER) - X(PF_ARP) X(PF_BLUETOOTH) XEND + X(PF_ARP) X(PF_BLUETOOTH) X(PF_IEEE80211) X(PF_INET_SDP) + X(PF_INET6_SDP) XEND }; static struct xlat socktype_arg[] = { X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM) X(SOCK_SEQPACKET) XEND }; static struct xlat open_flags[] = { X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK) X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC) X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY) - X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) XEND + X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) + XEND }; static struct xlat shutdown_arg[] = { X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND }; static struct xlat resource_arg[] = { X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK) X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC) - X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND + X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) X(RLIMIT_NPTS) + X(RLIMIT_SWAP) XEND }; static struct xlat pathconf_arg[] = { X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT) X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF) X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE) X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO) X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS) X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE) X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN) X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX) X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT) - XEND + X(_PC_ACL_NFS4) X(_PC_MIN_HOLE_SIZE) XEND }; static struct xlat rfork_flags[] = { - X(RFPROC) X(RFNOWAIT) X(RFFDG) X(RFCFDG) X(RFTHREAD) X(RFMEM) - X(RFSIGSHARE) X(RFTSIGZMB) X(RFLINUXTHPN) XEND + X(RFFDG) X(RFPROC) X(RFMEM) X(RFNOWAIT) X(RFCFDG) X(RFTHREAD) + X(RFSIGSHARE) X(RFLINUXTHPN) X(RFTSIGZMB) X(RFPPWAIT) XEND }; static struct xlat wait_options[] = { X(WNOHANG) X(WUNTRACED) X(WCONTINUED) X(WNOWAIT) X(WEXITED) X(WTRAPPED) XEND }; static struct xlat idtype_arg[] = { X(P_PID) X(P_PPID) X(P_PGID) X(P_SID) X(P_CID) X(P_UID) X(P_GID) X(P_ALL) X(P_LWPID) X(P_TASKID) X(P_PROJID) X(P_POOLID) X(P_JAILID) X(P_CTID) X(P_CPUID) X(P_PSETID) XEND }; static struct xlat procctl_arg[] = { - X(PROC_SPROTECT) XEND + X(PROC_SPROTECT) X(PROC_REAP_ACQUIRE) X(PROC_REAP_RELEASE) + X(PROC_REAP_STATUS) X(PROC_REAP_GETPIDS) X(PROC_REAP_KILL) + X(PROC_TRACE_CTL) X(PROC_TRACE_STATUS) XEND }; static struct xlat umtx_ops[] = { X(UMTX_OP_LOCK) X(UMTX_OP_UNLOCK) X(UMTX_OP_WAIT) X(UMTX_OP_WAKE) X(UMTX_OP_MUTEX_TRYLOCK) X(UMTX_OP_MUTEX_LOCK) X(UMTX_OP_MUTEX_UNLOCK) X(UMTX_OP_SET_CEILING) X(UMTX_OP_CV_WAIT) X(UMTX_OP_CV_SIGNAL) X(UMTX_OP_CV_BROADCAST) X(UMTX_OP_WAIT_UINT) X(UMTX_OP_RW_RDLOCK) X(UMTX_OP_RW_WRLOCK) X(UMTX_OP_RW_UNLOCK) X(UMTX_OP_WAIT_UINT_PRIVATE) X(UMTX_OP_WAKE_PRIVATE) X(UMTX_OP_MUTEX_WAIT) X(UMTX_OP_MUTEX_WAKE) X(UMTX_OP_SEM_WAIT) X(UMTX_OP_SEM_WAKE) X(UMTX_OP_NWAKE_PRIVATE) X(UMTX_OP_MUTEX_WAKE2) XEND }; +static struct xlat at_flags[] = { + X(AT_EACCESS) X(AT_SYMLINK_NOFOLLOW) X(AT_SYMLINK_FOLLOW) + X(AT_REMOVEDIR) XEND +}; + +static struct xlat access_modes[] = { + X(R_OK) X(W_OK) X(X_OK) XEND +}; + +static struct xlat sysarch_ops[] = { +#if defined(__i386__) || defined(__amd64__) + X(I386_GET_LDT) X(I386_SET_LDT) X(I386_GET_IOPERM) X(I386_SET_IOPERM) + X(I386_VM86) X(I386_GET_FSBASE) X(I386_SET_FSBASE) X(I386_GET_GSBASE) + X(I386_SET_GSBASE) X(I386_GET_XFPUSTATE) X(AMD64_GET_FSBASE) + X(AMD64_SET_FSBASE) X(AMD64_GET_GSBASE) X(AMD64_SET_GSBASE) + X(AMD64_GET_XFPUSTATE) +#endif + XEND +}; + +static struct xlat linux_socketcall_ops[] = { + X(LINUX_SOCKET) X(LINUX_BIND) X(LINUX_CONNECT) X(LINUX_LISTEN) + X(LINUX_ACCEPT) X(LINUX_GETSOCKNAME) X(LINUX_GETPEERNAME) + X(LINUX_SOCKETPAIR) X(LINUX_SEND) X(LINUX_RECV) X(LINUX_SENDTO) + X(LINUX_RECVFROM) X(LINUX_SHUTDOWN) X(LINUX_SETSOCKOPT) + X(LINUX_GETSOCKOPT) X(LINUX_SENDMSG) X(LINUX_RECVMSG) + XEND +}; + +static struct xlat sigprocmask_ops[] = { + X(SIG_BLOCK) X(SIG_UNBLOCK) X(SIG_SETMASK) + XEND +}; + #undef X #undef XEND /* * Searches an xlat array for a value, and returns it if found. Otherwise * return a string representation. */ static const char * lookup(struct xlat *xlat, int val, int base) { static char tmp[16]; for (; xlat->str != NULL; xlat++) if (xlat->val == val) return (xlat->str); switch (base) { case 8: sprintf(tmp, "0%o", val); break; case 16: sprintf(tmp, "0x%x", val); break; case 10: sprintf(tmp, "%u", val); break; default: errx(1,"Unknown lookup base"); break; } return (tmp); } static const char * xlookup(struct xlat *xlat, int val) { return (lookup(xlat, val, 16)); } -/* Searches an xlat array containing bitfield values. Remaining bits - set after removing the known ones are printed at the end: - IN|0x400 */ +/* + * Searches an xlat array containing bitfield values. Remaining bits + * set after removing the known ones are printed at the end: + * IN|0x400. + */ static char * xlookup_bits(struct xlat *xlat, int val) { int len, rem; static char str[512]; len = 0; rem = val; for (; xlat->str != NULL; xlat++) { if ((xlat->val & rem) == xlat->val) { - /* don't print the "all-bits-zero" string unless all - bits are really zero */ + /* + * Don't print the "all-bits-zero" string unless all + * bits are really zero. + */ if (xlat->val == 0 && val != 0) continue; len += sprintf(str + len, "%s|", xlat->str); rem &= ~(xlat->val); } } - /* if we have leftover bits or didn't match anything */ + + /* + * If we have leftover bits or didn't match anything, print + * the remainder. + */ if (rem || len == 0) len += sprintf(str + len, "0x%x", rem); if (len && str[len - 1] == '|') len--; str[len] = 0; return (str); } +void +init_syscalls(void) +{ + struct syscall *sc; + + STAILQ_INIT(&syscalls); + for (sc = decoded_syscalls; sc->name != NULL; sc++) + STAILQ_INSERT_HEAD(&syscalls, sc, entries); +} /* * If/when the list gets big, it might be desirable to do it * as a hash table or binary search. */ - struct syscall * -get_syscall(const char *name) +get_syscall(const char *name, int nargs) { struct syscall *sc; + int i; - sc = syscalls; if (name == NULL) return (NULL); - while (sc->name) { + STAILQ_FOREACH(sc, &syscalls, entries) if (strcmp(name, sc->name) == 0) return (sc); - sc++; + + /* It is unknown. Add it into the list. */ +#if DEBUG + fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name, + nargs); +#endif + + sc = calloc(1, sizeof(struct syscall)); + sc->name = strdup(name); + sc->ret_type = 1; + sc->nargs = nargs; + for (i = 0; i < nargs; i++) { + sc->args[i].offset = i; + /* Treat all unknown arguments as LongHex. */ + sc->args[i].type = LongHex; } - return (NULL); + STAILQ_INSERT_HEAD(&syscalls, sc, entries); + + return (sc); } /* - * get_struct - * * Copy a fixed amount of bytes from the process. */ - static int get_struct(pid_t pid, void *offset, void *buf, int len) { struct ptrace_io_desc iorequest; iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = offset; iorequest.piod_addr = buf; iorequest.piod_len = len; if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) return (-1); return (0); } #define MAXSIZE 4096 -#define BLOCKSIZE 1024 + /* - * get_string * Copy a string from the process. Note that it is * expected to be a C string, but if max is set, it will * only get that much. */ - static char * -get_string(pid_t pid, void *offset, int max) +get_string(pid_t pid, void *addr, int max) { struct ptrace_io_desc iorequest; - char *buf; - int diff, i, size, totalsize; + char *buf, *nbuf; + size_t offset, size, totalsize; - diff = 0; - totalsize = size = max ? (max + 1) : BLOCKSIZE; + offset = 0; + if (max) + size = max + 1; + else { + /* Read up to the end of the current page. */ + size = PAGE_SIZE - ((uintptr_t)addr % PAGE_SIZE); + if (size > MAXSIZE) + size = MAXSIZE; + } + totalsize = size; buf = malloc(totalsize); if (buf == NULL) return (NULL); for (;;) { - diff = totalsize - size; iorequest.piod_op = PIOD_READ_D; - iorequest.piod_offs = (char *)offset + diff; - iorequest.piod_addr = buf + diff; + iorequest.piod_offs = (char *)addr + offset; + iorequest.piod_addr = buf + offset; iorequest.piod_len = size; if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { free(buf); return (NULL); } - for (i = 0 ; i < size; i++) { - if (buf[diff + i] == '\0') + if (memchr(buf + offset, '\0', size) != NULL) + return (buf); + offset += size; + if (totalsize < MAXSIZE && max == 0) { + size = MAXSIZE - totalsize; + if (size > PAGE_SIZE) + size = PAGE_SIZE; + nbuf = realloc(buf, totalsize + size); + if (nbuf == NULL) { + buf[totalsize - 1] = '\0'; return (buf); - } - if (totalsize < MAXSIZE - BLOCKSIZE && max == 0) { - totalsize += BLOCKSIZE; - buf = realloc(buf, totalsize); - size = BLOCKSIZE; + } + buf = nbuf; + totalsize += size; } else { buf[totalsize - 1] = '\0'; return (buf); } } } static char * strsig2(int sig) { - char *tmp; + static char tmp[sizeof(int) * 3 + 1]; + char *ret; - tmp = strsig(sig); - if (tmp == NULL) - asprintf(&tmp, "%d", sig); - return (tmp); + ret = strsig(sig); + if (ret == NULL) { + snprintf(tmp, sizeof(tmp), "%d", sig); + ret = tmp; + } + return (ret); } +static void +print_kevent(FILE *fp, struct kevent *ke, int input) +{ + + switch (ke->filter) { + case EVFILT_READ: + case EVFILT_WRITE: + case EVFILT_VNODE: + case EVFILT_PROC: + case EVFILT_TIMER: + fprintf(fp, "%ju", (uintmax_t)ke->ident); + break; + case EVFILT_SIGNAL: + fputs(strsig2(ke->ident), fp); + break; + default: + fprintf(fp, "%p", (void *)ke->ident); + } + fprintf(fp, ",%s,%s,", xlookup(kevent_filters, ke->filter), + xlookup_bits(kevent_flags, ke->flags)); + switch (ke->filter) { + case EVFILT_READ: + case EVFILT_WRITE: + fputs(xlookup_bits(kevent_rdwr_fflags, ke->fflags), fp); + break; + case EVFILT_VNODE: + fputs(xlookup_bits(kevent_vnode_fflags, ke->fflags), fp); + break; + case EVFILT_PROC: + fputs(xlookup_bits(kevent_proc_fflags, ke->fflags), fp); + break; + case EVFILT_TIMER: + fputs(xlookup_bits(kevent_timer_fflags, ke->fflags), fp); + break; + case EVFILT_USER: { + int ctrl, data; + + ctrl = ke->fflags & NOTE_FFCTRLMASK; + data = ke->fflags & NOTE_FFLAGSMASK; + if (input) { + fputs(xlookup(kevent_user_ffctrl, ctrl), fp); + if (ke->fflags & NOTE_TRIGGER) + fputs("|NOTE_TRIGGER", fp); + if (data != 0) + fprintf(fp, "|%#x", data); + } else { + fprintf(fp, "%#x", data); + } + break; + } + default: + fprintf(fp, "%#x", ke->fflags); + } + fprintf(fp, ",%p,%p", (void *)ke->data, (void *)ke->udata); +} + /* - * print_arg * Converts a syscall argument into a string. Said string is - * allocated via malloc(), so needs to be free()'d. The file - * descriptor is for the process' memory (via /proc), and is used - * to get any data (where the argument is a pointer). sc is + * allocated via malloc(), so needs to be free()'d. sc is * a pointer to the syscall description (see above); args is * an array of all of the system call arguments. */ - char * -print_arg(struct syscall_args *sc, unsigned long *args, long retval, +print_arg(struct syscall_args *sc, unsigned long *args, long *retval, struct trussinfo *trussinfo) { + FILE *fp; char *tmp; + size_t tmplen; pid_t pid; - tmp = NULL; - pid = trussinfo->pid; + fp = open_memstream(&tmp, &tmplen); + pid = trussinfo->curthread->proc->pid; switch (sc->type & ARG_MASK) { case Hex: - asprintf(&tmp, "0x%x", (int)args[sc->offset]); + fprintf(fp, "0x%x", (int)args[sc->offset]); break; case Octal: - asprintf(&tmp, "0%o", (int)args[sc->offset]); + fprintf(fp, "0%o", (int)args[sc->offset]); break; case Int: - asprintf(&tmp, "%d", (int)args[sc->offset]); + fprintf(fp, "%d", (int)args[sc->offset]); break; case LongHex: - asprintf(&tmp, "0x%lx", args[sc->offset]); - break; + fprintf(fp, "0x%lx", args[sc->offset]); + break; + case Long: + fprintf(fp, "%ld", args[sc->offset]); + break; case Name: { /* NULL-terminated string. */ char *tmp2; + tmp2 = get_string(pid, (void*)args[sc->offset], 0); - asprintf(&tmp, "\"%s\"", tmp2); + fprintf(fp, "\"%s\"", tmp2); free(tmp2); break; } case BinString: { - /* Binary block of data that might have printable characters. - XXX If type|OUT, assume that the length is the syscall's - return value. Otherwise, assume that the length of the block - is in the next syscall argument. */ + /* + * Binary block of data that might have printable characters. + * XXX If type|OUT, assume that the length is the syscall's + * return value. Otherwise, assume that the length of the block + * is in the next syscall argument. + */ int max_string = trussinfo->strsize; - char tmp2[max_string+1], *tmp3; + char tmp2[max_string + 1], *tmp3; int len; int truncated = 0; if (sc->type & OUT) - len = retval; + len = retval[0]; else len = args[sc->offset + 1]; - /* Don't print more than max_string characters, to avoid word - wrap. If we have to truncate put some ... after the string. - */ + /* + * Don't print more than max_string characters, to avoid word + * wrap. If we have to truncate put some ... after the string. + */ if (len > max_string) { len = max_string; truncated = 1; } if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) != -1) { tmp3 = malloc(len * 4 + 1); while (len) { if (strvisx(tmp3, tmp2, len, VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string) break; len--; truncated = 1; }; - asprintf(&tmp, "\"%s\"%s", tmp3, truncated ? + fprintf(fp, "\"%s\"%s", tmp3, truncated ? "..." : ""); free(tmp3); } else { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); } break; } + case ExecArgs: + case ExecEnv: case StringArray: { - int num, size, i; - char *tmp2; + uintptr_t addr; + union { + char *strarray[0]; + char buf[PAGE_SIZE]; + } u; char *string; - char *strarray[100]; /* XXX This is ugly. */ + size_t len; + u_int first, i; - if (get_struct(pid, (void *)args[sc->offset], - (void *)&strarray, sizeof(strarray)) == -1) - err(1, "get_struct %p", (void *)args[sc->offset]); - num = 0; - size = 0; + /* + * Only parse argv[] and environment arrays from exec calls + * if requested. + */ + if (((sc->type & ARG_MASK) == ExecArgs && + (trussinfo->flags & EXECVEARGS) == 0) || + ((sc->type & ARG_MASK) == ExecEnv && + (trussinfo->flags & EXECVEENVS) == 0)) { + fprintf(fp, "0x%lx", args[sc->offset]); + break; + } - /* Find out how large of a buffer we'll need. */ - while (strarray[num] != NULL) { - string = get_string(pid, (void*)strarray[num], 0); - size += strlen(string); - free(string); - num++; + /* + * Read a page of pointers at a time. Punt if the top-level + * pointer is not aligned. Note that the first read is of + * a partial page. + */ + addr = args[sc->offset]; + if (addr % sizeof(char *) != 0) { + fprintf(fp, "0x%lx", args[sc->offset]); + break; } - size += 4 + (num * 4); - tmp = (char *)malloc(size); - tmp2 = tmp; - tmp2 += sprintf(tmp2, " ["); - for (i = 0; i < num; i++) { - string = get_string(pid, (void*)strarray[i], 0); - tmp2 += sprintf(tmp2, " \"%s\"%c", string, - (i + 1 == num) ? ' ' : ','); + len = PAGE_SIZE - (addr & PAGE_MASK); + if (get_struct(pid, (void *)addr, u.buf, len) == -1) { + fprintf(fp, "0x%lx", args[sc->offset]); + break; + } + + fputc('[', fp); + first = 1; + i = 0; + while (u.strarray[i] != NULL) { + string = get_string(pid, u.strarray[i], 0); + fprintf(fp, "%s \"%s\"", first ? "" : ",", string); free(string); + first = 0; + + i++; + if (i == len / sizeof(char *)) { + addr += len; + len = PAGE_SIZE; + if (get_struct(pid, (void *)addr, u.buf, len) == + -1) { + fprintf(fp, ", "); + break; + } + i = 0; + } } - tmp2 += sprintf(tmp2, "]"); + fputs(" ]", fp); break; } #ifdef __LP64__ case Quad: - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "%ld", args[sc->offset]); break; + case QuadHex: + fprintf(fp, "0x%lx", args[sc->offset]); + break; #else - case Quad: { + case Quad: + case QuadHex: { unsigned long long ll; - ll = *(unsigned long long *)(args + sc->offset); - asprintf(&tmp, "0x%llx", ll); + +#if _BYTE_ORDER == _LITTLE_ENDIAN + ll = (unsigned long long)args[sc->offset + 1] << 32 | + args[sc->offset]; +#else + ll = (unsigned long long)args[sc->offset] << 32 | + args[sc->offset + 1]; +#endif + if ((sc->type & ARG_MASK) == Quad) + fprintf(fp, "%lld", ll); + else + fprintf(fp, "0x%llx", ll); break; } #endif case Ptr: - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; case Readlinkres: { char *tmp2; - if (retval == -1) { - tmp = strdup(""); + + if (retval[0] == -1) break; - } - tmp2 = get_string(pid, (void*)args[sc->offset], retval); - asprintf(&tmp, "\"%s\"", tmp2); + tmp2 = get_string(pid, (void*)args[sc->offset], retval[0]); + fprintf(fp, "\"%s\"", tmp2); free(tmp2); break; } case Ioctl: { - const char *temp = ioctlname(args[sc->offset]); + const char *temp; + unsigned long cmd; + + cmd = args[sc->offset]; + temp = ioctlname(cmd); if (temp) - tmp = strdup(temp); + fputs(temp, fp); else { - unsigned long arg = args[sc->offset]; - asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", - arg, arg & IOC_OUT ? "R" : "", - arg & IOC_IN ? "W" : "", IOCGROUP(arg), - isprint(IOCGROUP(arg)) ? (char)IOCGROUP(arg) : '?', - arg & 0xFF, IOCPARM_LEN(arg)); + fprintf(fp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", + cmd, cmd & IOC_OUT ? "R" : "", + cmd & IOC_IN ? "W" : "", IOCGROUP(cmd), + isprint(IOCGROUP(cmd)) ? (char)IOCGROUP(cmd) : '?', + cmd & 0xFF, IOCPARM_LEN(cmd)); } break; } case Umtx: { struct umtx umtx; if (get_struct(pid, (void *)args[sc->offset], &umtx, sizeof(umtx)) != -1) - asprintf(&tmp, "{ 0x%lx }", (long)umtx.u_owner); + fprintf(fp, "{ 0x%lx }", (long)umtx.u_owner); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Timespec: { struct timespec ts; + if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts)) != -1) - asprintf(&tmp, "{%ld.%09ld }", (long)ts.tv_sec, + fprintf(fp, "{ %jd.%09ld }", (intmax_t)ts.tv_sec, ts.tv_nsec); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Timeval: { struct timeval tv; + if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1) - asprintf(&tmp, "{%ld.%06ld }", (long)tv.tv_sec, + fprintf(fp, "{ %jd.%06ld }", (intmax_t)tv.tv_sec, tv.tv_usec); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Timeval2: { struct timeval tv[2]; + if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1) - asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", - (long)tv[0].tv_sec, tv[0].tv_usec, - (long)tv[1].tv_sec, tv[1].tv_usec); + fprintf(fp, "{ %jd.%06ld, %jd.%06ld }", + (intmax_t)tv[0].tv_sec, tv[0].tv_usec, + (intmax_t)tv[1].tv_sec, tv[1].tv_usec); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Itimerval: { struct itimerval itv; + if (get_struct(pid, (void *)args[sc->offset], &itv, sizeof(itv)) != -1) - asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", - (long)itv.it_interval.tv_sec, + fprintf(fp, "{ %jd.%06ld, %jd.%06ld }", + (intmax_t)itv.it_interval.tv_sec, itv.it_interval.tv_usec, - (long)itv.it_value.tv_sec, + (intmax_t)itv.it_value.tv_sec, itv.it_value.tv_usec); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case LinuxSockArgs: { struct linux_socketcall_args largs; - if (get_struct(pid, (void *)args[sc->offset], (void *)&largs, - sizeof(largs)) == -1) { - err(1, "get_struct %p", (void *)args[sc->offset]); - } - const char *what; - char buf[30]; - switch (largs.what) { - case LINUX_SOCKET: - what = "LINUX_SOCKET"; - break; - case LINUX_BIND: - what = "LINUX_BIND"; - break; - case LINUX_CONNECT: - what = "LINUX_CONNECT"; - break; - case LINUX_LISTEN: - what = "LINUX_LISTEN"; - break; - case LINUX_ACCEPT: - what = "LINUX_ACCEPT"; - break; - case LINUX_GETSOCKNAME: - what = "LINUX_GETSOCKNAME"; - break; - case LINUX_GETPEERNAME: - what = "LINUX_GETPEERNAME"; - break; - case LINUX_SOCKETPAIR: - what = "LINUX_SOCKETPAIR"; - break; - case LINUX_SEND: - what = "LINUX_SEND"; - break; - case LINUX_RECV: - what = "LINUX_RECV"; - break; - case LINUX_SENDTO: - what = "LINUX_SENDTO"; - break; - case LINUX_RECVFROM: - what = "LINUX_RECVFROM"; - break; - case LINUX_SHUTDOWN: - what = "LINUX_SHUTDOWN"; - break; - case LINUX_SETSOCKOPT: - what = "LINUX_SETSOCKOPT"; - break; - case LINUX_GETSOCKOPT: - what = "LINUX_GETSOCKOPT"; - break; - case LINUX_SENDMSG: - what = "LINUX_SENDMSG"; - break; - case LINUX_RECVMSG: - what = "LINUX_RECVMSG"; - break; - default: - sprintf(buf, "%d", largs.what); - what = buf; - break; - } - asprintf(&tmp, "(0x%lx)%s, 0x%lx", args[sc->offset], what, (long unsigned int)largs.args); + if (get_struct(pid, (void *)args[sc->offset], (void *)&largs, + sizeof(largs)) != -1) + fprintf(fp, "{ %s, 0x%lx }", + lookup(linux_socketcall_ops, largs.what, 10), + (long unsigned int)largs.args); + else + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Pollfd: { /* * XXX: A Pollfd argument expects the /next/ syscall argument * to be the number of fds in the array. This matches the poll * syscall. */ struct pollfd *pfd; - int numfds = args[sc->offset+1]; - int bytes = sizeof(struct pollfd) * numfds; - int i, tmpsize, u, used; - const int per_fd = 100; + int numfds = args[sc->offset + 1]; + size_t bytes = sizeof(struct pollfd) * numfds; + int i; if ((pfd = malloc(bytes)) == NULL) - err(1, "Cannot malloc %d bytes for pollfd array", + err(1, "Cannot malloc %zu bytes for pollfd array", bytes); if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) != -1) { - used = 0; - tmpsize = 1 + per_fd * numfds + 2; - if ((tmp = malloc(tmpsize)) == NULL) - err(1, "Cannot alloc %d bytes for poll output", - tmpsize); - - tmp[used++] = '{'; + fputs("{", fp); for (i = 0; i < numfds; i++) { - - u = snprintf(tmp + used, per_fd, "%s%d/%s", - i > 0 ? " " : "", pfd[i].fd, + fprintf(fp, " %d/%s", pfd[i].fd, xlookup_bits(poll_flags, pfd[i].events)); - if (u > 0) - used += u < per_fd ? u : per_fd; } - tmp[used++] = '}'; - tmp[used++] = '\0'; + fputs(" }", fp); } else { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); } free(pfd); break; } case Fd_set: { /* * XXX: A Fd_set argument expects the /first/ syscall argument * to be the number of fds in the array. This matches the * select syscall. */ fd_set *fds; int numfds = args[0]; - int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; - int i, tmpsize, u, used; - const int per_fd = 20; + size_t bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; + int i; if ((fds = malloc(bytes)) == NULL) - err(1, "Cannot malloc %d bytes for fd_set array", + err(1, "Cannot malloc %zu bytes for fd_set array", bytes); if (get_struct(pid, (void *)args[sc->offset], fds, bytes) != -1) { - used = 0; - tmpsize = 1 + numfds * per_fd + 2; - if ((tmp = malloc(tmpsize)) == NULL) - err(1, "Cannot alloc %d bytes for fd_set " - "output", tmpsize); - - tmp[used++] = '{'; + fputs("{", fp); for (i = 0; i < numfds; i++) { - if (FD_ISSET(i, fds)) { - u = snprintf(tmp + used, per_fd, "%d ", - i); - if (u > 0) - used += u < per_fd ? u : per_fd; - } + if (FD_ISSET(i, fds)) + fprintf(fp, " %d", i); } - if (tmp[used-1] == ' ') - used--; - tmp[used++] = '}'; - tmp[used++] = '\0'; + fputs(" }", fp); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); free(fds); break; } case Signal: - tmp = strsig2(args[sc->offset]); + fputs(strsig2(args[sc->offset]), fp); break; case Sigset: { long sig; sigset_t ss; - int i, used; - char *signame; + int i, first; sig = args[sc->offset]; if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, sizeof(ss)) == -1) { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } - tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */ - used = 0; + fputs("{ ", fp); + first = 1; for (i = 1; i < sys_nsig; i++) { if (sigismember(&ss, i)) { - signame = strsig(i); - used += sprintf(tmp + used, "%s|", signame); - free(signame); + fprintf(fp, "%s%s", !first ? "|" : "", + strsig(i)); + first = 0; } } - if (used) - tmp[used-1] = 0; - else - strcpy(tmp, "0x0"); + if (!first) + fputc(' ', fp); + fputc('}', fp); break; } case Sigprocmask: { - switch (args[sc->offset]) { -#define S(a) case a: tmp = strdup(#a); break; - S(SIG_BLOCK); - S(SIG_UNBLOCK); - S(SIG_SETMASK); -#undef S - } - if (tmp == NULL) - asprintf(&tmp, "0x%lx", args[sc->offset]); + fputs(xlookup(sigprocmask_ops, args[sc->offset]), fp); break; } case Fcntlflag: { - /* XXX output depends on the value of the previous argument */ - switch (args[sc->offset-1]) { + /* XXX: Output depends on the value of the previous argument. */ + switch (args[sc->offset - 1]) { case F_SETFD: - tmp = strdup(xlookup_bits(fcntlfd_arg, - args[sc->offset])); + fputs(xlookup_bits(fcntlfd_arg, args[sc->offset]), fp); break; case F_SETFL: - tmp = strdup(xlookup_bits(fcntlfl_arg, - args[sc->offset])); + fputs(xlookup_bits(fcntlfl_arg, args[sc->offset]), fp); break; case F_GETFD: case F_GETFL: case F_GETOWN: - tmp = strdup(""); break; default: - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } break; } case Open: - tmp = strdup(xlookup_bits(open_flags, args[sc->offset])); + fputs(xlookup_bits(open_flags, args[sc->offset]), fp); break; case Fcntl: - tmp = strdup(xlookup(fcntl_arg, args[sc->offset])); + fputs(xlookup(fcntl_arg, args[sc->offset]), fp); break; case Mprot: - tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset])); + fputs(xlookup_bits(mprot_flags, args[sc->offset]), fp); break; case Mmapflags: { - char *base, *alignstr; int align, flags; /* * MAP_ALIGNED can't be handled by xlookup_bits(), so * generate that string manually and prepend it to the * string from xlookup_bits(). Have to be careful to * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is * the only flag. */ flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK; align = args[sc->offset] & MAP_ALIGNMENT_MASK; if (align != 0) { if (align == MAP_ALIGNED_SUPER) - alignstr = strdup("MAP_ALIGNED_SUPER"); + fputs("MAP_ALIGNED_SUPER", fp); else - asprintf(&alignstr, "MAP_ALIGNED(%d)", + fprintf(fp, "MAP_ALIGNED(%d)", align >> MAP_ALIGNMENT_SHIFT); - if (flags == 0) { - tmp = alignstr; + if (flags == 0) break; - } - } else - alignstr = NULL; - base = strdup(xlookup_bits(mmap_flags, flags)); - if (alignstr == NULL) { - tmp = base; - break; + fputc('|', fp); } - asprintf(&tmp, "%s|%s", alignstr, base); - free(alignstr); - free(base); + fputs(xlookup_bits(mmap_flags, flags), fp); break; } case Whence: - tmp = strdup(xlookup(whence_arg, args[sc->offset])); + fputs(xlookup(whence_arg, args[sc->offset]), fp); break; case Sockdomain: - tmp = strdup(xlookup(sockdomain_arg, args[sc->offset])); + fputs(xlookup(sockdomain_arg, args[sc->offset]), fp); break; - case Socktype: - tmp = strdup(xlookup(socktype_arg, args[sc->offset])); + case Socktype: { + int type, flags; + + flags = args[sc->offset] & (SOCK_CLOEXEC | SOCK_NONBLOCK); + type = args[sc->offset] & ~flags; + fputs(xlookup(socktype_arg, type), fp); + if (flags & SOCK_CLOEXEC) + fprintf(fp, "|SOCK_CLOEXEC"); + if (flags & SOCK_NONBLOCK) + fprintf(fp, "|SOCK_NONBLOCK"); break; + } case Shutdown: - tmp = strdup(xlookup(shutdown_arg, args[sc->offset])); + fputs(xlookup(shutdown_arg, args[sc->offset]), fp); break; case Resource: - tmp = strdup(xlookup(resource_arg, args[sc->offset])); + fputs(xlookup(resource_arg, args[sc->offset]), fp); break; case Pathconf: - tmp = strdup(xlookup(pathconf_arg, args[sc->offset])); + fputs(xlookup(pathconf_arg, args[sc->offset]), fp); break; case Rforkflags: - tmp = strdup(xlookup_bits(rfork_flags, args[sc->offset])); + fputs(xlookup_bits(rfork_flags, args[sc->offset]), fp); break; case Sockaddr: { - struct sockaddr_storage ss; char addr[64]; struct sockaddr_in *lsin; struct sockaddr_in6 *lsin6; struct sockaddr_un *sun; struct sockaddr *sa; - char *p; + socklen_t len; u_char *q; - int i; if (args[sc->offset] == 0) { - asprintf(&tmp, "NULL"); + fputs("NULL", fp); break; } - /* yuck: get ss_len */ - if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, - sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) - err(1, "get_struct %p", (void *)args[sc->offset]); /* - * If ss_len is 0, then try to guess from the sockaddr type. - * AF_UNIX may be initialized incorrectly, so always frob - * it by using the "right" size. + * Extract the address length from the next argument. If + * this is an output sockaddr (OUT is set), then the + * next argument is a pointer to a socklen_t. Otherwise + * the next argument contains a socklen_t by value. */ - if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) { - switch (ss.ss_family) { - case AF_INET: - ss.ss_len = sizeof(*lsin); + if (sc->type & OUT) { + if (get_struct(pid, (void *)args[sc->offset + 1], + &len, sizeof(len)) == -1) { + fprintf(fp, "0x%lx", args[sc->offset]); break; - case AF_UNIX: - ss.ss_len = sizeof(*sun); - break; - default: - /* hurrrr */ - break; } + } else + len = args[sc->offset + 1]; + + /* If the length is too small, just bail. */ + if (len < sizeof(*sa)) { + fprintf(fp, "0x%lx", args[sc->offset]); + break; } - if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, - ss.ss_len) == -1) { - err(2, "get_struct %p", (void *)args[sc->offset]); + + sa = calloc(1, len); + if (get_struct(pid, (void *)args[sc->offset], sa, len) == -1) { + free(sa); + fprintf(fp, "0x%lx", args[sc->offset]); + break; } - switch (ss.ss_family) { + switch (sa->sa_family) { case AF_INET: - lsin = (struct sockaddr_in *)&ss; - inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); - asprintf(&tmp, "{ AF_INET %s:%d }", addr, + if (len < sizeof(*lsin)) + goto sockaddr_short; + lsin = (struct sockaddr_in *)(void *)sa; + inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof(addr)); + fprintf(fp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port)); break; case AF_INET6: - lsin6 = (struct sockaddr_in6 *)&ss; + if (len < sizeof(*lsin6)) + goto sockaddr_short; + lsin6 = (struct sockaddr_in6 *)(void *)sa; inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, - sizeof addr); - asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, + sizeof(addr)); + fprintf(fp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port)); break; case AF_UNIX: - sun = (struct sockaddr_un *)&ss; - asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); + sun = (struct sockaddr_un *)sa; + fprintf(fp, "{ AF_UNIX \"%.*s\" }", + (int)(len - offsetof(struct sockaddr_un, sun_path)), + sun->sun_path); break; default: - sa = (struct sockaddr *)&ss; - asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data " - "= {%n%*s } }", (int)sa->sa_len, (int)sa->sa_family, - &i, 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - - (char *)sa)), ""); - if (tmp != NULL) { - p = tmp + i; - for (q = (u_char *)&sa->sa_data; - q < (u_char *)sa + sa->sa_len; q++) - p += sprintf(p, " %#02x,", *q); - } + sockaddr_short: + fprintf(fp, + "{ sa_len = %d, sa_family = %d, sa_data = {", + (int)sa->sa_len, (int)sa->sa_family); + for (q = (u_char *)sa->sa_data; + q < (u_char *)sa + len; q++) + fprintf(fp, "%s 0x%02x", + q == (u_char *)sa->sa_data ? "" : ",", + *q); + fputs(" } }", fp); } + free(sa); break; } case Sigaction: { struct sigaction sa; - char *hand; - const char *h; if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) != -1) { - asprintf(&hand, "%p", sa.sa_handler); + fputs("{ ", fp); if (sa.sa_handler == SIG_DFL) - h = "SIG_DFL"; + fputs("SIG_DFL", fp); else if (sa.sa_handler == SIG_IGN) - h = "SIG_IGN"; + fputs("SIG_IGN", fp); else - h = hand; - - asprintf(&tmp, "{ %s %s ss_t }", h, + fprintf(fp, "%p", sa.sa_handler); + fprintf(fp, " %s ss_t }", xlookup_bits(sigaction_flags, sa.sa_flags)); - free(hand); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Kevent: { /* - * XXX XXX: the size of the array is determined by either the - * next syscall argument, or by the syscall returnvalue, + * XXX XXX: The size of the array is determined by either the + * next syscall argument, or by the syscall return value, * depending on which argument number we are. This matches the * kevent syscall, but luckily that's the only syscall that uses * them. */ struct kevent *ke; int numevents = -1; - int bytes = 0; - int i, tmpsize, u, used; - const int per_ke = 100; + size_t bytes; + int i; if (sc->offset == 1) numevents = args[sc->offset+1]; - else if (sc->offset == 3 && retval != -1) - numevents = retval; + else if (sc->offset == 3 && retval[0] != -1) + numevents = retval[0]; - if (numevents >= 0) + if (numevents >= 0) { bytes = sizeof(struct kevent) * numevents; - if ((ke = malloc(bytes)) == NULL) - err(1, "Cannot malloc %d bytes for kevent array", - bytes); + if ((ke = malloc(bytes)) == NULL) + err(1, + "Cannot malloc %zu bytes for kevent array", + bytes); + } else + ke = NULL; if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], ke, bytes) != -1) { - used = 0; - tmpsize = 1 + per_ke * numevents + 2; - if ((tmp = malloc(tmpsize)) == NULL) - err(1, "Cannot alloc %d bytes for kevent " - "output", tmpsize); - - tmp[used++] = '{'; + fputc('{', fp); for (i = 0; i < numevents; i++) { - u = snprintf(tmp + used, per_ke, - "%s%p,%s,%s,%d,%p,%p", - i > 0 ? " " : "", - (void *)ke[i].ident, - xlookup(kevent_filters, ke[i].filter), - xlookup_bits(kevent_flags, ke[i].flags), - ke[i].fflags, - (void *)ke[i].data, - (void *)ke[i].udata); - if (u > 0) - used += u < per_ke ? u : per_ke; + fputc(' ', fp); + print_kevent(fp, &ke[i], sc->offset == 1); } - tmp[used++] = '}'; - tmp[used++] = '\0'; + fputs(" }", fp); } else { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); } free(ke); break; } case Stat: { struct stat st; + if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) != -1) { char mode[12]; + strmode(st.st_mode, mode); - asprintf(&tmp, - "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode, - (intmax_t)st.st_ino, (intmax_t)st.st_size, + fprintf(fp, + "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode, + (uintmax_t)st.st_ino, (intmax_t)st.st_size, (long)st.st_blksize); } else { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); } break; } + case StatFs: { + unsigned int i; + struct statfs buf; + + if (get_struct(pid, (void *)args[sc->offset], &buf, + sizeof(buf)) != -1) { + char fsid[17]; + + bzero(fsid, sizeof(fsid)); + if (buf.f_fsid.val[0] != 0 || buf.f_fsid.val[1] != 0) { + for (i = 0; i < sizeof(buf.f_fsid); i++) + snprintf(&fsid[i*2], + sizeof(fsid) - (i*2), "%02x", + ((u_char *)&buf.f_fsid)[i]); + } + fprintf(fp, + "{ fstypename=%s,mntonname=%s,mntfromname=%s," + "fsid=%s }", buf.f_fstypename, buf.f_mntonname, + buf.f_mntfromname, fsid); + } else + fprintf(fp, "0x%lx", args[sc->offset]); + break; + } + case Rusage: { struct rusage ru; + if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) != -1) { - asprintf(&tmp, - "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }", - (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, - (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, + fprintf(fp, + "{ u=%jd.%06ld,s=%jd.%06ld,in=%ld,out=%ld }", + (intmax_t)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, + (intmax_t)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, ru.ru_inblock, ru.ru_oublock); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Rlimit: { struct rlimit rl; + if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) != -1) { - asprintf(&tmp, "{ cur=%ju,max=%ju }", + fprintf(fp, "{ cur=%ju,max=%ju }", rl.rlim_cur, rl.rlim_max); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case ExitStatus: { - char *signame; int status; - signame = NULL; + if (get_struct(pid, (void *)args[sc->offset], &status, sizeof(status)) != -1) { + fputs("{ ", fp); if (WIFCONTINUED(status)) - tmp = strdup("{ CONTINUED }"); + fputs("CONTINUED", fp); else if (WIFEXITED(status)) - asprintf(&tmp, "{ EXITED,val=%d }", + fprintf(fp, "EXITED,val=%d", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) - asprintf(&tmp, "{ SIGNALED,sig=%s%s }", - signame = strsig2(WTERMSIG(status)), + fprintf(fp, "SIGNALED,sig=%s%s", + strsig2(WTERMSIG(status)), WCOREDUMP(status) ? ",cored" : ""); else - asprintf(&tmp, "{ STOPPED,sig=%s }", - signame = strsig2(WTERMSIG(status))); + fprintf(fp, "STOPPED,sig=%s", + strsig2(WTERMSIG(status))); + fputs(" }", fp); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); - free(signame); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Waitoptions: - tmp = strdup(xlookup_bits(wait_options, args[sc->offset])); + fputs(xlookup_bits(wait_options, args[sc->offset]), fp); break; case Idtype: - tmp = strdup(xlookup(idtype_arg, args[sc->offset])); + fputs(xlookup(idtype_arg, args[sc->offset]), fp); break; case Procctl: - tmp = strdup(xlookup(procctl_arg, args[sc->offset])); + fputs(xlookup(procctl_arg, args[sc->offset]), fp); break; case Umtxop: - tmp = strdup(xlookup(umtx_ops, args[sc->offset])); + fputs(xlookup(umtx_ops, args[sc->offset]), fp); break; + case Atfd: + if ((int)args[sc->offset] == AT_FDCWD) + fputs("AT_FDCWD", fp); + else + fprintf(fp, "%d", (int)args[sc->offset]); + break; + case Atflags: + fputs(xlookup_bits(at_flags, args[sc->offset]), fp); + break; + case Accessmode: + if (args[sc->offset] == F_OK) + fputs("F_OK", fp); + else + fputs(xlookup_bits(access_modes, args[sc->offset]), fp); + break; + case Sysarch: + fputs(xlookup(sysarch_ops, args[sc->offset]), fp); + break; + case PipeFds: + /* + * The pipe() system call in the kernel returns its + * two file descriptors via return values. However, + * the interface exposed by libc is that pipe() + * accepts a pointer to an array of descriptors. + * Format the output to match the libc API by printing + * the returned file descriptors as a fake argument. + * + * Overwrite the first retval to signal a successful + * return as well. + */ + fprintf(fp, "{ %ld, %ld }", retval[0], retval[1]); + retval[0] = 0; + break; default: errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); } + fclose(fp); return (tmp); } /* - * print_syscall * Print (to outfile) the system call and its arguments. Note that * nargs is the number of arguments (not the number of words; this is * potentially confusing, I know). */ - void print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) { struct timespec timediff; int i, len; len = 0; if (trussinfo->flags & FOLLOWFORKS) - len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); + len += fprintf(trussinfo->outfile, "%5d: ", + trussinfo->curthread->proc->pid); if (name != NULL && (strcmp(name, "execve") == 0 || strcmp(name, "exit") == 0)) { clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); } if (trussinfo->flags & ABSOLUTETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->start_time, &timediff); - len += fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, timediff.tv_nsec); + len += fprintf(trussinfo->outfile, "%jd.%09ld ", + (intmax_t)timediff.tv_sec, timediff.tv_nsec); } if (trussinfo->flags & RELATIVETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->curthread->before, &timediff); - len += fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, timediff.tv_nsec); + len += fprintf(trussinfo->outfile, "%jd.%09ld ", + (intmax_t)timediff.tv_sec, timediff.tv_nsec); } len += fprintf(trussinfo->outfile, "%s(", name); for (i = 0; i < nargs; i++) { if (s_args[i]) len += fprintf(trussinfo->outfile, "%s", s_args[i]); else len += fprintf(trussinfo->outfile, ""); len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : ""); } len += fprintf(trussinfo->outfile, ")"); for (i = 0; i < 6 - (len / 8); i++) fprintf(trussinfo->outfile, "\t"); } void print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, - char **s_args, int errorp, long retval, struct syscall *sc) + char **s_args, int errorp, long *retval, struct syscall *sc) { struct timespec timediff; if (trussinfo->flags & COUNTONLY) { - if (!sc) - return; clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); timespecsubt(&trussinfo->curthread->after, &trussinfo->curthread->before, &timediff); timespecadd(&sc->time, &timediff, &sc->time); sc->ncalls++; if (errorp) sc->nerror++; return; } print_syscall(trussinfo, name, nargs, s_args); fflush(trussinfo->outfile); if (errorp) - fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval, - strerror(retval)); - else { - /* - * Because pipe(2) has a special assembly glue to provide the - * libc API, we have to adjust retval. - */ - if (name != NULL && strcmp(name, "pipe") == 0) - retval = 0; - fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval); + fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval[0], + strerror(retval[0])); +#ifndef __LP64__ + else if (sc->ret_type == 2) { + off_t off; + +#if _BYTE_ORDER == _LITTLE_ENDIAN + off = (off_t)retval[1] << 32 | retval[0]; +#else + off = (off_t)retval[0] << 32 | retval[1]; +#endif + fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", (intmax_t)off, + (intmax_t)off); } +#endif + else + fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval[0], + retval[0]); } void print_summary(struct trussinfo *trussinfo) { struct timespec total = {0, 0}; struct syscall *sc; int ncall, nerror; fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n", "syscall", "seconds", "calls", "errors"); ncall = nerror = 0; - for (sc = syscalls; sc->name != NULL; sc++) + STAILQ_FOREACH(sc, &syscalls, entries) if (sc->ncalls) { fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", sc->name, (intmax_t)sc->time.tv_sec, sc->time.tv_nsec, sc->ncalls, sc->nerror); timespecadd(&total, &sc->time, &total); ncall += sc->ncalls; nerror += sc->nerror; } fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n", "", "-------------", "-------", "-------"); fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); } Index: stable/10/usr.bin/truss/truss.1 =================================================================== --- stable/10/usr.bin/truss/truss.1 (revision 290051) +++ stable/10/usr.bin/truss/truss.1 (revision 290052) @@ -1,107 +1,107 @@ .\" $FreeBSD$ .\" -.Dd May 12, 2009 +.Dd October 9, 2015 .Dt TRUSS 1 .Os .Sh NAME .Nm truss .Nd trace system calls .Sh SYNOPSIS .Nm .Op Fl facedDS .Op Fl o Ar file .Op Fl s Ar strsize .Fl p Ar pid .Nm .Op Fl facedDS .Op Fl o Ar file .Op Fl s Ar strsize .Ar command Op Ar args .Sh DESCRIPTION The .Nm utility traces the system calls called by the specified process or program. Output is to the specified output file, or standard error by default. It does this by stopping and restarting the process being monitored via .Xr ptrace 2 . .Pp The options are as follows: .Bl -tag -width indent .It Fl f Trace descendants of the original traced process created by .Xr fork 2 , .Xr vfork 2 , etc. .It Fl a Show the argument strings that are passed in each .Xr execve 2 system call. .It Fl c -Do not display individual system calls. +Do not display individual system calls or signals. Instead, before exiting, print a summary containing for each system call: the total system time used, the number of times the call was invoked, and the number of times the call returned with an error. .It Fl e Show the environment strings that are passed in each .Xr execve 2 system call. .It Fl d Include timestamps in the output showing the time elapsed since the trace was started. .It Fl D Include timestamps in the output showing the time elapsed since the last recorded event. .It Fl S Do not display information about signals received by the process. (Normally, .Nm displays signal as well as system call events.) .It Fl o Ar file Print the output to the specified .Ar file instead of standard error. .It Fl s Ar strsize Display strings using at most .Ar strsize characters. If the buffer is larger, .Dq Li ... will be displayed at the end of the string. The default .Ar strsize is 32. .It Fl p Ar pid Follow the process specified by .Ar pid instead of a new command. .It Ar command Op Ar args Execute .Ar command and trace the system calls of it. (The .Fl p and .Ar command options are mutually exclusive.) .El .Sh EXAMPLES # Follow the system calls used in echoing "hello" .Dl $ truss /bin/echo hello # Do the same, but put the output into a file .Dl $ truss -o /tmp/truss.out /bin/echo hello # Follow an already-running process .Dl $ truss -p 34 .Sh SEE ALSO .Xr kdump 1 , .Xr ktrace 1 , .Xr ptrace 2 .Sh HISTORY The .Nm command was written by .An Sean Eric Fagan for .Fx . It was modeled after similar commands available for System V Release 4 and SunOS. Index: stable/10/usr.bin/truss/truss.h =================================================================== --- stable/10/usr.bin/truss/truss.h (revision 290051) +++ stable/10/usr.bin/truss/truss.h (revision 290052) @@ -1,91 +1,124 @@ /* * Copyright 2001 Jamey Wood * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY 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. * * $FreeBSD$ */ +#include #include #define FOLLOWFORKS 0x00000001 #define RELATIVETIMESTAMPS 0x00000002 #define ABSOLUTETIMESTAMPS 0x00000004 #define NOSIGS 0x00000008 #define EXECVEARGS 0x00000010 #define EXECVEENVS 0x00000020 #define COUNTONLY 0x00000040 +struct procinfo; +struct trussinfo; + +struct procabi { + const char *type; + const char **syscallnames; + int nsyscalls; + int (*fetch_args)(struct trussinfo *, u_int); + int (*fetch_retval)(struct trussinfo *, long *, int *); +}; + +#define PROCABI(abi) DATA_SET(procabi, abi) + +/* + * This is confusingly named. It holds per-thread state about the + * currently executing system call. syscall.h defines a struct + * syscall that holds metadata used to format system call arguments. + * + * NB: args[] stores the raw argument values (e.g. from registers) + * passed to the system call. s_args[] stores a string representation + * of a system call's arguments. These do not necessarily map one to + * one. A system call description may omit individual arguments + * (padding) or combine adjacent arguments (e.g. when passing an off_t + * argument on a 32-bit system). The nargs member contains the count + * of valid pointers in s_args[], not args[]. + */ +struct current_syscall { + struct syscall *sc; + const char *name; + int number; + unsigned long args[10]; + unsigned int nargs; + char *s_args[10]; /* the printable arguments */ +}; + struct threadinfo { SLIST_ENTRY(threadinfo) entries; + struct procinfo *proc; lwpid_t tid; int in_syscall; - int in_fork; - void *fsc; + struct current_syscall cs; struct timespec before; struct timespec after; }; +struct procinfo { + LIST_ENTRY(procinfo) entries; + pid_t pid; + struct procabi *abi; + + SLIST_HEAD(, threadinfo) threadlist; +}; + struct trussinfo { - pid_t pid; int flags; - int pr_why; - int pr_data; int strsize; FILE *outfile; struct timespec start_time; struct threadinfo *curthread; - SLIST_HEAD(, threadinfo) threadlist; + LIST_HEAD(, procinfo) proclist; }; #define timespecsubt(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_nsec = (tvp)->tv_nsec - (uvp)->tv_nsec; \ if ((vvp)->tv_nsec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_nsec += 1000000000; \ } \ } while (0) #define timespecadd(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ (vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_nsec; \ if ((vvp)->tv_nsec > 1000000000) { \ (vvp)->tv_sec++; \ (vvp)->tv_nsec -= 1000000000; \ } \ } while (0) - -#define S_NONE 0 -#define S_SCE 1 -#define S_SCX 2 -#define S_EXIT 3 -#define S_SIG 4 -#define S_EXEC 5 -#define S_DETACHED 6 Index: stable/10 =================================================================== --- stable/10 (revision 290051) +++ stable/10 (revision 290052) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r271389,286330-286331,286358,286378,286380-286381,286383,286388,286848-286849,286857,286860,286913-286914,286937-286940,286962-286963,288405-288406,288424,288454-288456,288625-288626,288832,288834,288950,288997,289080