i386-linux.c revision 31567
1/*
2 * Linux/i386-specific system call handling.  Given how much of this code
3 * is taken from the freebsd equivalent, I can probably put even more of
4 * it in support routines that can be used by any personality support.
5 */
6/*
7 * $Id$
8 */
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <errno.h>
13#include <err.h>
14#include <signal.h>
15#include <fcntl.h>
16#include <unistd.h>
17#include <sys/ioctl.h>
18#include <sys/pioctl.h>
19#include <machine/reg.h>
20#include <machine/psl.h>
21
22#include "syscall.h"
23
24static int fd = -1;
25static int cpid = -1;
26extern int Procfd;
27
28extern FILE *outfile;
29#include "linux_syscalls.h"
30
31static int nsyscalls =
32	sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]);
33
34/* See the comment in i386-fbsd.c about this structure. */
35static struct linux_syscall {
36	struct syscall *sc;
37	char *name;
38	int number;
39	unsigned long args[5];
40	int nargs;	/* number of arguments -- *not* number of words! */
41	char **s_args;	/* the printable arguments */
42} lsc;
43
44static inline void
45clear_lsc() {
46  if (lsc.s_args) {
47    int i;
48    for (i = 0; i < lsc.nargs; i++)
49      if (lsc.s_args[i])
50	free(lsc.s_args[i]);
51    free(lsc.s_args);
52  }
53  memset(&lsc, 0, sizeof(lsc));
54}
55
56void
57i386_linux_syscall_entry(int pid, int nargs) {
58  char buf[32];
59  struct reg regs = { 0 };
60  int syscall;
61  int i;
62  int memfd;
63  struct syscall *sc;
64
65  if (fd == -1 || pid != cpid) {
66    sprintf(buf, "/proc/%d/regs", pid);
67    fd = open(buf, O_RDWR);
68    if (fd == -1) {
69      fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
70      return;
71    }
72    cpid = pid;
73  }
74
75  clear_lsc();
76  lseek(fd, 0L, 0);
77  i = read(fd, &regs, sizeof(regs));
78  syscall = regs.r_eax;
79
80  lsc.number = syscall;
81  lsc.name =
82    (syscall < 0 || syscall > nsyscalls) ? NULL : linux_syscallnames[syscall];
83  if (!lsc.name) {
84    fprintf (outfile, "-- UNKNOWN SYSCALL %d\n", syscall);
85  }
86
87  if (nargs == 0)
88    return;
89
90  /*
91   * Linux passes syscall arguments in registers, not
92   * on the stack.  Fortunately, we've got access to the
93   * register set.  Note that we don't bother checking the
94   * number of arguments.  And what does linux do for syscalls
95   * that have more than five arguments?
96   */
97
98  lsc.args[0] = regs.r_ebx;
99  lsc.args[1] = regs.r_ecx;
100  lsc.args[2] = regs.r_edx;
101  lsc.args[3] = regs.r_esi;
102  lsc.args[4] = regs.r_edi;
103
104  sc = get_syscall(lsc.name);
105  if (sc) {
106    lsc.nargs = sc->nargs;
107  } else {
108#ifdef DEBUG
109    fprintf(outfile, "unknown syscall %s -- setting args to %d\n",
110	    lsc.name, nargs);
111#endif
112    lsc.nargs = nargs;
113  }
114
115  lsc.s_args = malloc((1+lsc.nargs) * sizeof(char*));
116  memset(lsc.s_args, 0, lsc.nargs * sizeof(char*));
117  lsc.sc = sc;
118
119  if (lsc.name) {
120    char *tmp;
121
122#ifdef DEBUG
123    fprintf(stderr, "syscall %s(", lsc.name);
124#endif
125    for (i = 0; i < lsc.nargs ; i++) {
126#ifdef DEBUG
127      fprintf(stderr, "0x%x%s",
128	      sc ?
129	      lsc.args[sc->args[i].offset]
130	      : lsc.args[i],
131	      i < (lsc.nargs - 1) ? "," : "");
132#endif
133      if (sc && !(sc->args[i].type & OUT)) {
134	lsc.s_args[i] = print_arg(Procfd, &sc->args[i], lsc.args);
135      }
136    }
137#ifdef DEBUG
138    fprintf(stderr, ")\n");
139#endif
140  }
141
142  if (!strcmp(lsc.name, "linux_execve") || !strcmp(lsc.name, "exit")) {
143    print_syscall(outfile, lsc.name, lsc.nargs, lsc.s_args);
144  }
145
146  return;
147}
148
149/*
150 * Linux syscalls return negative errno's, we do positive and map them
151 */
152int bsd_to_linux_errno[] = {
153  	-0,  -1,  -2,  -3,  -4,  -5,  -6,  -7,  -8,  -9,
154 	-10, -35, -12, -13, -14, -15, -16, -17, -18, -19,
155 	-20, -21, -22, -23, -24, -25, -26, -27, -28, -29,
156 	-30, -31, -32, -33, -34, -11,-115,-114, -88, -89,
157 	-90, -91, -92, -93, -94, -95, -96, -97, -98, -99,
158	-100,-101,-102,-103,-104,-105,-106,-107,-108,-109,
159	-110,-111, -40, -36,-112,-113, -39, -11, -87,-122,
160	-116, -66,  -6,  -6,  -6,  -6,  -6, -37, -38,  -9,
161  	-6,
162};
163
164void
165i386_linux_syscall_exit(int pid, int syscall) {
166  char buf[32];
167  struct reg regs;
168  int retval;
169  int i;
170  int errorp;
171  struct syscall *sc;
172
173  if (fd == -1 || pid != cpid) {
174    sprintf(buf, "/proc/%d/regs", pid);
175    fd = open(buf, O_RDONLY);
176    if (fd == -1) {
177      fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
178      return;
179    }
180    cpid = pid;
181  }
182
183  lseek(fd, 0L, 0);
184  if (read(fd, &regs, sizeof(regs)) != sizeof(regs))
185    return;
186
187  retval = regs.r_eax;
188  errorp = !!(regs.r_eflags & PSL_C);
189
190  sc = lsc.sc;
191  if (!sc) {
192    for (i = 0; i < lsc.nargs; i++) {
193      lsc.s_args[i] = malloc(12);
194      sprintf(lsc.s_args[i], "0x%x", lsc.args[i]);
195    }
196  } else {
197    for (i = 0; i < sc->nargs; i++) {
198      char *temp;
199      if (sc->args[i].type & OUT) {
200	if (errorp) {
201	  temp = malloc(12);
202	  sprintf(temp, "0x%x", lsc.args[sc->args[i].offset]);
203	} else {
204	  temp = print_arg(Procfd, &sc->args[i], lsc.args);
205	}
206	lsc.s_args[i] = temp;
207      }
208    }
209  }
210  print_syscall(outfile, lsc.name, lsc.nargs, lsc.s_args);
211  if (errorp) {
212    for (i = 0; i < sizeof(bsd_to_linux_errno) / sizeof(int); i++)
213      if (retval == bsd_to_linux_errno[i])
214      break;
215    fprintf(outfile, "errno %d '%s'\n", retval, strerror(i));
216  } else {
217    fprintf(outfile, "returns %d (0x%x)\n", retval, retval);
218  }
219  clear_lsc();
220  return;
221}
222