i386-linux.c revision 225736
1218887Sdim/*
2218887Sdim * Copyright 1997 Sean Eric Fagan
3218887Sdim *
4218887Sdim * Redistribution and use in source and binary forms, with or without
5218887Sdim * modification, are permitted provided that the following conditions
6218887Sdim * are met:
7218887Sdim * 1. Redistributions of source code must retain the above copyright
8218887Sdim *    notice, this list of conditions and the following disclaimer.
9218887Sdim * 2. Redistributions in binary form must reproduce the above copyright
10218887Sdim *    notice, this list of conditions and the following disclaimer in the
11218887Sdim *    documentation and/or other materials provided with the distribution.
12218887Sdim * 3. All advertising materials mentioning features or use of this software
13218887Sdim *    must display the following acknowledgement:
14249423Sdim *	This product includes software developed by Sean Eric Fagan
15243830Sdim * 4. Neither the name of the author may be used to endorse or promote
16218887Sdim *    products derived from this software without specific prior written
17243830Sdim *    permission.
18218887Sdim *
19249423Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20249423Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21218887Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22218887Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23249423Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24249423Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25218887Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26218887Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27218887Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28218887Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29218887Sdim * SUCH DAMAGE.
30218887Sdim */
31218887Sdim
32226633Sdim#ifndef lint
33218887Sdimstatic const char rcsid[] =
34218887Sdim  "$FreeBSD: stable/9/usr.bin/truss/i386-linux.c 204977 2010-03-10 20:31:30Z imp $";
35234353Sdim#endif /* not lint */
36218887Sdim
37249423Sdim/*
38249423Sdim * Linux/i386-specific system call handling.  Given how much of this code
39249423Sdim * is taken from the freebsd equivalent, I can probably put even more of
40239462Sdim * it in support routines that can be used by any personality support.
41218887Sdim */
42234353Sdim
43218887Sdim#include <sys/types.h>
44234353Sdim#include <sys/ptrace.h>
45239462Sdim
46218887Sdim#include <machine/reg.h>
47226633Sdim#include <machine/psl.h>
48218887Sdim
49218887Sdim#include <errno.h>
50218887Sdim#include <fcntl.h>
51239462Sdim#include <signal.h>
52218887Sdim#include <stdio.h>
53234353Sdim#include <stdlib.h>
54234353Sdim#include <string.h>
55234353Sdim#include <time.h>
56218887Sdim#include <unistd.h>
57218887Sdim
58218887Sdim#include "truss.h"
59249423Sdim#include "syscall.h"
60249423Sdim#include "extern.h"
61218887Sdim
62239462Sdimstatic int cpid = -1;
63249423Sdim
64249423Sdim#include "linux_syscalls.h"
65234353Sdim
66218887Sdimstatic int nsyscalls =
67249423Sdim	sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]);
68249423Sdim
69239462Sdim/*
70239462Sdim * This is what this particular file uses to keep track of a system call.
71249423Sdim * It is probably not quite sufficient -- I can probably use the same
72249423Sdim * structure for the various syscall personalities, and I also probably
73218887Sdim * need to nest system calls (for signal handlers).
74218887Sdim *
75249423Sdim * 'struct syscall' describes the system call; it may be NULL, however,
76249423Sdim * if we don't know about this particular system call yet.
77239462Sdim */
78239462Sdimstatic struct linux_syscall {
79249423Sdim	struct syscall *sc;
80249423Sdim	const char *name;
81234353Sdim	int number;
82234353Sdim	unsigned long args[5];
83226633Sdim	int nargs;	/* number of arguments -- *not* number of words! */
84218887Sdim	char **s_args;	/* the printable arguments */
85218887Sdim} fsc;
86226633Sdim
87218887Sdim/* Clear up and free parts of the fsc structure. */
88218887Sdimstatic __inline void
89218887Sdimclear_fsc(void) {
90218887Sdim  if (fsc.s_args) {
91218887Sdim    int i;
92218887Sdim    for (i = 0; i < fsc.nargs; i++)
93218887Sdim      if (fsc.s_args[i])
94218887Sdim	free(fsc.s_args[i]);
95226633Sdim    free(fsc.s_args);
96218887Sdim  }
97218887Sdim  memset(&fsc, 0, sizeof(fsc));
98218887Sdim}
99218887Sdim
100218887Sdim/*
101226633Sdim * Called when a process has entered a system call.  nargs is the
102218887Sdim * number of words, not number of arguments (a necessary distinction
103218887Sdim * in some cases).  Note that if the STOPEVENT() code in i386/i386/trap.c
104218887Sdim * is ever changed these functions need to keep up.
105218887Sdim */
106226633Sdim
107218887Sdimvoid
108218887Sdimi386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs) {
109218887Sdim  struct reg regs;
110218887Sdim  int syscall_num;
111226633Sdim  int i;
112218887Sdim  struct syscall *sc;
113218887Sdim
114218887Sdim  cpid = trussinfo->curthread->tid;
115218887Sdim
116218887Sdim  clear_fsc();
117218887Sdim
118218887Sdim  if (ptrace(PT_GETREGS, cpid, (caddr_t)&regs, 0) < 0)
119226633Sdim  {
120218887Sdim    fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
121226633Sdim    return;
122218887Sdim  }
123218887Sdim  syscall_num = regs.r_eax;
124218887Sdim
125218887Sdim  fsc.number = syscall_num;
126218887Sdim  fsc.name =
127226633Sdim    (syscall_num < 0 || syscall_num >= nsyscalls) ? NULL : linux_syscallnames[syscall_num];
128218887Sdim  if (!fsc.name) {
129218887Sdim    fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall_num);
130218887Sdim  }
131218887Sdim
132218887Sdim  if (fsc.name && (trussinfo->flags & FOLLOWFORKS)
133218887Sdim   && ((!strcmp(fsc.name, "linux_fork")
134226633Sdim    || !strcmp(fsc.name, "linux_vfork"))))
135218887Sdim  {
136218887Sdim    trussinfo->curthread->in_fork = 1;
137218887Sdim  }
138218887Sdim
139218887Sdim  if (nargs == 0)
140218887Sdim    return;
141218887Sdim
142218887Sdim  /*
143218887Sdim   * Linux passes syscall arguments in registers, not
144234353Sdim   * on the stack.  Fortunately, we've got access to the
145218887Sdim   * register set.  Note that we don't bother checking the
146234353Sdim   * number of arguments.  And what does linux do for syscalls
147218887Sdim   * that have more than five arguments?
148218887Sdim   */
149218887Sdim
150218887Sdim  fsc.args[0] = regs.r_ebx;
151218887Sdim  fsc.args[1] = regs.r_ecx;
152218887Sdim  fsc.args[2] = regs.r_edx;
153218887Sdim  fsc.args[3] = regs.r_esi;
154218887Sdim  fsc.args[4] = regs.r_edi;
155218887Sdim
156218887Sdim  sc = get_syscall(fsc.name);
157218887Sdim  if (sc) {
158218887Sdim    fsc.nargs = sc->nargs;
159218887Sdim  } else {
160218887Sdim#if DEBUG
161226633Sdim    fprintf(trussinfo->outfile, "unknown syscall %s -- setting args to %d\n",
162218887Sdim	   fsc.name, nargs);
163218887Sdim#endif
164218887Sdim    fsc.nargs = nargs;
165218887Sdim  }
166218887Sdim
167218887Sdim  fsc.s_args = calloc(1, (1+fsc.nargs) * sizeof(char*));
168218887Sdim  fsc.sc = sc;
169218887Sdim
170218887Sdim  /*
171218887Sdim   * At this point, we set up the system call arguments.
172218887Sdim   * We ignore any OUT ones, however -- those are arguments that
173218887Sdim   * are set by the system call, and so are probably meaningless
174218887Sdim   * now.  This doesn't currently support arguments that are
175218887Sdim   * passed in *and* out, however.
176218887Sdim   */
177218887Sdim
178218887Sdim  if (fsc.name) {
179218887Sdim
180218887Sdim#if DEBUG
181218887Sdim    fprintf(stderr, "syscall %s(", fsc.name);
182239462Sdim#endif
183239462Sdim    for (i = 0; i < fsc.nargs; i++) {
184239462Sdim#if DEBUG
185239462Sdim      fprintf(stderr, "0x%x%s",
186218887Sdim	      sc
187239462Sdim	      ? fsc.args[sc->args[i].offset]
188239462Sdim	      : fsc.args[i],
189239462Sdim	      i < (fsc.nargs - 1) ? "," : "");
190218887Sdim#endif
191239462Sdim      if (sc && !(sc->args[i].type & OUT)) {
192239462Sdim	fsc.s_args[i] = print_arg(&sc->args[i], fsc.args, 0, trussinfo);
193239462Sdim      }
194218887Sdim    }
195218887Sdim#if DEBUG
196218887Sdim    fprintf(stderr, ")\n");
197218887Sdim#endif
198218887Sdim  }
199218887Sdim
200218887Sdim#if DEBUG
201218887Sdim  fprintf(trussinfo->outfile, "\n");
202218887Sdim#endif
203218887Sdim
204218887Sdim  if (fsc.name != NULL &&
205218887Sdim      (!strcmp(fsc.name, "linux_execve") || !strcmp(fsc.name, "exit"))) {
206218887Sdim
207218887Sdim    /* XXX
208218887Sdim     * This could be done in a more general
209218887Sdim     * manner but it still wouldn't be very pretty.
210218887Sdim     */
211218887Sdim    if (!strcmp(fsc.name, "linux_execve")) {
212226633Sdim        if ((trussinfo->flags & EXECVEARGS) == 0)
213218887Sdim          if (fsc.s_args[1]) {
214218887Sdim            free(fsc.s_args[1]);
215218887Sdim            fsc.s_args[1] = NULL;
216234353Sdim          }
217263508Sdim        if ((trussinfo->flags & EXECVEENVS) == 0)
218263508Sdim          if (fsc.s_args[2]) {
219218887Sdim            free(fsc.s_args[2]);
220218887Sdim            fsc.s_args[2] = NULL;
221218887Sdim          }
222218887Sdim    }
223218887Sdim  }
224218887Sdim
225263508Sdim  return;
226263508Sdim}
227263508Sdim
228263508Sdim/*
229218887Sdim * Linux syscalls return negative errno's, we do positive and map them
230218887Sdim */
231218887Sdimconst int bsd_to_linux_errno[] = {
232218887Sdim  	-0,  -1,  -2,  -3,  -4,  -5,  -6,  -7,  -8,  -9,
233218887Sdim 	-10, -35, -12, -13, -14, -15, -16, -17, -18, -19,
234218887Sdim 	-20, -21, -22, -23, -24, -25, -26, -27, -28, -29,
235218887Sdim 	-30, -31, -32, -33, -34, -11,-115,-114, -88, -89,
236239462Sdim 	-90, -91, -92, -93, -94, -95, -96, -97, -98, -99,
237218887Sdim	-100,-101,-102,-103,-104,-105,-106,-107,-108,-109,
238239462Sdim	-110,-111, -40, -36,-112,-113, -39, -11, -87,-122,
239218887Sdim	-116, -66,  -6,  -6,  -6,  -6,  -6, -37, -38,  -9,
240218887Sdim  	-6,
241218887Sdim};
242239462Sdim
243239462Sdimlong
244239462Sdimi386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
245239462Sdim{
246218887Sdim  struct reg regs;
247218887Sdim  long retval;
248218887Sdim  int i;
249234353Sdim  int errorp;
250234353Sdim  struct syscall *sc;
251234353Sdim
252234353Sdim  if (fsc.name == NULL)
253218887Sdim	return (-1);
254218887Sdim
255218887Sdim  cpid = trussinfo->curthread->tid;
256218887Sdim  if (ptrace(PT_GETREGS, cpid, (caddr_t)&regs, 0) < 0)
257218887Sdim  {
258218887Sdim    fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
259218887Sdim    return (-1);
260218887Sdim  }
261218887Sdim
262218887Sdim  retval = regs.r_eax;
263243830Sdim  errorp = !!(regs.r_eflags & PSL_C);
264218887Sdim
265234353Sdim  /*
266218887Sdim   * This code, while simpler than the initial versions I used, could
267218887Sdim   * stand some significant cleaning.
268218887Sdim   */
269218887Sdim
270218887Sdim  sc = fsc.sc;
271234353Sdim  if (!sc) {
272234353Sdim    for (i = 0; i < fsc.nargs; i++)
273234353Sdim      asprintf(&fsc.s_args[i], "0x%lx", fsc.args[i]);
274234353Sdim  } else {
275234353Sdim    /*
276234353Sdim     * Here, we only look for arguments that have OUT masked in --
277263508Sdim     * otherwise, they were handled in the syscall_entry function.
278263508Sdim     */
279234353Sdim    for (i = 0; i < sc->nargs; i++) {
280234353Sdim      char *temp;
281234353Sdim      if (sc->args[i].type & OUT) {
282234353Sdim	/*
283234353Sdim	 * If an error occurred, than don't bothe getting the data;
284234353Sdim	 * it may not be valid.
285234353Sdim	 */
286234353Sdim	if (errorp)
287234353Sdim	  asprintf(&temp, "0x%lx", fsc.args[sc->args[i].offset]);
288234353Sdim	else
289234353Sdim	  temp = print_arg(&sc->args[i], fsc.args, retval, trussinfo);
290234353Sdim	fsc.s_args[i] = temp;
291263508Sdim      }
292263508Sdim    }
293234353Sdim  }
294234353Sdim
295234353Sdim  /*
296234353Sdim   * It would probably be a good idea to merge the error handling,
297234353Sdim   * but that complicates things considerably.
298234353Sdim   */
299234353Sdim  if (errorp) {
300234353Sdim    for (i = 0; (size_t)i < sizeof(bsd_to_linux_errno) / sizeof(int); i++)
301234353Sdim      if (retval == bsd_to_linux_errno[i])
302234353Sdim      break;
303234353Sdim  }
304234353Sdim
305251662Sdim  if (fsc.name != NULL &&
306251662Sdim      (!strcmp(fsc.name, "linux_execve") || !strcmp(fsc.name, "exit"))) {
307234353Sdim	trussinfo->curthread->in_syscall = 1;
308234353Sdim  }
309234353Sdim
310234353Sdim  print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp,
311234353Sdim                    errorp ? i : retval, fsc.sc);
312234353Sdim  clear_fsc();
313234353Sdim
314234353Sdim  return (retval);
315226633Sdim}
316218887Sdim