Several changes to truss.

- Refactor the interface between the ABI-independent code and the
  ABI-specific backends.  The backends now provide smaller hooks to
  fetch system call arguments and return values.  The rest of the
  system call entry and exit handling that was previously duplicated
  among all the backends has been moved to one place.
- Merge the loop when waiting for an event with the loop for handling stops.
  This also means not emulating a procfs-like interface on top of ptrace().
  Instead, use a single event loop that fetches process events via waitid().
  Among other things this allows us to report the full 32-bit exit value.
- Use PT_FOLLOW_FORK to follow new child processes instead of forking a new
  truss process for each new child.  This allows one truss process to monitor
  a tree of processes and truss -c should now display one total for the
  entire tree instead of separate summaries per process.
- Use the recently added fields to ptrace_lwpinfo to determine the current
  system call number and argument count.  The latter is especially useful
  and fixes a regression since the conversion from procfs.  truss now
  generally prints the correct number of arguments for most system calls
  rather than printing extra arguments for any call not listed in the
  table in syscalls.c.
- Actually check the new ABI when processes call exec.  The comments claimed
  that this happened but it was not being done (perhaps this was another
  regression in the conversion to ptrace()).  If the new ABI after exec
  is not supported, truss detaches from the process.  If truss does not
  support the ABI for a newly executed process the process is killed
  before it returns from exec.
- Along with the refactor, teach the various ABI-specific backends to
  fetch both return values, not just the first.  Use this to properly
  report the full 64-bit return value from lseek().  In addition, the
  handler for "pipe" now pulls the pair of descriptors out of the
  return values (which is the true kernel system call interface) but
  displays them as an argument (which matches the interface exported by
  libc).
- Each ABI handler adds entries to a linker set rather than requiring
  a statically defined table of handlers in main.c.
- The arm and mips system call fetching code was changed to follow the
  same pattern as amd64 (and the in-kernel handler) of fetching register
  arguments first and then reading any remaining arguments from the
  stack.  This should fix indirect system call arguments on at least
  arm.
- The mipsn32 and n64 ABIs will now look for arguments in A4 through A7.
- Use register %ebp for the 6th system call argument for Linux/i386 ABIs
  to match the in-kernel argument fetch code.
- For powerpc binaries on a powerpc64 system, fetch the extra arguments
  on the stack as 32-bit values that are then copied into the 64-bit
  argument array instead of reading the 32-bit values directly into the
  64-bit array.

Reviewed by:	kib (earlier version)
Tested on:	amd64 (FreeBSD/amd64 & i386), i386, arm (earlier version)
Tested on:	powerpc64 (FreeBSD/powerpc64 & powerpc)
MFC after:	1 month
Differential Revision:	https://reviews.freebsd.org/D3575
This commit is contained in:
John Baldwin 2015-09-30 19:13:32 +00:00
parent 14044e16bc
commit 2b75c8ad3d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=288424
16 changed files with 1139 additions and 2816 deletions

View File

