i386-fbsd.c revision 50477
131567Ssef/*
231899Ssef * Copryight 1997 Sean Eric Fagan
331899Ssef *
431899Ssef * Redistribution and use in source and binary forms, with or without
531899Ssef * modification, are permitted provided that the following conditions
631899Ssef * are met:
731899Ssef * 1. Redistributions of source code must retain the above copyright
831899Ssef *    notice, this list of conditions and the following disclaimer.
931899Ssef * 2. Redistributions in binary form must reproduce the above copyright
1031899Ssef *    notice, this list of conditions and the following disclaimer in the
1131899Ssef *    documentation and/or other materials provided with the distribution.
1231899Ssef * 3. All advertising materials mentioning features or use of this software
1331899Ssef *    must display the following acknowledgement:
1431899Ssef *	This product includes software developed by Sean Eric Fagan
1531899Ssef * 4. Neither the name of the author may be used to endorse or promote
1631899Ssef *    products derived from this software without specific prior written
1731899Ssef *    permission.
1831899Ssef *
1931899Ssef * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2031899Ssef * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2131899Ssef * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2231899Ssef * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2331899Ssef * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2431899Ssef * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2531899Ssef * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2631899Ssef * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2731899Ssef * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2831899Ssef * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2931899Ssef * SUCH DAMAGE.
3031899Ssef */
3131899Ssef
3232275Scharnier#ifndef lint
3332275Scharnierstatic const char rcsid[] =
3450477Speter  "$FreeBSD: head/usr.bin/truss/i386-fbsd.c 50477 1999-08-28 01:08:13Z peter $";
3532275Scharnier#endif /* not lint */
3632275Scharnier
3731899Ssef/*
3831567Ssef * FreeBSD/386-specific system call handling.  This is probably the most
3931567Ssef * complex part of the entire truss program, although I've got lots of
4031567Ssef * it handled relatively cleanly now.  The system call names are generated
4131567Ssef * automatically, thanks to /usr/src/sys/kern/syscalls.master.  The
4231567Ssef * names used for the various structures are confusing, I sadly admit.
4331567Ssef */
4431567Ssef
4532275Scharnier#include <errno.h>
4632275Scharnier#include <fcntl.h>
4732275Scharnier#include <signal.h>
4831567Ssef#include <stdio.h>
4931567Ssef#include <stdlib.h>
5031567Ssef#include <string.h>
5131567Ssef#include <unistd.h>
5231567Ssef#include <sys/ioctl.h>
5331567Ssef#include <sys/pioctl.h>
5432367Ssef#include <machine/reg.h>
5532367Ssef#include <machine/psl.h>
5631567Ssef#include <sys/syscall.h>
5731567Ssef
5831567Ssef#include "syscall.h"
5931567Ssef
6031567Ssefstatic int fd = -1;
6131567Ssefstatic int cpid = -1;
6231567Ssefextern int Procfd;
6331567Ssef
6431567Ssefextern FILE *outfile;
6531567Ssef#include "syscalls.h"
6631567Ssef
6731567Ssefstatic int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]);
6831567Ssef
6931567Ssef/*
7031567Ssef * This is what this particular file uses to keep track of a system call.
7131567Ssef * It is probably not quite sufficient -- I can probably use the same
7231567Ssef * structure for the various syscall personalities, and I also probably
7331567Ssef * need to nest system calls (for signal handlers).
7431567Ssef *
7531567Ssef * 'struct syscall' describes the system call; it may be NULL, however,
7631567Ssef * if we don't know about this particular system call yet.
7731567Ssef */
7831567Ssefstatic struct freebsd_syscall {
7931567Ssef	struct syscall *sc;
8031567Ssef	char *name;
8131567Ssef	int number;
8231567Ssef	unsigned long *args;
8331567Ssef	int nargs;	/* number of arguments -- *not* number of words! */
8431567Ssef	char **s_args;	/* the printable arguments */
8531567Ssef} fsc;
8631567Ssef
8731567Ssef/* Clear up and free parts of the fsc structure. */
8831567Ssefstatic inline void
8931567Ssefclear_fsc() {
9031567Ssef  if (fsc.args) {
9131567Ssef    free(fsc.args);
9231567Ssef  }
9331567Ssef  if (fsc.s_args) {
9431567Ssef    int i;
9531567Ssef    for (i = 0; i < fsc.nargs; i++)
9631567Ssef      if (fsc.s_args[i])
9731567Ssef	free(fsc.s_args[i]);
9831567Ssef    free(fsc.s_args);
9931567Ssef  }
10031567Ssef  memset(&fsc, 0, sizeof(fsc));
10131567Ssef}
10231567Ssef
10331567Ssef/*
10431567Ssef * Called when a process has entered a system call.  nargs is the
10531567Ssef * number of words, not number of arguments (a necessary distinction
10631567Ssef * in some cases).  Note that if the STOPEVENT() code in i386/i386/trap.c
10731567Ssef * is ever changed these functions need to keep up.
10831567Ssef */
10931567Ssef
11031567Ssefvoid
11131567Ssefi386_syscall_entry(int pid, int nargs) {
11231567Ssef  char buf[32];
11331567Ssef  struct reg regs = { 0 };
11431567Ssef  int syscall;
11531567Ssef  int i;
11631567Ssef  unsigned int parm_offset;
11731567Ssef  struct syscall *sc;
11831567Ssef
11931567Ssef  if (fd == -1 || pid != cpid) {
12031567Ssef    sprintf(buf, "/proc/%d/regs", pid);
12131567Ssef    fd = open(buf, O_RDWR);
12231567Ssef    if (fd == -1) {
12331567Ssef      fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
12431567Ssef      return;
12531567Ssef    }
12631567Ssef    cpid = pid;
12731567Ssef  }
12831567Ssef
12931567Ssef  clear_fsc();
13031567Ssef  lseek(fd, 0L, 0);
13131567Ssef  i = read(fd, &regs, sizeof(regs));
13231567Ssef  parm_offset = regs.r_esp + sizeof(int);
13331567Ssef
13431567Ssef  /*
13531567Ssef   * FreeBSD has two special kinds of system call redirctions --
13631567Ssef   * SYS_syscall, and SYS___syscall.  The former is the old syscall()
13731567Ssef   * routine, basicly; the latter is for quad-aligned arguments.
13831567Ssef   */
13931567Ssef  syscall = regs.r_eax;
14031567Ssef  switch (syscall) {
14131567Ssef  case SYS_syscall:
14231567Ssef    lseek(Procfd, parm_offset, SEEK_SET);
14331567Ssef    read(Procfd, &syscall, sizeof(int));
14431567Ssef    parm_offset += sizeof(int);
14531567Ssef    break;
14631567Ssef  case SYS___syscall:
14731567Ssef    lseek(Procfd, parm_offset, SEEK_SET);
14831567Ssef    read(Procfd, &syscall, sizeof(int));
14931567Ssef    parm_offset += sizeof(quad_t);
15031567Ssef    break;
15131567Ssef  }
15231567Ssef
15331567Ssef  fsc.number = syscall;
15431567Ssef  fsc.name =
15531567Ssef    (syscall < 0 || syscall > nsyscalls) ? NULL : syscallnames[syscall];
15631567Ssef  if (!fsc.name) {
15731567Ssef    fprintf(outfile, "-- UNKNOWN SYSCALL %d --\n", syscall);
15831567Ssef  }
15931567Ssef
16031567Ssef  if (nargs == 0)
16131567Ssef    return;
16231567Ssef
16331567Ssef  fsc.args = malloc((1+nargs) * sizeof(unsigned long));
16431567Ssef  lseek(Procfd, parm_offset, SEEK_SET);
16531567Ssef  if (read(Procfd, fsc.args, nargs * sizeof(unsigned long)) == -1)
16631567Ssef    return;
16731567Ssef
16831567Ssef  sc = get_syscall(fsc.name);
16931567Ssef  if (sc) {
17031567Ssef    fsc.nargs = sc->nargs;
17131567Ssef  } else {
17231567Ssef#if DEBUG
17331567Ssef    fprintf(outfile, "unknown syscall %s -- setting args to %d\n",
17431567Ssef	   fsc.name, nargs);
17531567Ssef#endif
17631567Ssef    fsc.nargs = nargs;
17731567Ssef  }
17831567Ssef
17931567Ssef  fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*));
18031567Ssef  memset(fsc.s_args, 0, fsc.nargs * sizeof(char*));
18131567Ssef  fsc.sc = sc;
18231567Ssef
18331567Ssef  /*
18431567Ssef   * At this point, we set up the system call arguments.
18531567Ssef   * We ignore any OUT ones, however -- those are arguments that
18631567Ssef   * are set by the system call, and so are probably meaningless
18731567Ssef   * now.  This doesn't currently support arguments that are
18831567Ssef   * passed in *and* out, however.
18931567Ssef   */
19031567Ssef
19131567Ssef  if (fsc.name) {
19231567Ssef
19331567Ssef#if DEBUG
19431567Ssef    fprintf(stderr, "syscall %s(", fsc.name);
19531567Ssef#endif
19631567Ssef    for (i = 0; i < fsc.nargs; i++) {
19731567Ssef#if DEBUG
19831567Ssef      fprintf(stderr, "0x%x%s",
19931567Ssef	     sc
20031567Ssef	     ? fsc.args[sc->args[i].offset]
20131567Ssef	     : fsc.args[i],
20231567Ssef	     i < (fsc.nargs -1) ? "," : "");
20331567Ssef#endif
20431567Ssef      if (sc && !(sc->args[i].type & OUT)) {
20531567Ssef	fsc.s_args[i] = print_arg(Procfd, &sc->args[i], fsc.args);
20631567Ssef      }
20731567Ssef    }
20831567Ssef#if DEBUG
20931567Ssef    fprintf(stderr, ")\n");
21031567Ssef#endif
21131567Ssef  }
21231567Ssef
21331567Ssef#if DEBUG
21431567Ssef  fprintf(outfile, "\n");
21531567Ssef#endif
21631567Ssef
21731567Ssef  /*
21831567Ssef   * Some system calls should be printed out before they are done --
21931567Ssef   * execve() and exit(), for example, never return.  Possibly change
22031567Ssef   * this to work for any system call that doesn't have an OUT
22131567Ssef   * parameter?
22231567Ssef   */
22331567Ssef
22431567Ssef  if (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit")) {
22531567Ssef    print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args);
22631567Ssef  }
22731567Ssef
22831567Ssef  return;
22931567Ssef}
23031567Ssef
23131567Ssef/*
23231567Ssef * And when the system call is done, we handle it here.
23331567Ssef * Currently, no attempt is made to ensure that the system calls
23431567Ssef * match -- this needs to be fixed (and is, in fact, why S_SCX includes
23531567Ssef * the sytem call number instead of, say, an error status).
23631567Ssef */
23731567Ssef
23831567Ssefvoid
23931567Ssefi386_syscall_exit(int pid, int syscall) {
24031567Ssef  char buf[32];
24131567Ssef  struct reg regs;
24231567Ssef  int retval;
24331567Ssef  int i;
24431567Ssef  int errorp;
24531567Ssef  struct syscall *sc;
24631567Ssef
24731567Ssef  if (fd == -1 || pid != cpid) {
24831567Ssef    sprintf(buf, "/proc/%d/regs", pid);
24931567Ssef    fd = open(buf, O_RDONLY);
25031567Ssef    if (fd == -1) {
25131567Ssef      fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
25231567Ssef      return;
25331567Ssef    }
25431567Ssef    cpid = pid;
25531567Ssef  }
25631567Ssef
25731567Ssef  lseek(fd, 0L, 0);
25831567Ssef  if (read(fd, &regs, sizeof(regs)) != sizeof(regs))
25931567Ssef    return;
26031567Ssef  retval = regs.r_eax;
26131567Ssef  errorp = !!(regs.r_eflags & PSL_C);
26231567Ssef
26331567Ssef  /*
26431567Ssef   * This code, while simpler than the initial versions I used, could
26531567Ssef   * stand some significant cleaning.
26631567Ssef   */
26731567Ssef
26831567Ssef  sc = fsc.sc;
26931567Ssef  if (!sc) {
27031567Ssef    for (i = 0; i < fsc.nargs; i++) {
27131567Ssef      fsc.s_args[i] = malloc(12);
27237453Sbde      sprintf(fsc.s_args[i], "0x%lx", fsc.args[i]);
27331567Ssef    }
27431567Ssef  } else {
27531567Ssef    /*
27631567Ssef     * Here, we only look for arguments that have OUT masked in --
27731567Ssef     * otherwise, they were handled in the syscall_entry function.
27831567Ssef     */
27931567Ssef    for (i = 0; i < sc->nargs; i++) {
28031567Ssef      char *temp;
28131567Ssef      if (sc->args[i].type & OUT) {
28231567Ssef	/*
28331567Ssef	 * If an error occurred, than don't bothe getting the data;
28431567Ssef	 * it may not be valid.
28531567Ssef	 */
28631567Ssef	if (errorp) {
28731567Ssef	  temp = malloc(12);
28837453Sbde	  sprintf(temp, "0x%lx", fsc.args[sc->args[i].offset]);
28931567Ssef	} else {
29031567Ssef	  temp = print_arg(Procfd, &sc->args[i], fsc.args);
29131567Ssef	}
29231567Ssef	fsc.s_args[i] = temp;
29331567Ssef      }
29431567Ssef    }
29531567Ssef  }
29631567Ssef
29731567Ssef  /*
29831567Ssef   * It would probably be a good idea to merge the error handling,
29931567Ssef   * but that complicates things considerably.
30031567Ssef   */
30131567Ssef
30231567Ssef  print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args);
30331567Ssef  if (errorp) {
30431567Ssef    fprintf(outfile, "errno %d '%s'\n", retval, strerror(retval));
30531567Ssef  } else {
30631567Ssef    fprintf(outfile, "returns %d (0x%x)\n", retval, retval);
30731567Ssef  }
30831567Ssef  clear_fsc();
30931567Ssef
31031567Ssef  return;
31131567Ssef}
312