i386-fbsd.c revision 31567
11558Srgrimes/*
244317Sjkh * FreeBSD/386-specific system call handling.  This is probably the most
31558Srgrimes * complex part of the entire truss program, although I've got lots of
412481Speter * it handled relatively cleanly now.  The system call names are generated
51558Srgrimes * automatically, thanks to /usr/src/sys/kern/syscalls.master.  The
641061Sbde * names used for the various structures are confusing, I sadly admit.
741061Sbde */
839271Sphk/*
939255Sgibbs * $Id$
1038653Sgpalmer */
1138653Sgpalmer
1243859Sobrien#include <stdio.h>
1338653Sgpalmer#include <stdlib.h>
1438653Sgpalmer#include <string.h>
1538653Sgpalmer#include <errno.h>
1638653Sgpalmer#include <err.h>
1738653Sgpalmer#include <signal.h>
1838653Sgpalmer#include <fcntl.h>
1938653Sgpalmer#include <unistd.h>
2038653Sgpalmer#include <sys/ioctl.h>
2138653Sgpalmer#include <sys/pioctl.h>
2238653Sgpalmer#include <machine/reg.h>
2338653Sgpalmer#include <machine/psl.h>
2438653Sgpalmer#include <sys/syscall.h>
2538653Sgpalmer
2638653Sgpalmer#include "syscall.h"
2738653Sgpalmer
2838843Sjbstatic int fd = -1;
2938653Sgpalmerstatic int cpid = -1;
3038653Sgpalmerextern int Procfd;
3138653Sgpalmer
3238653Sgpalmerextern FILE *outfile;
3338653Sgpalmer#include "syscalls.h"
3438653Sgpalmer
3538653Sgpalmerstatic int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]);
3638653Sgpalmer
3743557Ssemenu/*
3838653Sgpalmer * This is what this particular file uses to keep track of a system call.
3938653Sgpalmer * It is probably not quite sufficient -- I can probably use the same
4038653Sgpalmer * structure for the various syscall personalities, and I also probably
4138653Sgpalmer * need to nest system calls (for signal handlers).
4238653Sgpalmer *
4338653Sgpalmer * 'struct syscall' describes the system call; it may be NULL, however,
4438653Sgpalmer * if we don't know about this particular system call yet.
4538653Sgpalmer */
4638653Sgpalmerstatic struct freebsd_syscall {
4738653Sgpalmer	struct syscall *sc;
4838653Sgpalmer	char *name;
4938653Sgpalmer	int number;
5038653Sgpalmer	unsigned long *args;
5138653Sgpalmer	int nargs;	/* number of arguments -- *not* number of words! */
5238653Sgpalmer	char **s_args;	/* the printable arguments */
5338653Sgpalmer} fsc;
5438653Sgpalmer
5538653Sgpalmer/* Clear up and free parts of the fsc structure. */
5638653Sgpalmerstatic inline void
5738653Sgpalmerclear_fsc() {
5838653Sgpalmer  if (fsc.args) {
5941061Sbde    free(fsc.args);
6038653Sgpalmer  }
6138653Sgpalmer  if (fsc.s_args) {
6238653Sgpalmer    int i;
6342117Ssos    for (i = 0; i < fsc.nargs; i++)
6442117Ssos      if (fsc.s_args[i])
6510855Sjoerg	free(fsc.s_args[i]);
6644317Sjkh    free(fsc.s_args);
6744317Sjkh  }
6844317Sjkh  memset(&fsc, 0, sizeof(fsc));
6944317Sjkh}
7038852Sjb
7138852Sjb/*
7238458Sjb * Called when a process has entered a system call.  nargs is the
7338458Sjb * number of words, not number of arguments (a necessary distinction
741558Srgrimes * in some cases).  Note that if the STOPEVENT() code in i386/i386/trap.c
75 * is ever changed these functions need to keep up.
76 */
77
78void
79i386_syscall_entry(int pid, int nargs) {
80  char buf[32];
81  struct reg regs = { 0 };
82  int syscall;
83  int i;
84  int memfd;
85  unsigned int parm_offset;
86  struct syscall *sc;
87
88  if (fd == -1 || pid != cpid) {
89    sprintf(buf, "/proc/%d/regs", pid);
90    fd = open(buf, O_RDWR);
91    if (fd == -1) {
92      fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
93      return;
94    }
95    cpid = pid;
96  }
97
98  clear_fsc();
99  lseek(fd, 0L, 0);
100  i = read(fd, &regs, sizeof(regs));
101  parm_offset = regs.r_esp + sizeof(int);
102
103  /*
104   * FreeBSD has two special kinds of system call redirctions --
105   * SYS_syscall, and SYS___syscall.  The former is the old syscall()
106   * routine, basicly; the latter is for quad-aligned arguments.
107   */
108  syscall = regs.r_eax;
109  switch (syscall) {
110  case SYS_syscall:
111    lseek(Procfd, parm_offset, SEEK_SET);
112    read(Procfd, &syscall, sizeof(int));
113    parm_offset += sizeof(int);
114    break;
115  case SYS___syscall:
116    lseek(Procfd, parm_offset, SEEK_SET);
117    read(Procfd, &syscall, sizeof(int));
118    parm_offset += sizeof(quad_t);
119    break;
120  }
121
122  fsc.number = syscall;
123  fsc.name =
124    (syscall < 0 || syscall > nsyscalls) ? NULL : syscallnames[syscall];
125  if (!fsc.name) {
126    fprintf(outfile, "-- UNKNOWN SYSCALL %d --\n", syscall);
127  }
128
129  if (nargs == 0)
130    return;
131
132  fsc.args = malloc((1+nargs) * sizeof(unsigned long));
133  lseek(Procfd, parm_offset, SEEK_SET);
134  if (read(Procfd, fsc.args, nargs * sizeof(unsigned long)) == -1)
135    return;
136
137  sc = get_syscall(fsc.name);
138  if (sc) {
139    fsc.nargs = sc->nargs;
140  } else {
141#if DEBUG
142    fprintf(outfile, "unknown syscall %s -- setting args to %d\n",
143	   fsc.name, nargs);
144#endif
145    fsc.nargs = nargs;
146  }
147
148  fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*));
149  memset(fsc.s_args, 0, fsc.nargs * sizeof(char*));
150  fsc.sc = sc;
151
152  /*
153   * At this point, we set up the system call arguments.
154   * We ignore any OUT ones, however -- those are arguments that
155   * are set by the system call, and so are probably meaningless
156   * now.  This doesn't currently support arguments that are
157   * passed in *and* out, however.
158   */
159
160  if (fsc.name) {
161    char *tmp;
162
163#if DEBUG
164    fprintf(stderr, "syscall %s(", fsc.name);
165#endif
166    for (i = 0; i < fsc.nargs; i++) {
167#if DEBUG
168      fprintf(stderr, "0x%x%s",
169	     sc
170	     ? fsc.args[sc->args[i].offset]
171	     : fsc.args[i],
172	     i < (fsc.nargs -1) ? "," : "");
173#endif
174      if (sc && !(sc->args[i].type & OUT)) {
175	fsc.s_args[i] = print_arg(Procfd, &sc->args[i], fsc.args);
176      }
177    }
178#if DEBUG
179    fprintf(stderr, ")\n");
180#endif
181  }
182
183#if DEBUG
184  fprintf(outfile, "\n");
185#endif
186
187  /*
188   * Some system calls should be printed out before they are done --
189   * execve() and exit(), for example, never return.  Possibly change
190   * this to work for any system call that doesn't have an OUT
191   * parameter?
192   */
193
194  if (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit")) {
195    print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args);
196  }
197
198  return;
199}
200
201/*
202 * And when the system call is done, we handle it here.
203 * Currently, no attempt is made to ensure that the system calls
204 * match -- this needs to be fixed (and is, in fact, why S_SCX includes
205 * the sytem call number instead of, say, an error status).
206 */
207
208void
209i386_syscall_exit(int pid, int syscall) {
210  char buf[32];
211  struct reg regs;
212  int retval;
213  int i;
214  int errorp;
215  struct syscall *sc;
216
217  if (fd == -1 || pid != cpid) {
218    sprintf(buf, "/proc/%d/regs", pid);
219    fd = open(buf, O_RDONLY);
220    if (fd == -1) {
221      fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
222      return;
223    }
224    cpid = pid;
225  }
226
227  lseek(fd, 0L, 0);
228  if (read(fd, &regs, sizeof(regs)) != sizeof(regs))
229    return;
230  retval = regs.r_eax;
231  errorp = !!(regs.r_eflags & PSL_C);
232
233  /*
234   * This code, while simpler than the initial versions I used, could
235   * stand some significant cleaning.
236   */
237
238  sc = fsc.sc;
239  if (!sc) {
240    for (i = 0; i < fsc.nargs; i++) {
241      fsc.s_args[i] = malloc(12);
242      sprintf(fsc.s_args[i], "0x%x", fsc.args[i]);
243    }
244  } else {
245    /*
246     * Here, we only look for arguments that have OUT masked in --
247     * otherwise, they were handled in the syscall_entry function.
248     */
249    for (i = 0; i < sc->nargs; i++) {
250      char *temp;
251      if (sc->args[i].type & OUT) {
252	/*
253	 * If an error occurred, than don't bothe getting the data;
254	 * it may not be valid.
255	 */
256	if (errorp) {
257	  temp = malloc(12);
258	  sprintf(temp, "0x%x", fsc.args[sc->args[i].offset]);
259	} else {
260	  temp = print_arg(Procfd, &sc->args[i], fsc.args);
261	}
262	fsc.s_args[i] = temp;
263      }
264    }
265  }
266
267  /*
268   * It would probably be a good idea to merge the error handling,
269   * but that complicates things considerably.
270   */
271
272  print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args);
273  if (errorp) {
274    fprintf(outfile, "errno %d '%s'\n", retval, strerror(retval));
275  } else {
276    fprintf(outfile, "returns %d (0x%x)\n", retval, retval);
277  }
278  clear_fsc();
279
280  return;
281}
282