@ -29,290 +29,103 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#include "syscalls.h"
static int nsyscalls = nitems(syscallnames);
/*
* 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)
{
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)
static int
amd64_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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)&regs, 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
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)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
retval = regs.r_rax;
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 (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);
retval[0] = regs.r_rax;
retval[1] = regs.r_rdx;
*errorp = !!(regs.r_rflags & PSL_C);
return (0);
}
static struct procabi amd64_fbsd = {
"FreeBSD ELF64",
syscallnames,
nitems(syscallnames),
amd64_fetch_args,
amd64_fetch_retval
};
PROCABI(amd64_fbsd);

View File

@ -29,290 +29,109 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#include "freebsd32_syscalls.h"
static int nsyscalls = nitems(freebsd32_syscallnames);
/*
* 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)
{
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)
static int
amd64_fbsd32_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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)&regs, 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
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)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
retval = regs.r_rax;
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 (fsc->name != NULL && (strcmp(fsc->name, "freebsd32_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);
retval[0] = regs.r_rax & 0xffffffff;
retval[1] = regs.r_rdx & 0xffffffff;
*errorp = !!(regs.r_rflags & PSL_C);
return (0);
}
static struct procabi amd64_fbsd32 = {
"FreeBSD ELF32",
freebsd32_syscallnames,
nitems(freebsd32_syscallnames),
amd64_fbsd32_fetch_args,
amd64_fbsd32_fetch_retval
};
PROCABI(amd64_fbsd32);
static struct procabi amd64_fbsd32_aout = {
"FreeBSD a.out",
freebsd32_syscallnames,
nitems(freebsd32_syscallnames),
amd64_fbsd32_fetch_args,
amd64_fbsd32_fetch_retval
};
PROCABI(amd64_fbsd32_aout);

View File

@ -29,123 +29,36 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/ptrace.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#include "linux32_syscalls.h"
static int nsyscalls = nitems(linux32_syscallnames);
/*
* 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)
{
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)
static int
amd64_linux32_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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)&regs, 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
@ -153,60 +66,22 @@ amd64_linux32_syscall_entry(struct trussinfo *trussinfo, int nargs)
* 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
trussinfo->curthread->fsc = fsc;
return (0);
}
/*
@ -224,83 +99,43 @@ static const int bsd_to_linux_errno[] = {
-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;
if (trussinfo->curthread->fsc == NULL)
return (-1);
size_t i;
tid = trussinfo->curthread->tid;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 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);
}
}
/* XXX: How to handle unknown errors? */
}
/*
* 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 < nitems(bsd_to_linux_errno); i++) {
if (retval == bsd_to_linux_errno[i])
break;
}
}
if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 ||
strcmp(fsc->name, "exit") == 0))
trussinfo->curthread->in_syscall = 1;
print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
errorp ? i : retval, fsc->sc);
free_fsc(fsc);
return (retval);
return (0);
}
static struct procabi amd64_linux32 = {
"Linux ELF32",
linux32_syscallnames,
nitems(linux32_syscallnames),
amd64_linux32_fetch_args,
amd64_linux32_fetch_retval
};
PROCABI(amd64_linux32);

View File

@ -29,17 +29,11 @@
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
/* FreeBSD/arm-specific system call handling. */
#include <sys/ptrace.h>
#include <sys/syscall.h>
@ -47,95 +41,35 @@ __FBSDID("$FreeBSD$");
#include <machine/armreg.h>
#include <machine/ucontext.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <err.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#include "syscalls.h"
static int nsyscalls = nitems(syscallnames);
/*
* 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)
{
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)
static int
arm_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return;
return (-1);
}
ap = &regs.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];
@ -143,207 +77,62 @@ arm_syscall_entry(struct trussinfo *trussinfo, int nargs)
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_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
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)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
retval = regs.r[0];
errorp = !!(regs.r_cpsr & PSR_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 (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);
/* 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);
}
static struct procabi arm_fbsd = {
"FreeBSD ELF32",
syscallnames,
nitems(syscallnames),
arm_fetch_args,
arm_fetch_retval
};
PROCABI(arm_fbsd);

View File

@ -31,42 +31,9 @@
* $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 __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

View File

@ -29,284 +29,103 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#include "syscalls.h"
static int nsyscalls = nitems(syscallnames);
/*
* 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)
{
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)
static int
i386_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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)&regs, 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;
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
trussinfo->curthread->fsc = fsc;
}
/*
* 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)
{
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;
return (0);
}
static int
i386_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp)
{
struct reg regs;
lwpid_t tid;
tid = trussinfo->curthread->tid;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
retval = regs.r_eax;
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 (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);
retval[0] = regs.r_eax;
retval[1] = regs.r_edx;
*errorp = !!(regs.r_eflags & PSL_C);
return (0);
}
static struct procabi i386_fbsd = {
"FreeBSD ELF32",
syscallnames,
nitems(syscallnames),
i386_fetch_args,
i386_fetch_retval
};
PROCABI(i386_fbsd);
static struct procabi i386_fbsd_aout = {
"FreeBSD a.out",
syscallnames,
nitems(syscallnames),
i386_fetch_args,
i386_fetch_retval
};
PROCABI(i386_fbsd_aout);

View File

@ -29,123 +29,36 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/ptrace.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#include "linux_syscalls.h"
static int nsyscalls = nitems(linux_syscallnames);
/*
* 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)
{
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)
static int
i386_linux_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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)&regs, 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
@ -153,60 +66,22 @@ i386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs)
* 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
trussinfo->curthread->fsc = fsc;
return (0);
}
/*
@ -224,82 +99,42 @@ static const int bsd_to_linux_errno[] = {
-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;
if (trussinfo->curthread->fsc == NULL)
return (-1);
size_t i;
tid = trussinfo->curthread->tid;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 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);
}
}
/* XXX: How to handle unknown errors? */
}
/*
* 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 < nitems(bsd_to_linux_errno); i++) {
if (retval == bsd_to_linux_errno[i])
break;
}
}
if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 ||
strcmp(fsc->name, "exit") == 0))
trussinfo->curthread->in_syscall = 1;
print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
errorp ? i : retval, fsc->sc);
free_fsc(fsc);
return (retval);
return (0);
}
static struct procabi i386_linux = {
"Linux ELF32",
linux_syscallnames,
nitems(linux_syscallnames),
i386_linux_fetch_args,
i386_linux_fetch_retval
};
PROCABI(i386_linux);

View File

@ -38,21 +38,12 @@ __FBSDID("$FreeBSD$");
* do a lot of the work :).
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
@ -60,8 +51,6 @@ __FBSDID("$FreeBSD$");
#include "extern.h"
#include "syscall.h"
#define MAXARGS 6
static void
usage(void)
{
@ -71,82 +60,6 @@ usage(void)
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 __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)
{
@ -162,37 +75,32 @@ strsig(int sig)
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);
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 */
@ -228,8 +136,8 @@ main(int ac, char **av)
}
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 */
@ -247,10 +155,10 @@ main(int ac, char **av)
* 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);
@ -261,119 +169,37 @@ main(int ac, char **av)
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, "%jd.%09ld ",
(intmax_t)timediff.tv_sec,
timediff.tv_nsec);
}
if (trussinfo->flags & RELATIVETIMESTAMPS) {
timespecsubt(&trussinfo->curthread->after,
&trussinfo->curthread->before, &timediff);
fprintf(trussinfo->outfile, "%jd.%09ld ",
(intmax_t)timediff.tv_sec,
timediff.tv_nsec);
}
signame = strsig(trussinfo->pr_data);
fprintf(trussinfo->outfile,
"SIGNAL %u (%s)\n", trussinfo->pr_data,
signame == NULL ? "?" : 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, "%jd.%09ld ",
(intmax_t)timediff.tv_sec,
timediff.tv_nsec);
}
if (trussinfo->flags & RELATIVETIMESTAMPS) {
timespecsubt(&trussinfo->curthread->after,
&trussinfo->curthread->before, &timediff);
fprintf(trussinfo->outfile, "%jd.%09ld ",
(intmax_t)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);

View File

@ -29,318 +29,113 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/frame.h>
#include <machine/reg.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#include "syscalls.h"
static int nsyscalls = nitems(syscallnames);
/*
* 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)
{
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)
static int
mips_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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)&regs, 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];
}
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;
/*
* 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
iorequest.piod_op = PIOD_READ_D;
reg = A2;
#endif
break;
}
#if defined(__mips_n32) || defined(__mips_n64)
#define MAXREG A7
#else
#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;
}
if (indir) {
memmove(&fsc->args[0], &fsc->args[1],
(nargs - 1) * sizeof(fsc->args[0]));
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%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
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)&regs, 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];
/*
* 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 (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);
/* 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);
}
static struct procabi mips_fbsd = {
#ifdef __mips_n64
"FreeBSD ELF64",
#else
"FreeBSD ELF32",
#endif
syscallnames,
nitems(syscallnames),
mips_fetch_args,
mips_fetch_retval
};
PROCABI(mips_fbsd);

View File

@ -25,41 +25,20 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/frame.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#ifdef __powerpc64__ /* 32-bit compatibility */
#include "freebsd32_syscalls.h"
@ -68,264 +47,104 @@ static const char rcsid[] =
#include "syscalls.h"
#endif
static int nsyscalls = nitems(syscallnames);
/*
* 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)
{
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)
static int
powerpc_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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;
/* Account for a 64-bit argument with corresponding alignment. */
nargs += 2;
u_int i, reg;
tid = trussinfo->curthread->tid;
cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 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 = &regs.fixreg[3];
if (syscall_num == SYS_syscall) {
args = &regs.fixreg[4];
regargs -= 1;
syscall_num = regs.fixreg[3];
} else if (syscall_num == SYS___syscall) {
args = &regs.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);
#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");
return (-1);
#ifdef __powerpc64__
for (j = 0; j < narg - i; j++)
cs->args[i + j] = args32[j];
#endif
}
#if DEBUG
fprintf(trussinfo->outfile, "\n");
#endif
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)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "\n");
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
retval = regs.fixreg[3];
errorp = !!(regs.cr & 0x10000000);
/*
* 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 {
/*
* 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);
/* 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);
}
static struct procabi powerpc_fbsd = {
"FreeBSD ELF32",
syscallnames,
nitems(syscallnames),
powerpc_fetch_args,
powerpc_fetch_retval
};
PROCABI(powerpc_fbsd);

View File

@ -25,287 +25,94 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/frame.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#include "syscalls.h"
static int nsyscalls = nitems(syscallnames);
/*
* 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)
{
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)
static int
powerpc64_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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)&regs, 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 = &regs.fixreg[3];
if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) {
args = &regs.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
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)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "\n");
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
retval = regs.fixreg[3];
errorp = !!(regs.cr & 0x10000000);
/*
* 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 (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);
retval[0] = regs.fixreg[3];
retval[1] = regs.fixreg[4];
*errorp = !!(regs.cr & 0x10000000);
return (0);
}
static struct procabi powerpc64_fbsd = {
"FreeBSD ELF64",
syscallnames,
nitems(syscallnames),
powerpc64_fetch_args,
powerpc64_fetch_retval
};
PROCABI(powerpc64_fbsd);

View File

@ -37,37 +37,39 @@ __FBSDID("$FreeBSD$");
* I'm afraid.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <machine/reg.h>
#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;
@ -84,17 +86,14 @@ setup_and_wait(char *command[])
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;
@ -109,7 +108,7 @@ start_tracing(pid_t pid)
if (waitpid(pid, NULL, 0) < 0)
err(1, "Unexpect stop in waitpid");
return (0);
new_proc(info, pid);
}
/*
@ -118,7 +117,6 @@ start_tracing(pid_t pid)
* applies if truss was told to monitor an already-existing
* process.
*/
void
restore_proc(int signo __unused)
{
@ -126,116 +124,485 @@ 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);
}
/*
* Change curthread member based on lwpid.
* If it is a new thread, create a threadinfo structure
* 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 (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.
*/
static void
find_exit_thread(struct trussinfo *info, pid_t pid)
{
struct procinfo *np;
struct threadinfo *nt;
np = find_proc(info, pid);
assert(np != NULL);
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 (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);
if (sc) {
t->cs.nargs = sc->nargs;
assert(sc->nargs <= nitems(t->cs.s_args));
} else {
#if DEBUG
fprintf(stderr, "unknown syscall %s -- setting "
"args to %d\n", t->cs.name, t->cs.nargs);
#endif
t->cs.nargs = narg;
}
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 && !(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
}
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;
clock_gettime(CLOCK_REALTIME, &t->after);
p = t->proc;
if (p->abi->fetch_retval(info, retval, &errorp) < 0) {
free_syscall(t);
return;
}
sc = t->cs.sc;
if (sc == NULL) {
for (i = 0; i < t->cs.nargs; i++)
asprintf(&t->cs.s_args[i], "0x%lx", t->cs.args[i]);
} else {
/*
* 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);
}
t->cs.s_args[i] = temp;
}
}
}
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, "<new process>\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
waitevent(struct trussinfo *info)
eventloop(struct trussinfo *info)
{
struct ptrace_lwpinfo lwpinfo;
static int pending_signal = 0;
int waitval;
struct ptrace_lwpinfo pl;
siginfo_t si;
int pending_signal;
ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal);
pending_signal = 0;
while (!LIST_EMPTY(&info->proclist)) {
if (detaching) {
detach_all_procs(info);
return;
}
detach:
if (detaching) {
waitval = detach_proc(info->pid);
info->pr_why = S_DETACHED;
info->pr_data = WEXITSTATUS(waitval);
return;
}
if (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) {
if (errno == EINTR)
continue;
err(1, "Unexpected error from waitid");
}
if (waitpid(info->pid, &waitval, 0) == -1) {
if (errno == EINTR)
goto detach;
err(1, "Unexpected stop in waitpid");
}
assert(si.si_signo == SIGCHLD);
if (WIFCONTINUED(waitval)) {
info->pr_why = S_NONE;
return;
}
if (WIFEXITED(waitval)) {
info->pr_why = S_EXIT;
info->pr_data = WEXITSTATUS(waitval);
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;
} else {
errx(1,
"pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX",
lwpinfo.pl_flags);
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);
}
default:
info->pr_why = S_SIG;
info->pr_data = WSTOPSIG(waitval);
pending_signal = info->pr_data;
find_thread(info, si.si_pid, pl.pl_lwpid);
if (si.si_status == SIGTRAP) {
if (pl.pl_flags & PL_FLAG_SCE)
enter_syscall(info, &pl);
else if (pl.pl_flags & PL_FLAG_SCX)
exit_syscall(info, &pl);
else
errx(1,
"pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX",
pl.pl_flags);
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;
}
}
if (WIFSIGNALED(waitval)) {
info->pr_why = S_EXIT;
info->pr_data = 0;
return;
}
}

View File

@ -29,22 +29,11 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__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 <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
@ -52,289 +41,85 @@ static const char rcsid[] =
#include <machine/reg.h>
#include <machine/tstate.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "truss.h"
#include "syscall.h"
#include "extern.h"
#include "syscalls.h"
static int nsyscalls = nitems(syscallnames);
/*
* 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)
{
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)
static int
sparc64_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
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)&regs, 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];
}
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);
reg = 0;
switch (regs.r_global[1]) {
case SYS_syscall:
case SYS___syscall:
reg = 1;
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
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)&regs, 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);
/*
* 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 (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);
retval[0] = regs.r_out[0];
retval[1] = regs.r_out[1];
*errorp = !!(regs.r_tstate & TSTATE_XCC_C);
return (0);
}
static struct procabi sparc64_fbsd = {
"FreeBSD ELF64",
syscallnames,
nitems(syscallnames),
sparc64_fetch_args,
sparc64_fetch_retval
};
PROCABI(sparc64_fbsd);

View File

@ -42,7 +42,7 @@ enum Argtype { None = 1, Hex, Octal, Int, LongHex, Name, Ptr, Stat, Ioctl, Quad,
Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2,
Pathconf, Rforkflags, ExitStatus, Waitoptions, Idtype, Procctl,
LinuxSockArgs, Umtxop, Atfd, Atflags, Timespec2, Accessmode, Long,
Sysarch, ExecArgs, ExecEnv };
Sysarch, ExecArgs, ExecEnv, PipeFds };
#define ARG_MASK 0xff
#define OUT 0x100
@ -55,8 +55,8 @@ struct syscall_args {
struct syscall {
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 */
@ -65,7 +65,7 @@ struct syscall {
};
struct syscall *get_syscall(const char*);
char *print_arg(struct syscall_args *, unsigned long*, long, struct trussinfo *);
char *print_arg(struct syscall_args *, unsigned long*, long *, struct trussinfo *);
/*
* Linux Socket defines
@ -109,5 +109,5 @@ struct linux_socketcall_args {
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);

View File

@ -29,10 +29,8 @@
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* This file has routines used to print out system calls and their
@ -40,23 +38,20 @@ static const char rcsid[] =
*/
#include <sys/types.h>
#include <sys/event.h>
#include <sys/ioccom.h>
#include <sys/mman.h>
#include <sys/procctl.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/umtx.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <machine/sysarch.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioccom.h>
#include <machine/atomic.h>
#include <errno.h>
#include <sys/umtx.h>
#include <sys/event.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <machine/sysarch.h>
#include <ctype.h>
#include <err.h>
@ -95,26 +90,16 @@ static const char rcsid[] =
static struct syscall syscalls[] = {
{ .name = "fcntl", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 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 = "issetugid", .ret_type = 1, .nargs = 0 },
{ .name = "readlink", .ret_type = 1, .nargs = 3,
.args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } },
{ .name = "readlinkat", .ret_type = 1, .nargs = 4,
@ -125,7 +110,7 @@ static struct syscall syscalls[] = {
{ 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,
{ .name = "mmap", .ret_type = 1, .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,
@ -145,49 +130,49 @@ static struct syscall syscalls[] = {
.args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } },
{ .name = "close", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
{ .name = "link", .ret_type = 0, .nargs = 2,
{ .name = "link", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Name, 1 } } },
{ .name = "linkat", .ret_type = 0, .nargs = 5,
{ .name = "linkat", .ret_type = 1, .nargs = 5,
.args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 },
{ Atflags, 4 } } },
{ .name = "unlink", .ret_type = 0, .nargs = 1,
{ .name = "unlink", .ret_type = 1, .nargs = 1,
.args = { { Name, 0 } } },
{ .name = "unlinkat", .ret_type = 0, .nargs = 3,
{ .name = "unlinkat", .ret_type = 1, .nargs = 3,
.args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } },
{ .name = "chdir", .ret_type = 0, .nargs = 1,
{ .name = "chdir", .ret_type = 1, .nargs = 1,
.args = { { Name, 0 } } },
{ .name = "chroot", .ret_type = 0, .nargs = 1,
{ .name = "chroot", .ret_type = 1, .nargs = 1,
.args = { { Name, 0 } } },
{ .name = "mkfifo", .ret_type = 0, .nargs = 2,
{ .name = "mkfifo", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Octal, 1 } } },
{ .name = "mkfifoat", .ret_type = 0, .nargs = 3,
{ .name = "mkfifoat", .ret_type = 1, .nargs = 3,
.args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
{ .name = "mknod", .ret_type = 0, .nargs = 3,
{ .name = "mknod", .ret_type = 1, .nargs = 3,
.args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } },
{ .name = "mknodat", .ret_type = 0, .nargs = 4,
{ .name = "mknodat", .ret_type = 1, .nargs = 4,
.args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } },
{ .name = "chmod", .ret_type = 0, .nargs = 2,
{ .name = "chmod", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Octal, 1 } } },
{ .name = "fchmod", .ret_type = 0, .nargs = 2,
{ .name = "fchmod", .ret_type = 1, .nargs = 2,
.args = { { Int, 0 }, { Octal, 1 } } },
{ .name = "lchmod", .ret_type = 0, .nargs = 2,
{ .name = "lchmod", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Octal, 1 } } },
{ .name = "fchmodat", .ret_type = 0, .nargs = 4,
{ .name = "fchmodat", .ret_type = 1, .nargs = 4,
.args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } },
{ .name = "chown", .ret_type = 0, .nargs = 3,
{ .name = "chown", .ret_type = 1, .nargs = 3,
.args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
{ .name = "fchown", .ret_type = 0, .nargs = 3,
{ .name = "fchown", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } },
{ .name = "lchown", .ret_type = 0, .nargs = 3,
{ .name = "lchown", .ret_type = 1, .nargs = 3,
.args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
{ .name = "fchownat", .ret_type = 0, .nargs = 5,
{ .name = "fchownat", .ret_type = 1, .nargs = 5,
.args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 },
{ Atflags, 4 } } },
{ .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,
{ .name = "mount", .ret_type = 1, .nargs = 4,
.args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } },
{ .name = "umount", .ret_type = 0, .nargs = 2,
{ .name = "umount", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Int, 2 } } },
{ .name = "fstat", .ret_type = 1, .nargs = 2,
.args = { { Int, 0 }, { Stat | OUT, 1 } } },
@ -250,19 +235,19 @@ static struct syscall syscalls[] = {
{ .name = "linux_execve", .ret_type = 1, .nargs = 3,
.args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
{ ExecEnv | IN, 2 } } },
{ .name = "kldload", .ret_type = 0, .nargs = 1,
{ .name = "kldload", .ret_type = 1, .nargs = 1,
.args = { { Name | IN, 0 } } },
{ .name = "kldunload", .ret_type = 0, .nargs = 1,
{ .name = "kldunload", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
{ .name = "kldfind", .ret_type = 0, .nargs = 1,
{ .name = "kldfind", .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 = "kldfirstmod", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
{ .name = "nanosleep", .ret_type = 0, .nargs = 1,
{ .name = "nanosleep", .ret_type = 1, .nargs = 1,
.args = { { Timespec, 0 } } },
{ .name = "select", .ret_type = 1, .nargs = 5,
.args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 },
@ -279,18 +264,18 @@ static struct syscall syscalls[] = {
.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,
{ .name = "kevent", .ret_type = 1, .nargs = 6,
.args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 },
{ Int, 4 }, { Timespec, 5 } } },
{ .name = "sigpending", .ret_type = 0, .nargs = 1,
{ .name = "sigpending", .ret_type = 1, .nargs = 1,
.args = { { Sigset | OUT, 0 } } },
{ .name = "sigprocmask", .ret_type = 0, .nargs = 3,
{ .name = "sigprocmask", .ret_type = 1, .nargs = 3,
.args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } },
{ .name = "sigqueue", .ret_type = 0, .nargs = 3,
{ .name = "sigqueue", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } },
{ .name = "sigreturn", .ret_type = 0, .nargs = 1,
{ .name = "sigreturn", .ret_type = 1, .nargs = 1,
.args = { { Ptr, 0 } } },
{ .name = "sigsuspend", .ret_type = 0, .nargs = 1,
{ .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 } } },
@ -332,7 +317,7 @@ static struct syscall syscalls[] = {
{ .name = "pathconf", .ret_type = 1, .nargs = 2,
.args = { { Name | IN, 0 }, { Pathconf, 1 } } },
{ .name = "pipe", .ret_type = 1, .nargs = 1,
.args = { { Ptr, 0 } } },
.args = { { PipeFds | OUT, 0 } } },
{ .name = "pipe2", .ret_type = 1, .nargs = 2,
.args = { { Ptr, 0 }, { Open, 1 } } },
{ .name = "truncate", .ret_type = 1, .nargs = 3,
@ -368,9 +353,9 @@ static struct syscall syscalls[] = {
{ .name = "_umtx_op", .ret_type = 1, .nargs = 5,
.args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 },
{ Ptr, 4 } } },
{ .name = "thr_kill", .ret_type = 0, .nargs = 2,
{ .name = "thr_kill", .ret_type = 1, .nargs = 2,
.args = { { Long, 0 }, { Signal, 1 } } },
{ .name = "thr_self", .ret_type = 0, .nargs = 1,
{ .name = "thr_self", .ret_type = 1, .nargs = 1,
.args = { { Ptr, 0 } } },
{ .name = 0 },
};
@ -836,7 +821,7 @@ print_kevent(FILE *fp, struct kevent *ke, int input)
* 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;
@ -845,7 +830,7 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
pid_t pid;
fp = open_memstream(&tmp, &tmplen);
pid = trussinfo->pid;
pid = trussinfo->curthread->proc->pid;
switch (sc->type & ARG_MASK) {
case Hex:
fprintf(fp, "0x%x", (int)args[sc->offset]);
@ -884,7 +869,7 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
int truncated = 0;
if (sc->type & OUT)
len = retval;
len = retval[0];
else
len = args[sc->offset + 1];
@ -924,7 +909,7 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
} u;
char *string;
size_t len;
int first, i;
u_int first, i;
/*
* Only parse argv[] and environment arrays from exec calls
@ -987,7 +972,13 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
case Quad: {
unsigned long long ll;
ll = *(unsigned long long *)(args + sc->offset);
#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
fprintf(fp, "0x%llx", ll);
break;
}
@ -998,9 +989,9 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
case Readlinkres: {
char *tmp2;
if (retval == -1)
if (retval[0] == -1)
break;
tmp2 = get_string(pid, (void*)args[sc->offset], retval);
tmp2 = get_string(pid, (void*)args[sc->offset], retval[0]);
fprintf(fp, "\"%s\"", tmp2);
free(tmp2);
break;
@ -1397,8 +1388,8 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
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) {
bytes = sizeof(struct kevent) * numevents;
@ -1517,6 +1508,21 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
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);
}
@ -1538,7 +1544,8 @@ print_syscall(struct trussinfo *trussinfo, const char *name, int nargs,
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)) {
@ -1577,7 +1584,7 @@ print_syscall(struct trussinfo *trussinfo, const char *name, int nargs,
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;
@ -1597,17 +1604,24 @@ print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs,
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 != NULL && 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

View File

@ -25,6 +25,7 @@
* $FreeBSD$
*/
#include <sys/linker_set.h>
#include <sys/queue.h>
#define FOLLOWFORKS 0x00000001
@ -35,23 +36,63 @@
#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. syscalls.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;
@ -59,7 +100,7 @@ struct trussinfo
struct threadinfo *curthread;
SLIST_HEAD(, threadinfo) threadlist;
LIST_HEAD(, procinfo) proclist;
};
#define timespecsubt(tvp, uvp, vvp) \
@ -81,11 +122,3 @@ struct trussinfo
(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