1247975Scognet/*
2247975Scognet * Copyright 1997 Sean Eric Fagan
3247975Scognet *
4247975Scognet * Redistribution and use in source and binary forms, with or without
5247975Scognet * modification, are permitted provided that the following conditions
6247975Scognet * are met:
7247975Scognet * 1. Redistributions of source code must retain the above copyright
8247975Scognet *    notice, this list of conditions and the following disclaimer.
9247975Scognet * 2. Redistributions in binary form must reproduce the above copyright
10247975Scognet *    notice, this list of conditions and the following disclaimer in the
11247975Scognet *    documentation and/or other materials provided with the distribution.
12247975Scognet * 3. All advertising materials mentioning features or use of this software
13247975Scognet *    must display the following acknowledgement:
14247975Scognet *	This product includes software developed by Sean Eric Fagan
15247975Scognet * 4. Neither the name of the author may be used to endorse or promote
16247975Scognet *    products derived from this software without specific prior written
17247975Scognet *    permission.
18247975Scognet *
19247975Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20247975Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21247975Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22247975Scognet * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23247975Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24247975Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25247975Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26247975Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27247975Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28247975Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29247975Scognet * SUCH DAMAGE.
30247975Scognet */
31247975Scognet
32247975Scognet/*
33247975Scognet * FreeBSD/arm-specific system call handling.  This is probably the most
34247975Scognet * complex part of the entire truss program, although I've got lots of
35247975Scognet * it handled relatively cleanly now.  The system call names are generated
36247975Scognet * automatically, thanks to /usr/src/sys/kern/syscalls.master.  The
37247975Scognet * names used for the various structures are confusing, I sadly admit.
38247975Scognet */
39247975Scognet
40247975Scognet#include <sys/cdefs.h>
41247975Scognet__FBSDID("$FreeBSD$");
42247975Scognet#include <sys/types.h>
43247975Scognet#include <sys/ptrace.h>
44247975Scognet#include <sys/syscall.h>
45247975Scognet
46247975Scognet#include <machine/reg.h>
47247975Scognet#include <machine/armreg.h>
48247975Scognet#include <machine/ucontext.h>
49247975Scognet
50247975Scognet#include <errno.h>
51247975Scognet#include <fcntl.h>
52247975Scognet#include <signal.h>
53247975Scognet#include <stdio.h>
54247975Scognet#include <stdlib.h>
55247975Scognet#include <string.h>
56247975Scognet#include <time.h>
57247975Scognet#include <unistd.h>
58247975Scognet#include <err.h>
59247975Scognet
60247975Scognet#include "truss.h"
61247975Scognet#include "syscall.h"
62247975Scognet#include "extern.h"
63247975Scognet
64247975Scognet#include "syscalls.h"
65247975Scognet
66247975Scognet
67247975Scognetstatic int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]);
68247975Scognet
69247975Scognet/*
70247975Scognet * This is what this particular file uses to keep track of a system call.
71247975Scognet * It is probably not quite sufficient -- I can probably use the same
72247975Scognet * structure for the various syscall personalities, and I also probably
73247975Scognet * need to nest system calls (for signal handlers).
74247975Scognet *
75247975Scognet * 'struct syscall' describes the system call; it may be NULL, however,
76247975Scognet * if we don't know about this particular system call yet.
77247975Scognet */
78247975Scognetstruct freebsd_syscall {
79247975Scognet	struct syscall *sc;
80247975Scognet	const char *name;
81247975Scognet	int number;
82247975Scognet	unsigned long *args;
83247975Scognet	int nargs;	/* number of arguments -- *not* number of words! */
84247975Scognet	char **s_args;	/* the printable arguments */
85247975Scognet};
86247975Scognet
87247975Scognetstatic struct freebsd_syscall *
88247975Scognetalloc_fsc(void)
89247975Scognet{
90247975Scognet
91247975Scognet	return (malloc(sizeof(struct freebsd_syscall)));
92247975Scognet}
93247975Scognet
94247975Scognet/* Clear up and free parts of the fsc structure. */
95247975Scognetstatic void
96247975Scognetfree_fsc(struct freebsd_syscall *fsc)
97247975Scognet{
98247975Scognet	int i;
99247975Scognet
100247975Scognet	free(fsc->args);
101247975Scognet	if (fsc->s_args) {
102247975Scognet		for (i = 0; i < fsc->nargs; i++)
103247975Scognet			free(fsc->s_args[i]);
104247975Scognet		free(fsc->s_args);
105247975Scognet	}
106247975Scognet	free(fsc);
107247975Scognet}
108247975Scognet
109247975Scognet/*
110247975Scognet * Called when a process has entered a system call.  nargs is the
111247975Scognet * number of words, not number of arguments (a necessary distinction
112247975Scognet * in some cases).  Note that if the STOPEVENT() code in i386/i386/trap.c
113247975Scognet * is ever changed these functions need to keep up.
114247975Scognet */
115247975Scognet
116247975Scognetvoid
117247975Scognetarm_syscall_entry(struct trussinfo *trussinfo, int nargs)
118247975Scognet{
119247975Scognet	struct ptrace_io_desc iorequest;
120247975Scognet	struct reg regs;
121247975Scognet	struct freebsd_syscall *fsc;
122247975Scognet	struct syscall *sc;
123247975Scognet	lwpid_t tid;
124247975Scognet	int i, syscall_num;
125247975Scognet	register_t *ap;
126247975Scognet
127247975Scognet	tid = trussinfo->curthread->tid;
128247975Scognet
129247975Scognet	if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
130247975Scognet		fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
131247975Scognet		return;
132247975Scognet	}
133247975Scognet	ap = &regs.r[0];
134247975Scognet
135247975Scognet	/*
136247975Scognet	 * FreeBSD has two special kinds of system call redirctions --
137247975Scognet	 * SYS_syscall, and SYS___syscall.  The former is the old syscall()
138247975Scognet	 * routine, basically; the latter is for quad-aligned arguments.
139247975Scognet	 */
140247975Scognet#ifdef __ARM_EABI__
141247975Scognet	syscall_num = regs.r[7];
142247975Scognet#else
143247975Scognet	if ((syscall_num = ptrace(PT_READ_I, tid,
144247975Scognet	    (caddr_t)(regs.r[_REG_PC] - INSN_SIZE), 0)) == -1) {
145247975Scognet		fprintf(trussinfo->outfile, "-- CANNOT READ PC --\n");
146247975Scognet		return;
147247975Scognet	}
148247975Scognet	syscall_num = syscall_num & 0x000fffff;
149247975Scognet#endif
150247975Scognet	switch (syscall_num) {
151247975Scognet	case SYS_syscall:
152247975Scognet		syscall_num = *ap++;
153247975Scognet		nargs--;
154247975Scognet		break;
155247975Scognet	case SYS___syscall:
156247975Scognet		syscall_num = ap[_QUAD_LOWWORD];
157247975Scognet		ap += 2;
158247975Scognet		nargs -= 2;
159247975Scognet		break;
160247975Scognet	}
161247975Scognet
162247975Scognet	fsc = alloc_fsc();
163247975Scognet	if (fsc == NULL)
164247975Scognet		return;
165247975Scognet	fsc->number = syscall_num;
166247975Scognet	fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
167247975Scognet	    NULL : syscallnames[syscall_num];
168247975Scognet	if (!fsc->name) {
169247975Scognet		fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
170247975Scognet		    syscall_num);
171247975Scognet	}
172247975Scognet
173247975Scognet	if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
174247975Scognet	    (strcmp(fsc->name, "fork") == 0 ||
175247975Scognet	    strcmp(fsc->name, "rfork") == 0 ||
176247975Scognet	    strcmp(fsc->name, "vfork") == 0))
177247975Scognet		trussinfo->curthread->in_fork = 1;
178247975Scognet
179247975Scognet	if (nargs == 0)
180247975Scognet		return;
181247975Scognet
182247975Scognet	fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
183247975Scognet	switch (nargs) {
184247975Scognet	default:
185247975Scognet		/*
186247975Scognet		 * The OS doesn't seem to allow more than 10 words of
187247975Scognet		 * parameters (yay!).	So we shouldn't be here.
188247975Scognet		 */
189247975Scognet		warn("More than 10 words (%d) of arguments!\n", nargs);
190247975Scognet		break;
191247975Scognet	case 10:
192247975Scognet	case 9:
193247975Scognet	case 8:
194247975Scognet	case 7:
195247975Scognet	case 6:
196247975Scognet	case 5:
197247975Scognet		/*
198247975Scognet		 * If there are 7-10 words of arguments, they are placed
199247975Scognet		 * on the stack, as is normal for other processors.
200247975Scognet		 * The fall-through for all of these is deliberate!!!
201247975Scognet		 */
202247975Scognet		// XXX BAD constant used here
203247975Scognet		iorequest.piod_op = PIOD_READ_D;
204247975Scognet		iorequest.piod_offs = (void *)(regs.r[_REG_SP] +
205247975Scognet		    4 * sizeof(uint32_t));
206247975Scognet		iorequest.piod_addr = &fsc->args[4];
207247975Scognet		iorequest.piod_len = (nargs - 4) * sizeof(fsc->args[0]);
208247975Scognet		ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
209247975Scognet		if (iorequest.piod_len == 0)
210247975Scognet			return;
211247975Scognet	case 4:	fsc->args[3] = ap[3];
212247975Scognet	case 3:	fsc->args[2] = ap[2];
213247975Scognet	case 2:	fsc->args[1] = ap[1];
214247975Scognet	case 1:	fsc->args[0] = ap[0];
215247975Scognet	case 0: break;
216247975Scognet	}
217247975Scognet
218247975Scognet	sc = NULL;
219247975Scognet	if (fsc->name)
220247975Scognet		sc = get_syscall(fsc->name);
221247975Scognet	if (sc)
222247975Scognet		fsc->nargs = sc->nargs;
223247975Scognet	else {
224247975Scognet#if DEBUG
225247975Scognet		fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
226247975Scognet		    "args to %d\n", fsc->name, nargs);
227247975Scognet#endif
228247975Scognet		fsc->nargs = nargs;
229247975Scognet	}
230247975Scognet
231247975Scognet	fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
232247975Scognet	fsc->sc = sc;
233247975Scognet
234247975Scognet	/*
235247975Scognet	 * At this point, we set up the system call arguments.
236247975Scognet	 * We ignore any OUT ones, however -- those are arguments that
237247975Scognet	 * are set by the system call, and so are probably meaningless
238247975Scognet	 * now.	This doesn't currently support arguments that are
239247975Scognet	 * passed in *and* out, however.
240247975Scognet	 */
241247975Scognet
242247975Scognet	if (fsc->name) {
243247975Scognet#if DEBUG
244247975Scognet		fprintf(stderr, "syscall %s(", fsc->name);
245247975Scognet#endif
246247975Scognet		for (i = 0; i < fsc->nargs; i++) {
247247975Scognet#if DEBUG
248247975Scognet			fprintf(stderr, "0x%x%s", sc ?
249247975Scognet			    fsc->args[sc->args[i].offset] : fsc->args[i],
250247975Scognet			    i < (fsc->nargs - 1) ? "," : "");
251247975Scognet#endif
252247975Scognet			if (sc && !(sc->args[i].type & OUT)) {
253247975Scognet				fsc->s_args[i] = print_arg(&sc->args[i],
254247975Scognet				    fsc->args, 0, trussinfo);
255247975Scognet			}
256247975Scognet		}
257247975Scognet#if DEBUG
258247975Scognet		fprintf(stderr, ")\n");
259247975Scognet#endif
260247975Scognet	}
261247975Scognet
262247975Scognet#if DEBUG
263247975Scognet	fprintf(trussinfo->outfile, "\n");
264247975Scognet#endif
265247975Scognet
266247975Scognet	if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
267247975Scognet	    strcmp(fsc->name, "exit") == 0)) {
268247975Scognet		/*
269247975Scognet		 * XXX
270247975Scognet		 * This could be done in a more general
271247975Scognet		 * manner but it still wouldn't be very pretty.
272247975Scognet		 */
273247975Scognet		if (strcmp(fsc->name, "execve") == 0) {
274247975Scognet			if ((trussinfo->flags & EXECVEARGS) == 0) {
275247975Scognet				if (fsc->s_args[1]) {
276247975Scognet					free(fsc->s_args[1]);
277247975Scognet					fsc->s_args[1] = NULL;
278247975Scognet				}
279247975Scognet			}
280247975Scognet			if ((trussinfo->flags & EXECVEENVS) == 0) {
281247975Scognet				if (fsc->s_args[2]) {
282247975Scognet					free(fsc->s_args[2]);
283247975Scognet					fsc->s_args[2] = NULL;
284247975Scognet				}
285247975Scognet			}
286247975Scognet		}
287247975Scognet	}
288247975Scognet	trussinfo->curthread->fsc = fsc;
289247975Scognet}
290247975Scognet
291247975Scognet/*
292247975Scognet * And when the system call is done, we handle it here.
293247975Scognet * Currently, no attempt is made to ensure that the system calls
294247975Scognet * match -- this needs to be fixed (and is, in fact, why S_SCX includes
295247975Scognet * the system call number instead of, say, an error status).
296247975Scognet */
297247975Scognet
298247975Scognetlong
299247975Scognetarm_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
300247975Scognet{
301247975Scognet	struct reg regs;
302247975Scognet	struct freebsd_syscall *fsc;
303247975Scognet	struct syscall *sc;
304247975Scognet	lwpid_t tid;
305247975Scognet	long retval;
306247975Scognet	int errorp, i;
307247975Scognet
308247975Scognet	if (trussinfo->curthread->fsc == NULL)
309247975Scognet		return (-1);
310247975Scognet
311247975Scognet	tid = trussinfo->curthread->tid;
312247975Scognet
313247975Scognet	if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
314247975Scognet		fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
315247975Scognet		return (-1);
316247975Scognet	}
317247975Scognet
318247975Scognet	retval = regs.r[0];
319247975Scognet	errorp = !!(regs.r_cpsr & PSR_C_bit);
320247975Scognet
321247975Scognet	/*
322247975Scognet	 * This code, while simpler than the initial versions I used, could
323247975Scognet	 * stand some significant cleaning.
324247975Scognet	 */
325247975Scognet
326247975Scognet	fsc = trussinfo->curthread->fsc;
327247975Scognet	sc = fsc->sc;
328247975Scognet	if (!sc) {
329247975Scognet		for (i = 0; i < fsc->nargs; i++)
330247975Scognet			asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
331247975Scognet	} else {
332247975Scognet		/*
333247975Scognet		 * Here, we only look for arguments that have OUT masked in --
334247975Scognet		 * otherwise, they were handled in the syscall_entry function.
335247975Scognet		 */
336247975Scognet		for (i = 0; i < sc->nargs; i++) {
337247975Scognet			char *temp;
338247975Scognet			if (sc->args[i].type & OUT) {
339247975Scognet				/*
340247975Scognet				 * If an error occurred, then don't bother
341247975Scognet				 * getting the data; it may not be valid.
342247975Scognet				 */
343247975Scognet				if (errorp) {
344247975Scognet					asprintf(&temp, "0x%lx",
345247975Scognet					    fsc->args[sc->args[i].offset]);
346247975Scognet				} else {
347247975Scognet					temp = print_arg(&sc->args[i],
348247975Scognet					    fsc->args, retval, trussinfo);
349247975Scognet				}
350247975Scognet				fsc->s_args[i] = temp;
351247975Scognet			}
352247975Scognet		}
353247975Scognet	}
354247975Scognet
355247975Scognet	if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
356247975Scognet	    strcmp(fsc->name, "exit") == 0))
357247975Scognet		trussinfo->curthread->in_syscall = 1;
358247975Scognet
359247975Scognet	/*
360247975Scognet	 * It would probably be a good idea to merge the error handling,
361247975Scognet	 * but that complicates things considerably.
362247975Scognet	 */
363247975Scognet
364247975Scognet	print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
365247975Scognet	    retval, fsc->sc);
366247975Scognet	free_fsc(fsc);
367247975Scognet
368247975Scognet	return (retval);
369247975Scognet}
370