i386-linux.c revision 101309
1/*
2 * Copryight 1997 Sean Eric Fagan
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. All advertising materials mentioning features or use of this software
13 *    must display the following acknowledgement:
14 *	This product includes software developed by Sean Eric Fagan
15 * 4. Neither the name of the author may be used to endorse or promote
16 *    products derived from this software without specific prior written
17 *    permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#ifndef lint
33static const char rcsid[] =
34  "$FreeBSD: head/usr.bin/truss/i386-linux.c 101309 2002-08-04 10:57:41Z bde $";
35#endif /* not lint */
36
37/*
38 * Linux/i386-specific system call handling.  Given how much of this code
39 * is taken from the freebsd equivalent, I can probably put even more of
40 * it in support routines that can be used by any personality support.
41 */
42
43#include <sys/types.h>
44#include <sys/ioctl.h>
45#include <sys/pioctl.h>
46#include <sys/time.h>
47
48#include <machine/reg.h>
49#include <machine/psl.h>
50
51#include <errno.h>
52#include <fcntl.h>
53#include <signal.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58
59#include "truss.h"
60#include "extern.h"
61#include "syscall.h"
62
63static int fd = -1;
64static int cpid = -1;
65extern int Procfd;
66
67#include "linux_syscalls.h"
68
69static int nsyscalls =
70	sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]);
71
72/* See the comment in i386-fbsd.c about this structure. */
73static struct linux_syscall {
74	struct syscall *sc;
75	char *name;
76	int number;
77	unsigned long args[5];
78	int nargs;	/* number of arguments -- *not* number of words! */
79	char **s_args;	/* the printable arguments */
80} lsc;
81
82static __inline void
83clear_lsc() {
84  if (lsc.s_args) {
85    int i;
86    for (i = 0; i < lsc.nargs; i++)
87      if (lsc.s_args[i])
88	free(lsc.s_args[i]);
89    free(lsc.s_args);
90  }
91  memset(&lsc, 0, sizeof(lsc));
92}
93
94void
95i386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs) {
96  char buf[32];
97  struct reg regs = { 0 };
98  int syscall;
99  int i;
100  struct syscall *sc;
101
102  if (fd == -1 || trussinfo->pid != cpid) {
103    sprintf(buf, "/proc/%d/regs", trussinfo->pid);
104    fd = open(buf, O_RDWR);
105    if (fd == -1) {
106      fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
107      return;
108    }
109    cpid = trussinfo->pid;
110  }
111
112  clear_lsc();
113  lseek(fd, 0L, 0);
114  i = read(fd, &regs, sizeof(regs));
115  syscall = regs.r_eax;
116
117  lsc.number = syscall;
118  lsc.name =
119    (syscall < 0 || syscall > nsyscalls) ? NULL : linux_syscallnames[syscall];
120  if (!lsc.name) {
121    fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d\n", syscall);
122  }
123
124  if (lsc.name && (trussinfo->flags & FOLLOWFORKS)
125   && ((!strcmp(lsc.name, "linux_fork")
126    || !strcmp(lsc.name, "linux_vfork"))))
127  {
128    trussinfo->in_fork = 1;
129  }
130
131  if (nargs == 0)
132    return;
133
134  /*
135   * Linux passes syscall arguments in registers, not
136   * on the stack.  Fortunately, we've got access to the
137   * register set.  Note that we don't bother checking the
138   * number of arguments.  And what does linux do for syscalls
139   * that have more than five arguments?
140   */
141
142  lsc.args[0] = regs.r_ebx;
143  lsc.args[1] = regs.r_ecx;
144  lsc.args[2] = regs.r_edx;
145  lsc.args[3] = regs.r_esi;
146  lsc.args[4] = regs.r_edi;
147
148  sc = get_syscall(lsc.name);
149  if (sc) {
150    lsc.nargs = sc->nargs;
151  } else {
152#ifdef DEBUG
153    fprintf(trussinfo->outfile, "unknown syscall %s -- setting args to %d\n",
154	    lsc.name, nargs);
155#endif
156    lsc.nargs = nargs;
157  }
158
159  lsc.s_args = malloc((1+lsc.nargs) * sizeof(char*));
160  memset(lsc.s_args, 0, lsc.nargs * sizeof(char*));
161  lsc.sc = sc;
162
163  if (lsc.name) {
164
165#ifdef DEBUG
166    fprintf(stderr, "syscall %s(", lsc.name);
167#endif
168    for (i = 0; i < lsc.nargs ; i++) {
169#ifdef DEBUG
170      fprintf(stderr, "0x%x%s",
171	      sc ?
172	      lsc.args[sc->args[i].offset]
173	      : lsc.args[i],
174	      i < (lsc.nargs - 1) ? "," : "");
175#endif
176      if (sc && !(sc->args[i].type & OUT)) {
177	lsc.s_args[i] = print_arg(Procfd, &sc->args[i], lsc.args);
178      }
179    }
180#ifdef DEBUG
181    fprintf(stderr, ")\n");
182#endif
183  }
184
185  if (!strcmp(lsc.name, "linux_execve") || !strcmp(lsc.name, "exit")) {
186
187    /* XXX
188     * This could be done in a more general
189     * manner but it still wouldn't be very pretty.
190     */
191    if (!strcmp(lsc.name, "linux_execve")) {
192        if ((trussinfo->flags & EXECVEARGS) == 0)
193          if (lsc.s_args[1]) {
194            free(lsc.s_args[1]);
195            lsc.s_args[1] = NULL;
196          }
197        if ((trussinfo->flags & EXECVEENVS) == 0)
198          if (lsc.s_args[2]) {
199            free(lsc.s_args[2]);
200            lsc.s_args[2] = NULL;
201          }
202    }
203
204    print_syscall(trussinfo, lsc.name, lsc.nargs, lsc.s_args);
205    fprintf(trussinfo->outfile, "\n");
206  }
207
208  return;
209}
210
211/*
212 * Linux syscalls return negative errno's, we do positive and map them
213 */
214const int bsd_to_linux_errno[] = {
215  	-0,  -1,  -2,  -3,  -4,  -5,  -6,  -7,  -8,  -9,
216 	-10, -35, -12, -13, -14, -15, -16, -17, -18, -19,
217 	-20, -21, -22, -23, -24, -25, -26, -27, -28, -29,
218 	-30, -31, -32, -33, -34, -11,-115,-114, -88, -89,
219 	-90, -91, -92, -93, -94, -95, -96, -97, -98, -99,
220	-100,-101,-102,-103,-104,-105,-106,-107,-108,-109,
221	-110,-111, -40, -36,-112,-113, -39, -11, -87,-122,
222	-116, -66,  -6,  -6,  -6,  -6,  -6, -37, -38,  -9,
223  	-6,
224};
225
226int
227i386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall) {
228  char buf[32];
229  struct reg regs;
230  int retval;
231  int i;
232  int errorp;
233  struct syscall *sc;
234
235  if (fd == -1 || trussinfo->pid != cpid) {
236    sprintf(buf, "/proc/%d/regs", trussinfo->pid);
237    fd = open(buf, O_RDONLY);
238    if (fd == -1) {
239      fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
240      return;
241    }
242    cpid = trussinfo->pid;
243  }
244
245  lseek(fd, 0L, 0);
246  if (read(fd, &regs, sizeof(regs)) != sizeof(regs)) {
247    fprintf(trussinfo->outfile, "\n");
248    return;
249  }
250  retval = regs.r_eax;
251  errorp = !!(regs.r_eflags & PSL_C);
252
253  sc = lsc.sc;
254  if (!sc) {
255    for (i = 0; i < lsc.nargs; i++) {
256      lsc.s_args[i] = malloc(12);
257      sprintf(lsc.s_args[i], "0x%lx", lsc.args[i]);
258    }
259  } else {
260    for (i = 0; i < sc->nargs; i++) {
261      char *temp;
262      if (sc->args[i].type & OUT) {
263	if (errorp) {
264	  temp = malloc(12);
265	  sprintf(temp, "0x%lx", lsc.args[sc->args[i].offset]);
266	} else {
267	  temp = print_arg(Procfd, &sc->args[i], lsc.args);
268	}
269	lsc.s_args[i] = temp;
270      }
271    }
272  }
273  if (errorp) {
274    for (i = 0; i < sizeof(bsd_to_linux_errno) / sizeof(int); i++)
275      if (retval == bsd_to_linux_errno[i])
276      break;
277  }
278  print_syscall_ret(trussinfo, lsc.name, lsc.nargs, lsc.s_args, errorp,
279                    errorp ? i : retval);
280  clear_lsc();
281
282  return (retval);
283}
284