i386-fbsd.c revision 286789
133965Sjdp/*
238889Sjdp * Copyright 1997 Sean Eric Fagan
333965Sjdp *
433965Sjdp * Redistribution and use in source and binary forms, with or without
533965Sjdp * modification, are permitted provided that the following conditions
633965Sjdp * are met:
733965Sjdp * 1. Redistributions of source code must retain the above copyright
833965Sjdp *    notice, this list of conditions and the following disclaimer.
933965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1033965Sjdp *    notice, this list of conditions and the following disclaimer in the
1133965Sjdp *    documentation and/or other materials provided with the distribution.
1233965Sjdp * 3. All advertising materials mentioning features or use of this software
1333965Sjdp *    must display the following acknowledgement:
1433965Sjdp *	This product includes software developed by Sean Eric Fagan
1533965Sjdp * 4. Neither the name of the author may be used to endorse or promote
1633965Sjdp *    products derived from this software without specific prior written
1733965Sjdp *    permission.
1833965Sjdp *
1933965Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2033965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2133965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2233965Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2333965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2433965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2533965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2633965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2733965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2833965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2933965Sjdp * SUCH DAMAGE.
3033965Sjdp */
3133965Sjdp
3233965Sjdp#ifndef lint
3333965Sjdpstatic const char rcsid[] =
3433965Sjdp  "$FreeBSD: stable/10/usr.bin/truss/i386-fbsd.c 286789 2015-08-14 21:44:42Z emaste $";
3533965Sjdp#endif /* not lint */
3633965Sjdp
3733965Sjdp/*
3833965Sjdp * FreeBSD/i386-specific system call handling.  This is probably the most
3933965Sjdp * complex part of the entire truss program, although I've got lots of
4033965Sjdp * it handled relatively cleanly now.  The system call names are generated
4133965Sjdp * automatically, thanks to /usr/src/sys/kern/syscalls.master.  The
4233965Sjdp * names used for the various structures are confusing, I sadly admit.
4333965Sjdp */
4433965Sjdp
4533965Sjdp#include <sys/types.h>
4633965Sjdp#include <sys/ptrace.h>
4733965Sjdp#include <sys/syscall.h>
4833965Sjdp
4933965Sjdp#include <machine/reg.h>
5033965Sjdp#include <machine/psl.h>
5133965Sjdp
5233965Sjdp#include <errno.h>
5333965Sjdp#include <fcntl.h>
5433965Sjdp#include <signal.h>
5533965Sjdp#include <stdio.h>
5633965Sjdp#include <stdlib.h>
5733965Sjdp#include <string.h>
5833965Sjdp#include <time.h>
5933965Sjdp#include <unistd.h>
6033965Sjdp
6133965Sjdp#include "truss.h"
6233965Sjdp#include "syscall.h"
6333965Sjdp#include "extern.h"
6433965Sjdp
6533965Sjdp#include "syscalls.h"
6633965Sjdp
6733965Sjdpstatic int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]);
6833965Sjdp
6933965Sjdp/*
7033965Sjdp * This is what this particular file uses to keep track of a system call.
7133965Sjdp * It is probably not quite sufficient -- I can probably use the same
7233965Sjdp * structure for the various syscall personalities, and I also probably
7333965Sjdp * need to nest system calls (for signal handlers).
7433965Sjdp *
7533965Sjdp * 'struct syscall' describes the system call; it may be NULL, however,
7633965Sjdp * if we don't know about this particular system call yet.
7733965Sjdp */
7833965Sjdpstruct freebsd_syscall {
7933965Sjdp	struct syscall *sc;
8033965Sjdp	const char *name;
8133965Sjdp	int number;
8233965Sjdp	unsigned long *args;
8333965Sjdp	int nargs;	/* number of arguments -- *not* number of words! */
8433965Sjdp	char **s_args;	/* the printable arguments */
8533965Sjdp};
8633965Sjdp
8733965Sjdpstatic struct freebsd_syscall *
8833965Sjdpalloc_fsc(void)
8933965Sjdp{
9033965Sjdp
9133965Sjdp	return (malloc(sizeof(struct freebsd_syscall)));
9233965Sjdp}
9333965Sjdp
9433965Sjdp/* Clear up and free parts of the fsc structure. */
9533965Sjdpstatic void
9633965Sjdpfree_fsc(struct freebsd_syscall *fsc)
9733965Sjdp{
9833965Sjdp	int i;
9933965Sjdp
10033965Sjdp	free(fsc->args);
10133965Sjdp	if (fsc->s_args) {
10233965Sjdp		for (i = 0; i < fsc->nargs; i++)
10333965Sjdp			free(fsc->s_args[i]);
10433965Sjdp		free(fsc->s_args);
10533965Sjdp	}
10633965Sjdp	free(fsc);
10733965Sjdp}
10833965Sjdp
10933965Sjdp/*
11033965Sjdp * Called when a process has entered a system call.  nargs is the
11133965Sjdp * number of words, not number of arguments (a necessary distinction
11233965Sjdp * in some cases).  Note that if the STOPEVENT() code in i386/i386/trap.c
11333965Sjdp * is ever changed these functions need to keep up.
11433965Sjdp */
11533965Sjdp
11633965Sjdpvoid
11733965Sjdpi386_syscall_entry(struct trussinfo *trussinfo, int nargs)
11833965Sjdp{
11933965Sjdp	struct ptrace_io_desc iorequest;
12033965Sjdp	struct reg regs;
12133965Sjdp	struct freebsd_syscall *fsc;
12233965Sjdp	struct syscall *sc;
12333965Sjdp	lwpid_t tid;
12433965Sjdp	unsigned int parm_offset;
12533965Sjdp	int i, syscall_num;
12633965Sjdp
12733965Sjdp	tid = trussinfo->curthread->tid;
12833965Sjdp
12933965Sjdp	if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
13033965Sjdp		fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
13138889Sjdp		return;
13233965Sjdp	}
13338889Sjdp	parm_offset = regs.r_esp + sizeof(int);
13433965Sjdp
13533965Sjdp	/*
13633965Sjdp	 * FreeBSD has two special kinds of system call redirctions --
13733965Sjdp	 * SYS_syscall, and SYS___syscall.  The former is the old syscall()
13833965Sjdp	 * routine, basically; the latter is for quad-aligned arguments.
13933965Sjdp	 */
14033965Sjdp	syscall_num = regs.r_eax;
14133965Sjdp	switch (syscall_num) {
14233965Sjdp	case SYS_syscall:
14333965Sjdp		syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0);
14433965Sjdp		parm_offset += sizeof(int);
14533965Sjdp		break;
14633965Sjdp	case SYS___syscall:
14733965Sjdp		syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0);
14833965Sjdp		parm_offset += sizeof(quad_t);
14933965Sjdp		break;
15033965Sjdp	}
15133965Sjdp
15233965Sjdp	fsc = alloc_fsc();
15333965Sjdp	if (fsc == NULL)
15433965Sjdp		return;
15533965Sjdp	fsc->number = syscall_num;
15633965Sjdp	fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
15733965Sjdp	    NULL : syscallnames[syscall_num];
15833965Sjdp	if (!fsc->name) {
15933965Sjdp		fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
16033965Sjdp		    syscall_num);
16133965Sjdp	}
16233965Sjdp
16333965Sjdp	if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
16433965Sjdp	    (strcmp(fsc->name, "fork") == 0 ||
16533965Sjdp	    strcmp(fsc->name, "pdfork") == 0 ||
16633965Sjdp	    strcmp(fsc->name, "rfork") == 0 ||
16733965Sjdp	    strcmp(fsc->name, "vfork") == 0))
16838889Sjdp		trussinfo->curthread->in_fork = 1;
16933965Sjdp
17038889Sjdp	if (nargs == 0)
17133965Sjdp		return;
17238889Sjdp
17338889Sjdp	fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
17433965Sjdp	iorequest.piod_op = PIOD_READ_D;
17533965Sjdp	iorequest.piod_offs = (void *)parm_offset;
17633965Sjdp	iorequest.piod_addr = fsc->args;
17733965Sjdp	iorequest.piod_len = (1 + nargs) * sizeof(unsigned long);
17833965Sjdp	ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
17933965Sjdp	if (iorequest.piod_len == 0)
18033965Sjdp		return;
18133965Sjdp
18233965Sjdp	sc = NULL;
18333965Sjdp	if (fsc->name)
18433965Sjdp		sc = get_syscall(fsc->name);
18533965Sjdp	if (sc)
18633965Sjdp		fsc->nargs = sc->nargs;
18733965Sjdp	else {
18833965Sjdp#if DEBUG
18933965Sjdp		fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
19033965Sjdp		    "args to %d\n", fsc->name, nargs);
19133965Sjdp#endif
19233965Sjdp		fsc->nargs = nargs;
19333965Sjdp	}
19433965Sjdp
19533965Sjdp	fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
19633965Sjdp	fsc->sc = sc;
19733965Sjdp
19833965Sjdp	/*
19933965Sjdp	 * At this point, we set up the system call arguments.
20038889Sjdp	 * We ignore any OUT ones, however -- those are arguments that
20138889Sjdp	 * are set by the system call, and so are probably meaningless
20238889Sjdp	 * now.	This doesn't currently support arguments that are
20338889Sjdp	 * passed in *and* out, however.
20438889Sjdp	 */
20538889Sjdp
20638889Sjdp	if (fsc->name) {
20738889Sjdp#if DEBUG
20838889Sjdp		fprintf(stderr, "syscall %s(", fsc->name);
20933965Sjdp#endif
21033965Sjdp		for (i = 0; i < fsc->nargs; i++) {
21133965Sjdp#if DEBUG
21233965Sjdp			fprintf(stderr, "0x%x%s", sc ?
21333965Sjdp			    fsc->args[sc->args[i].offset] : fsc->args[i],
21433965Sjdp			    i < (fsc->nargs - 1) ? "," : "");
21533965Sjdp#endif
21633965Sjdp			if (sc && !(sc->args[i].type & OUT)) {
21733965Sjdp				fsc->s_args[i] = print_arg(&sc->args[i],
21833965Sjdp				    fsc->args, 0, trussinfo);
21933965Sjdp			}
22033965Sjdp		}
22133965Sjdp#if DEBUG
22233965Sjdp		fprintf(stderr, ")\n");
22333965Sjdp#endif
22433965Sjdp	}
22533965Sjdp
22633965Sjdp#if DEBUG
22733965Sjdp	fprintf(trussinfo->outfile, "\n");
22833965Sjdp#endif
22933965Sjdp
23033965Sjdp	if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
23133965Sjdp	    strcmp(fsc->name, "exit") == 0)) {
23233965Sjdp		/*
23333965Sjdp		 * XXX
23433965Sjdp		 * This could be done in a more general
23533965Sjdp		 * manner but it still wouldn't be very pretty.
23633965Sjdp		 */
23733965Sjdp		if (strcmp(fsc->name, "execve") == 0) {
23833965Sjdp			if ((trussinfo->flags & EXECVEARGS) == 0) {
23933965Sjdp				if (fsc->s_args[1]) {
24033965Sjdp					free(fsc->s_args[1]);
24133965Sjdp					fsc->s_args[1] = NULL;
24233965Sjdp				}
24333965Sjdp			}
24433965Sjdp			if ((trussinfo->flags & EXECVEENVS) == 0) {
24533965Sjdp				if (fsc->s_args[2]) {
24633965Sjdp					free(fsc->s_args[2]);
24733965Sjdp					fsc->s_args[2] = NULL;
24833965Sjdp				}
24933965Sjdp			}
25033965Sjdp		}
25133965Sjdp	}
25233965Sjdp	trussinfo->curthread->fsc = fsc;
25333965Sjdp}
25433965Sjdp
25533965Sjdp/*
25633965Sjdp * And when the system call is done, we handle it here.
25733965Sjdp * Currently, no attempt is made to ensure that the system calls
25833965Sjdp * match -- this needs to be fixed (and is, in fact, why S_SCX includes
25933965Sjdp * the system call number instead of, say, an error status).
26033965Sjdp */
26133965Sjdp
26233965Sjdplong
26333965Sjdpi386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
26433965Sjdp{
26533965Sjdp	struct reg regs;
26633965Sjdp	struct freebsd_syscall *fsc;
26733965Sjdp	struct syscall *sc;
26833965Sjdp	lwpid_t tid;
26933965Sjdp	long retval;
27033965Sjdp	int errorp, i;
27133965Sjdp
27233965Sjdp	if (trussinfo->curthread->fsc == NULL)
27333965Sjdp		return (-1);
27433965Sjdp
27533965Sjdp	tid = trussinfo->curthread->tid;
27633965Sjdp
27733965Sjdp	if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
27833965Sjdp		fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
27933965Sjdp		return (-1);
28033965Sjdp	}
28133965Sjdp
28233965Sjdp	retval = regs.r_eax;
28333965Sjdp	errorp = !!(regs.r_eflags & PSL_C);
28433965Sjdp
28533965Sjdp	/*
28633965Sjdp	 * This code, while simpler than the initial versions I used, could
28733965Sjdp	 * stand some significant cleaning.
28833965Sjdp	 */
28933965Sjdp
29033965Sjdp	fsc = trussinfo->curthread->fsc;
29133965Sjdp	sc = fsc->sc;
29233965Sjdp	if (!sc) {
29333965Sjdp		for (i = 0; i < fsc->nargs; i++)
29433965Sjdp			asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
29533965Sjdp	} else {
29633965Sjdp		/*
29733965Sjdp		 * Here, we only look for arguments that have OUT masked in --
29833965Sjdp		 * otherwise, they were handled in the syscall_entry function.
29933965Sjdp		 */
30033965Sjdp		for (i = 0; i < sc->nargs; i++) {
30133965Sjdp			char *temp;
30233965Sjdp			if (sc->args[i].type & OUT) {
30333965Sjdp				/*
30433965Sjdp				 * If an error occurred, then don't bother
30533965Sjdp				 * getting the data; it may not be valid.
30633965Sjdp				 */
30733965Sjdp				if (errorp) {
30833965Sjdp					asprintf(&temp, "0x%lx",
30933965Sjdp					    fsc->args[sc->args[i].offset]);
31033965Sjdp				} else {
31133965Sjdp					temp = print_arg(&sc->args[i],
31233965Sjdp					    fsc->args, retval, trussinfo);
31333965Sjdp				}
31433965Sjdp				fsc->s_args[i] = temp;
31533965Sjdp			}
31633965Sjdp		}
31733965Sjdp	}
31833965Sjdp
31933965Sjdp	if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
32033965Sjdp	    strcmp(fsc->name, "exit") == 0))
32133965Sjdp		trussinfo->curthread->in_syscall = 1;
32233965Sjdp
32333965Sjdp	/*
32433965Sjdp	 * It would probably be a good idea to merge the error handling,
32533965Sjdp	 * but that complicates things considerably.
32633965Sjdp	 */
32733965Sjdp
32833965Sjdp	print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
32933965Sjdp	    retval, fsc->sc);
33033965Sjdp	free_fsc(fsc);
33133965Sjdp
33233965Sjdp	return (retval);
33333965Sjdp}
33433965Sjdp