xref: /dragonfly/usr.bin/truss/x86_64-fbsd.c (revision 2dac8a3e)
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-fbsd.c,v 1.7.2.2 2001/10/29 20:12:56 des Exp $
32  */
33 
34 /*
35  * FreeBSD/x86_64-specific system call handling.  This is probably the most
36  * complex part of the entire truss program, although I've got lots of
37  * it handled relatively cleanly now.  The system call names are generated
38  * automatically, thanks to /usr/src/sys/kern/syscalls.master.  The
39  * names used for the various structures are confusing, I sadly admit.
40  */
41 
42 #include <sys/types.h>
43 #include <sys/ioctl.h>
44 #include <sys/pioctl.h>
45 #include <sys/syscall.h>
46 
47 #include <machine/reg.h>
48 #include <machine/psl.h>
49 
50 #include <err.h>
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 
63 static int fd = -1;
64 static int cpid = -1;
65 
66 #include "syscalls.h"
67 
68 static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]);
69 
70 /*
71  * This is what this particular file uses to keep track of a system call.
72  * It is probably not quite sufficient -- I can probably use the same
73  * structure for the various syscall personalities, and I also probably
74  * need to nest system calls (for signal handlers).
75  *
76  * 'struct syscall' describes the system call; it may be NULL, however,
77  * if we don't know about this particular system call yet.
78  */
79 static struct freebsd_syscall {
80 	struct syscall *sc;
81 	const char *name;
82 	int number;
83 	unsigned long *args;
84 	int nargs;	/* number of arguments -- *not* number of words! */
85 	char **s_args;	/* the printable arguments */
86 } fsc;
87 
88 /* Clear up and free parts of the fsc structure. */
89 static inline void
90 clear_fsc(void) {
91   if (fsc.args) {
92     free(fsc.args);
93   }
94   if (fsc.s_args) {
95     int i;
96     for (i = 0; i < fsc.nargs; i++)
97       if (fsc.s_args[i])
98 	free(fsc.s_args[i]);
99     free(fsc.s_args);
100   }
101   memset(&fsc, 0, sizeof(fsc));
102 }
103 
104 /*
105  * Called when a process has entered a system call.  nargs is the
106  * number of words, not number of arguments (a necessary distinction
107  * in some cases).  Note that if the STOPEVENT() code in i386/i386/trap.c
108  * is ever changed these functions need to keep up.
109  */
110 
111 void
112 x86_64_syscall_entry(struct trussinfo *trussinfo, int nargs) {
113   char *buf;
114   struct reg regs = { .r_err = 0 };
115   int syscall_num;
116   int i, reg;
117   struct syscall *sc;
118 
119   if (fd == -1 || trussinfo->pid != cpid) {
120     asprintf(&buf, "%s/%d/regs", procfs_path, trussinfo->pid);
121     if (buf == NULL)
122       err(1, "Out of memory");
123     fd = open(buf, O_RDWR);
124     free(buf);
125     if (fd == -1) {
126       fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
127       return;
128     }
129     cpid = trussinfo->pid;
130   }
131 
132   clear_fsc();
133   lseek(fd, 0L, 0);
134   i = read(fd, &regs, sizeof(regs));
135 
136   /*
137    * FreeBSD has two special kinds of system call redirctions --
138    * SYS_syscall, and SYS___syscall.  The former is the old syscall()
139    * routine, basicly; the latter is for quad-aligned arguments.
140    */
141   reg = 0;
142   syscall_num = regs.r_rax;
143   switch (syscall_num) {
144   case SYS_syscall:
145   case SYS___syscall:
146     syscall_num = regs.r_rdi;
147     reg++;
148     break;
149   }
150 
151   fsc.number = syscall_num;
152   fsc.name =
153     (syscall_num < 0 || syscall_num >= nsyscalls) ? NULL : syscallnames[syscall_num];
154   if (!fsc.name) {
155     fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall_num);
156   }
157 
158   if (nargs == 0)
159     return;
160 
161   fsc.args = malloc((1+nargs) * sizeof(unsigned long));
162   for (i = 0; i < nargs && reg < 6; i++, reg++) {
163     switch (reg) {
164     case 0: fsc.args[i] = regs.r_rdi; break;
165     case 1: fsc.args[i] = regs.r_rsi; break;
166     case 2: fsc.args[i] = regs.r_rdx; break;
167     case 3: fsc.args[i] = regs.r_rcx; break;
168     case 4: fsc.args[i] = regs.r_r8; break;
169     case 5: fsc.args[i] = regs.r_r9; break;
170     }
171   }
172   if (nargs > i) {
173     lseek(Procfd, regs.r_rsp + sizeof(register_t), SEEK_SET);
174     if (read(Procfd, &fsc.args[i], (nargs-i) * sizeof(register_t)) == -1)
175       return;
176   }
177 
178   sc = fsc.name ? get_syscall(fsc.name) : NULL;
179   if (sc) {
180     fsc.nargs = sc->nargs;
181   } else {
182 #ifdef DEBUG
183     fprintf(trussinfo->trussinfo->outfile, "unknown syscall %s -- setting args to %d\n",
184 	   fsc.name, nargs);
185 #endif
186     fsc.nargs = nargs;
187   }
188 
189   fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*));
190   memset(fsc.s_args, 0, fsc.nargs * sizeof(char*));
191   fsc.sc = sc;
192 
193   /*
194    * At this point, we set up the system call arguments.
195    * We ignore any OUT ones, however -- those are arguments that
196    * are set by the system call, and so are probably meaningless
197    * now.  This doesn't currently support arguments that are
198    * passed in *and* out, however.
199    */
200 
201   if (fsc.name) {
202 
203 #ifdef DEBUG
204     fprintf(stderr, "syscall %s(", fsc.name);
205 #endif
206     for (i = 0; i < fsc.nargs; i++) {
207 #ifdef DEBUG
208       fprintf(stderr, "0x%x%s",
209 	     sc
210 	     ? fsc.args[sc->args[i].offset]
211 	     : fsc.args[i],
212 	     i < (fsc.nargs -1) ? "," : "");
213 #endif
214       if (sc && !(sc->args[i].type & OUT)) {
215 	fsc.s_args[i] = print_arg(Procfd, &sc->args[i], fsc.args);
216       }
217     }
218 #ifdef DEBUG
219     fprintf(stderr, ")\n");
220 #endif
221   }
222 
223 #ifdef DEBUG
224   fprintf(trussinfo->trussinfo->outfile, "\n");
225 #endif
226 
227   /*
228    * Some system calls should be printed out before they are done --
229    * execve() and exit(), for example, never return.  Possibly change
230    * this to work for any system call that doesn't have an OUT
231    * parameter?
232    */
233 
234   if (fsc.name != NULL &&
235       (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit"))) {
236     print_syscall(trussinfo, fsc.name, fsc.nargs, fsc.s_args);
237   }
238 
239   return;
240 }
241 
242 /*
243  * And when the system call is done, we handle it here.
244  * Currently, no attempt is made to ensure that the system calls
245  * match -- this needs to be fixed (and is, in fact, why S_SCX includes
246  * the sytem call number instead of, say, an error status).
247  */
248 
249 int
250 x86_64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) {
251   char *buf;
252   struct reg regs;
253   int retval;
254   int i;
255   int errorp;
256   struct syscall *sc;
257 
258   if (fsc.name == NULL)
259     return 0;
260 
261   if (fd == -1 || trussinfo->pid != cpid) {
262     asprintf(&buf, "%s/%d/regs", procfs_path, trussinfo->pid);
263     if (buf == NULL)
264       err(1, "Out of memory");
265     fd = open(buf, O_RDONLY);
266     free(buf);
267     if (fd == -1) {
268       fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
269       return 0;
270     }
271     cpid = trussinfo->pid;
272   }
273 
274   lseek(fd, 0L, 0);
275   if (read(fd, &regs, sizeof(regs)) != sizeof(regs)) {
276 	  fprintf(trussinfo->outfile, "\n");
277 	  return 0;
278   }
279   retval = regs.r_rax;
280   errorp = !!(regs.r_rflags & PSL_C);
281 
282   /*
283    * This code, while simpler than the initial versions I used, could
284    * stand some significant cleaning.
285    */
286 
287   sc = fsc.sc;
288   if (!sc) {
289     for (i = 0; i < fsc.nargs; i++) {
290       fsc.s_args[i] = malloc(12);
291       sprintf(fsc.s_args[i], "0x%lx", fsc.args[i]);
292     }
293   } else {
294     /*
295      * Here, we only look for arguments that have OUT masked in --
296      * otherwise, they were handled in the syscall_entry function.
297      */
298     for (i = 0; i < sc->nargs; i++) {
299       char *temp;
300       if (sc->args[i].type & OUT) {
301 	/*
302 	 * If an error occurred, than don't bothe getting the data;
303 	 * it may not be valid.
304 	 */
305 	if (errorp) {
306 	  temp = malloc(12);
307 	  sprintf(temp, "0x%lx", fsc.args[sc->args[i].offset]);
308 	} else {
309 	  temp = print_arg(Procfd, &sc->args[i], fsc.args);
310 	}
311 	fsc.s_args[i] = temp;
312       }
313     }
314   }
315 
316   /*
317    * It would probably be a good idea to merge the error handling,
318    * but that complicates things considerably.
319    */
320 
321   print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, retval);
322   clear_fsc();
323 
324   return (retval);
325 }
326