i386-linux.c revision 101423
1181834Sroberto/*
2280849Scy * Copryight 1997 Sean Eric Fagan
3280849Scy *
4181834Sroberto * Redistribution and use in source and binary forms, with or without
5181834Sroberto * modification, are permitted provided that the following conditions
6181834Sroberto * are met:
7181834Sroberto * 1. Redistributions of source code must retain the above copyright
8181834Sroberto *    notice, this list of conditions and the following disclaimer.
9280849Scy * 2. Redistributions in binary form must reproduce the above copyright
10280849Scy *    notice, this list of conditions and the following disclaimer in the
11280849Scy *    documentation and/or other materials provided with the distribution.
12181834Sroberto * 3. All advertising materials mentioning features or use of this software
13181834Sroberto *    must display the following acknowledgement:
14280849Scy *	This product includes software developed by Sean Eric Fagan
15280849Scy * 4. Neither the name of the author may be used to endorse or promote
16285169Scy *    products derived from this software without specific prior written
17181834Sroberto *    permission.
18280849Scy *
19280849Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20280849Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21181834Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22280849Scy * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23280849Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24181834Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25280849Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26280849Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27181834Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28280849Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29181834Sroberto * SUCH DAMAGE.
30280849Scy */
31280849Scy
32280849Scy#ifndef lint
33181834Srobertostatic const char rcsid[] =
34181834Sroberto  "$FreeBSD: head/usr.bin/truss/i386-linux.c 101423 2002-08-06 12:46:14Z mdodd $";
35280849Scy#endif /* not lint */
36280849Scy
37280849Scy/*
38280849Scy * Linux/i386-specific system call handling.  Given how much of this code
39181834Sroberto * is taken from the freebsd equivalent, I can probably put even more of
40280849Scy * it in support routines that can be used by any personality support.
41280849Scy */
42280849Scy
43280849Scy#include <sys/types.h>
44181834Sroberto#include <sys/ioctl.h>
45280849Scy#include <sys/pioctl.h>
46280849Scy
47280849Scy#include <machine/reg.h>
48280849Scy#include <machine/psl.h>
49280849Scy
50181834Sroberto#include <errno.h>
51280849Scy#include <fcntl.h>
52280849Scy#include <signal.h>
53280849Scy#include <stdio.h>
54280849Scy#include <stdlib.h>
55181834Sroberto#include <string.h>
56280849Scy#include <time.h>
57280849Scy#include <unistd.h>
58280849Scy
59280849Scy#include "truss.h"
60280849Scy#include "syscall.h"
61181834Sroberto
62181834Srobertostatic int fd = -1;
63280849Scystatic int cpid = -1;
64181834Srobertoextern int Procfd;
65181834Sroberto
66181834Sroberto#include "linux_syscalls.h"
67280849Scy
68280849Scystatic int nsyscalls =
69181834Sroberto	sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]);
70181834Sroberto
71181834Sroberto/*
72181834Sroberto * This is what this particular file uses to keep track of a system call.
73280849Scy * It is probably not quite sufficient -- I can probably use the same
74181834Sroberto * structure for the various syscall personalities, and I also probably
75181834Sroberto * need to nest system calls (for signal handlers).
76280849Scy *
77181834Sroberto * 'struct syscall' describes the system call; it may be NULL, however,
78280849Scy * if we don't know about this particular system call yet.
79181834Sroberto */
80280849Scystatic struct linux_syscall {
81280849Scy	struct syscall *sc;
82181834Sroberto	char *name;
83181834Sroberto	int number;
84181834Sroberto	unsigned long args[5];
85181834Sroberto	int nargs;	/* number of arguments -- *not* number of words! */
86280849Scy	char **s_args;	/* the printable arguments */
87181834Sroberto} fsc;
88181834Sroberto
89280849Scy/* Clear up and free parts of the fsc structure. */
90181834Srobertostatic __inline void
91181834Srobertoclear_fsc() {
92181834Sroberto  if (fsc.s_args) {
93280849Scy    int i;
94280849Scy    for (i = 0; i < fsc.nargs; i++)
95181834Sroberto      if (fsc.s_args[i])
96181834Sroberto	free(fsc.s_args[i]);
97181834Sroberto    free(fsc.s_args);
98181834Sroberto  }
99280849Scy  memset(&fsc, 0, sizeof(fsc));
100181834Sroberto}
101280849Scy
102280849Scy/*
103181834Sroberto * Called when a process has entered a system call.  nargs is the
104181834Sroberto * number of words, not number of arguments (a necessary distinction
105181834Sroberto * in some cases).  Note that if the STOPEVENT() code in i386/i386/trap.c
106181834Sroberto * is ever changed these functions need to keep up.
107181834Sroberto */
108280849Scy
109181834Srobertovoid
110181834Srobertoi386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs) {
111181834Sroberto  char buf[32];
112181834Sroberto  struct reg regs = { 0 };
113181834Sroberto  int syscall;
114280849Scy  int i;
115280849Scy  struct syscall *sc;
116280849Scy
117280849Scy  if (fd == -1 || trussinfo->pid != cpid) {
118181834Sroberto    sprintf(buf, "/proc/%d/regs", trussinfo->pid);
119280849Scy    fd = open(buf, O_RDWR);
120181834Sroberto    if (fd == -1) {
121181834Sroberto      fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
122181834Sroberto      return;
123181834Sroberto    }
124181834Sroberto    cpid = trussinfo->pid;
125280849Scy  }
126280849Scy
127181834Sroberto  clear_fsc();
128181834Sroberto  lseek(fd, 0L, 0);
129181834Sroberto  i = read(fd, &regs, sizeof(regs));
130181834Sroberto  syscall = regs.r_eax;
131181834Sroberto
132181834Sroberto  fsc.number = syscall;
133181834Sroberto  fsc.name =
134280849Scy    (syscall < 0 || syscall > nsyscalls) ? NULL : linux_syscallnames[syscall];
135181834Sroberto  if (!fsc.name) {
136181834Sroberto    fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall);
137181834Sroberto  }
138280849Scy
139280849Scy  if (fsc.name && (trussinfo->flags & FOLLOWFORKS)
140181834Sroberto   && ((!strcmp(fsc.name, "linux_fork")
141181834Sroberto    || !strcmp(fsc.name, "linux_vfork"))))
142181834Sroberto  {
143181834Sroberto    trussinfo->in_fork = 1;
144181834Sroberto  }
145181834Sroberto
146280849Scy  if (nargs == 0)
147181834Sroberto    return;
148181834Sroberto
149181834Sroberto  /*
150181834Sroberto   * Linux passes syscall arguments in registers, not
151181834Sroberto   * on the stack.  Fortunately, we've got access to the
152181834Sroberto   * register set.  Note that we don't bother checking the
153181834Sroberto   * number of arguments.  And what does linux do for syscalls
154181834Sroberto   * that have more than five arguments?
155280849Scy   */
156280849Scy
157280849Scy  fsc.args[0] = regs.r_ebx;
158181834Sroberto  fsc.args[1] = regs.r_ecx;
159181834Sroberto  fsc.args[2] = regs.r_edx;
160181834Sroberto  fsc.args[3] = regs.r_esi;
161181834Sroberto  fsc.args[4] = regs.r_edi;
162181834Sroberto
163181834Sroberto  sc = get_syscall(fsc.name);
164181834Sroberto  if (sc) {
165181834Sroberto    fsc.nargs = sc->nargs;
166181834Sroberto  } else {
167280849Scy#if DEBUG
168181834Sroberto    fprintf(trussinfo->outfile, "unknown syscall %s -- setting args to %d\n",
169181834Sroberto	   fsc.name, nargs);
170280849Scy#endif
171181834Sroberto    fsc.nargs = nargs;
172181834Sroberto  }
173181834Sroberto
174181834Sroberto  fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*));
175280849Scy  memset(fsc.s_args, 0, fsc.nargs * sizeof(char*));
176280849Scy  fsc.sc = sc;
177280849Scy
178181834Sroberto  /*
179181834Sroberto   * At this point, we set up the system call arguments.
180181834Sroberto   * We ignore any OUT ones, however -- those are arguments that
181181834Sroberto   * are set by the system call, and so are probably meaningless
182280849Scy   * now.  This doesn't currently support arguments that are
183181834Sroberto   * passed in *and* out, however.
184280849Scy   */
185280849Scy
186181834Sroberto  if (fsc.name) {
187181834Sroberto
188280849Scy#if DEBUG
189280849Scy    fprintf(stderr, "syscall %s(", fsc.name);
190181834Sroberto#endif
191181834Sroberto    for (i = 0; i < fsc.nargs; i++) {
192280849Scy#if DEBUG
193280849Scy      fprintf(stderr, "0x%x%s",
194181834Sroberto	      sc
195181834Sroberto	      ? fsc.args[sc->args[i].offset]
196181834Sroberto	      : fsc.args[i],
197181834Sroberto	      i < (fsc.nargs - 1) ? "," : "");
198181834Sroberto#endif
199280849Scy      if (sc && !(sc->args[i].type & OUT)) {
200280849Scy	fsc.s_args[i] = print_arg(Procfd, &sc->args[i], fsc.args);
201280849Scy      }
202181834Sroberto    }
203181834Sroberto#if DEBUG
204181834Sroberto    fprintf(stderr, ")\n");
205280849Scy#endif
206280849Scy  }
207181834Sroberto
208181834Sroberto#if DEBUG
209181834Sroberto  fprintf(trussinfo->outfile, "\n");
210181834Sroberto#endif
211280849Scy
212181834Sroberto  /*
213181834Sroberto   * Some system calls should be printed out before they are done --
214181834Sroberto   * execve() and exit(), for example, never return.  Possibly change
215181834Sroberto   * this to work for any system call that doesn't have an OUT
216280849Scy   * parameter?
217280849Scy   */
218280849Scy
219181834Sroberto  if (!strcmp(fsc.name, "linux_execve") || !strcmp(fsc.name, "exit")) {
220280849Scy
221280849Scy    /* XXX
222181834Sroberto     * This could be done in a more general
223280849Scy     * manner but it still wouldn't be very pretty.
224280849Scy     */
225280849Scy    if (!strcmp(fsc.name, "linux_execve")) {
226280849Scy        if ((trussinfo->flags & EXECVEARGS) == 0)
227280849Scy          if (fsc.s_args[1]) {
228181834Sroberto            free(fsc.s_args[1]);
229280849Scy            fsc.s_args[1] = NULL;
230280849Scy          }
231280849Scy        if ((trussinfo->flags & EXECVEENVS) == 0)
232280849Scy          if (fsc.s_args[2]) {
233181834Sroberto            free(fsc.s_args[2]);
234181834Sroberto            fsc.s_args[2] = NULL;
235280849Scy          }
236181834Sroberto    }
237181834Sroberto
238280849Scy    print_syscall(trussinfo, fsc.name, fsc.nargs, fsc.s_args);
239280849Scy    fprintf(trussinfo->outfile, "\n");
240280849Scy  }
241280849Scy
242280849Scy  return;
243181834Sroberto}
244280849Scy
245280849Scy/*
246181834Sroberto * Linux syscalls return negative errno's, we do positive and map them
247181834Sroberto */
248280849Scyconst int bsd_to_linux_errno[] = {
249181834Sroberto  	-0,  -1,  -2,  -3,  -4,  -5,  -6,  -7,  -8,  -9,
250280849Scy 	-10, -35, -12, -13, -14, -15, -16, -17, -18, -19,
251181834Sroberto 	-20, -21, -22, -23, -24, -25, -26, -27, -28, -29,
252280849Scy 	-30, -31, -32, -33, -34, -11,-115,-114, -88, -89,
253181834Sroberto 	-90, -91, -92, -93, -94, -95, -96, -97, -98, -99,
254280849Scy	-100,-101,-102,-103,-104,-105,-106,-107,-108,-109,
255280849Scy	-110,-111, -40, -36,-112,-113, -39, -11, -87,-122,
256181834Sroberto	-116, -66,  -6,  -6,  -6,  -6,  -6, -37, -38,  -9,
257181834Sroberto  	-6,
258181834Sroberto};
259181834Sroberto
260181834Srobertoint
261280849Scyi386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall) {
262181834Sroberto  char buf[32];
263181834Sroberto  struct reg regs;
264280849Scy  int retval;
265280849Scy  int i;
266181834Sroberto  int errorp;
267280849Scy  struct syscall *sc;
268181834Sroberto
269181834Sroberto  if (fd == -1 || trussinfo->pid != cpid) {
270280849Scy    sprintf(buf, "/proc/%d/regs", trussinfo->pid);
271181834Sroberto    fd = open(buf, O_RDONLY);
272280849Scy    if (fd == -1) {
273181834Sroberto      fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
274280849Scy      return;
275280849Scy    }
276181834Sroberto    cpid = trussinfo->pid;
277280849Scy  }
278181834Sroberto
279181834Sroberto  lseek(fd, 0L, 0);
280181834Sroberto  if (read(fd, &regs, sizeof(regs)) != sizeof(regs)) {
281181834Sroberto    fprintf(trussinfo->outfile, "\n");
282181834Sroberto    return;
283181834Sroberto  }
284181834Sroberto  retval = regs.r_eax;
285181834Sroberto  errorp = !!(regs.r_eflags & PSL_C);
286181834Sroberto
287181834Sroberto  /*
288181834Sroberto   * This code, while simpler than the initial versions I used, could
289181834Sroberto   * stand some significant cleaning.
290181834Sroberto   */
291181834Sroberto
292181834Sroberto  sc = fsc.sc;
293181834Sroberto  if (!sc) {
294181834Sroberto    for (i = 0; i < fsc.nargs; i++) {
295181834Sroberto      fsc.s_args[i] = malloc(12);
296181834Sroberto      sprintf(fsc.s_args[i], "0x%lx", fsc.args[i]);
297181834Sroberto    }
298181834Sroberto  } else {
299181834Sroberto    /*
300181834Sroberto     * Here, we only look for arguments that have OUT masked in --
301181834Sroberto     * otherwise, they were handled in the syscall_entry function.
302285169Scy     */
303285169Scy    for (i = 0; i < sc->nargs; i++) {
304285169Scy      char *temp;
305181834Sroberto      if (sc->args[i].type & OUT) {
306181834Sroberto	/*
307181834Sroberto	 * If an error occurred, than don't bothe getting the data;
308181834Sroberto	 * it may not be valid.
309181834Sroberto	 */
310181834Sroberto	if (errorp) {
311181834Sroberto	  temp = malloc(12);
312181834Sroberto	  sprintf(temp, "0x%lx", fsc.args[sc->args[i].offset]);
313181834Sroberto	} else {
314181834Sroberto	  temp = print_arg(Procfd, &sc->args[i], fsc.args);
315181834Sroberto	}
316181834Sroberto	fsc.s_args[i] = temp;
317181834Sroberto      }
318181834Sroberto    }
319181834Sroberto  }
320181834Sroberto
321181834Sroberto  /*
322181834Sroberto   * It would probably be a good idea to merge the error handling,
323181834Sroberto   * but that complicates things considerably.
324181834Sroberto   */
325181834Sroberto  if (errorp) {
326181834Sroberto    for (i = 0; i < sizeof(bsd_to_linux_errno) / sizeof(int); i++)
327181834Sroberto      if (retval == bsd_to_linux_errno[i])
328181834Sroberto      break;
329181834Sroberto  }
330181834Sroberto  print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp,
331181834Sroberto                    errorp ? i : retval);
332181834Sroberto  clear_fsc();
333280849Scy
334181834Sroberto  return (retval);
335280849Scy}
336280849Scy