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