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)®s, 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)®s, 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