1130394Sdwmalone/* 2204977Simp * Copyright 1997 Sean Eric Fagan 3130394Sdwmalone * 4130394Sdwmalone * Redistribution and use in source and binary forms, with or without 5130394Sdwmalone * modification, are permitted provided that the following conditions 6130394Sdwmalone * are met: 7130394Sdwmalone * 1. Redistributions of source code must retain the above copyright 8130394Sdwmalone * notice, this list of conditions and the following disclaimer. 9130394Sdwmalone * 2. Redistributions in binary form must reproduce the above copyright 10130394Sdwmalone * notice, this list of conditions and the following disclaimer in the 11130394Sdwmalone * documentation and/or other materials provided with the distribution. 12130394Sdwmalone * 3. All advertising materials mentioning features or use of this software 13130394Sdwmalone * must display the following acknowledgement: 14130394Sdwmalone * This product includes software developed by Sean Eric Fagan 15130394Sdwmalone * 4. Neither the name of the author may be used to endorse or promote 16130394Sdwmalone * products derived from this software without specific prior written 17130394Sdwmalone * permission. 18130394Sdwmalone * 19130394Sdwmalone * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20130394Sdwmalone * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21130394Sdwmalone * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22130394Sdwmalone * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23130394Sdwmalone * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24130394Sdwmalone * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25130394Sdwmalone * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26130394Sdwmalone * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27130394Sdwmalone * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28130394Sdwmalone * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29130394Sdwmalone * SUCH DAMAGE. 30130394Sdwmalone */ 31130394Sdwmalone 32130394Sdwmalone#ifndef lint 33130394Sdwmalonestatic const char rcsid[] = 34130421Sbms "$FreeBSD$"; 35130394Sdwmalone#endif /* not lint */ 36130394Sdwmalone 37130394Sdwmalone/* 38130394Sdwmalone * FreeBSD/amd64-specific system call handling. This is probably the most 39130394Sdwmalone * complex part of the entire truss program, although I've got lots of 40130394Sdwmalone * it handled relatively cleanly now. The system call names are generated 41130394Sdwmalone * automatically, thanks to /usr/src/sys/kern/syscalls.master. The 42130394Sdwmalone * names used for the various structures are confusing, I sadly admit. 43130394Sdwmalone */ 44130394Sdwmalone 45130394Sdwmalone#include <sys/types.h> 46168569Sdelphij#include <sys/ptrace.h> 47130394Sdwmalone#include <sys/syscall.h> 48130394Sdwmalone 49130394Sdwmalone#include <machine/reg.h> 50130394Sdwmalone#include <machine/psl.h> 51130394Sdwmalone 52130394Sdwmalone#include <errno.h> 53130394Sdwmalone#include <fcntl.h> 54130394Sdwmalone#include <signal.h> 55130394Sdwmalone#include <stdio.h> 56130394Sdwmalone#include <stdlib.h> 57130394Sdwmalone#include <string.h> 58130394Sdwmalone#include <time.h> 59130394Sdwmalone#include <unistd.h> 60130394Sdwmalone 61130394Sdwmalone#include "truss.h" 62130394Sdwmalone#include "syscall.h" 63130394Sdwmalone#include "extern.h" 64130394Sdwmalone 65130394Sdwmalone#include "syscalls.h" 66130394Sdwmalone 67130394Sdwmalonestatic int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); 68130394Sdwmalone 69130394Sdwmalone/* 70130394Sdwmalone * This is what this particular file uses to keep track of a system call. 71130394Sdwmalone * It is probably not quite sufficient -- I can probably use the same 72130394Sdwmalone * structure for the various syscall personalities, and I also probably 73130394Sdwmalone * need to nest system calls (for signal handlers). 74130394Sdwmalone * 75130394Sdwmalone * 'struct syscall' describes the system call; it may be NULL, however, 76130394Sdwmalone * if we don't know about this particular system call yet. 77130394Sdwmalone */ 78240562Szontstruct freebsd_syscall { 79130394Sdwmalone struct syscall *sc; 80130394Sdwmalone const char *name; 81130394Sdwmalone int number; 82130394Sdwmalone unsigned long *args; 83130394Sdwmalone int nargs; /* number of arguments -- *not* number of words! */ 84130394Sdwmalone char **s_args; /* the printable arguments */ 85240562Szont}; 86130394Sdwmalone 87240562Szontstatic struct freebsd_syscall * 88240562Szontalloc_fsc(void) 89240562Szont{ 90240562Szont 91240562Szont return (malloc(sizeof(struct freebsd_syscall))); 92240562Szont} 93240562Szont 94130394Sdwmalone/* Clear up and free parts of the fsc structure. */ 95240562Szontstatic void 96240562Szontfree_fsc(struct freebsd_syscall *fsc) 97240005Szont{ 98240005Szont int i; 99240005Szont 100240562Szont free(fsc->args); 101240562Szont if (fsc->s_args) { 102240562Szont for (i = 0; i < fsc->nargs; i++) 103240562Szont free(fsc->s_args[i]); 104240562Szont free(fsc->s_args); 105240005Szont } 106240562Szont free(fsc); 107130394Sdwmalone} 108130394Sdwmalone 109130394Sdwmalone/* 110130394Sdwmalone * Called when a process has entered a system call. nargs is the 111130394Sdwmalone * number of words, not number of arguments (a necessary distinction 112130394Sdwmalone * in some cases). Note that if the STOPEVENT() code in amd64/amd64/trap.c 113130394Sdwmalone * is ever changed these functions need to keep up. 114130394Sdwmalone */ 115130394Sdwmalone 116130394Sdwmalonevoid 117240005Szontamd64_syscall_entry(struct trussinfo *trussinfo, int nargs) 118240005Szont{ 119240005Szont struct ptrace_io_desc iorequest; 120240005Szont struct reg regs; 121240562Szont struct freebsd_syscall *fsc; 122240005Szont struct syscall *sc; 123240562Szont lwpid_t tid; 124240005Szont int i, reg, syscall_num; 125130394Sdwmalone 126240562Szont tid = trussinfo->curthread->tid; 127130394Sdwmalone 128240562Szont if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { 129240005Szont fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); 130240005Szont return; 131240005Szont } 132130394Sdwmalone 133240005Szont /* 134240005Szont * FreeBSD has two special kinds of system call redirctions -- 135240005Szont * SYS_syscall, and SYS___syscall. The former is the old syscall() 136240005Szont * routine, basically; the latter is for quad-aligned arguments. 137240005Szont */ 138240005Szont reg = 0; 139240005Szont syscall_num = regs.r_rax; 140240005Szont switch (syscall_num) { 141240005Szont case SYS_syscall: 142240005Szont case SYS___syscall: 143240005Szont syscall_num = regs.r_rdi; 144240005Szont reg++; 145240005Szont break; 146240005Szont } 147130394Sdwmalone 148240562Szont fsc = alloc_fsc(); 149240562Szont if (fsc == NULL) 150240562Szont return; 151240562Szont fsc->number = syscall_num; 152240562Szont fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? 153240005Szont NULL : syscallnames[syscall_num]; 154240562Szont if (!fsc->name) { 155240005Szont fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", 156240005Szont syscall_num); 157240005Szont } 158130394Sdwmalone 159240562Szont if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && 160240562Szont (strcmp(fsc->name, "fork") == 0 || 161240562Szont strcmp(fsc->name, "rfork") == 0 || 162240562Szont strcmp(fsc->name, "vfork") == 0)) 163240005Szont trussinfo->curthread->in_fork = 1; 164130394Sdwmalone 165240005Szont if (nargs == 0) 166240005Szont return; 167130394Sdwmalone 168240562Szont fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); 169240005Szont for (i = 0; i < nargs && reg < 6; i++, reg++) { 170240005Szont switch (reg) { 171240562Szont case 0: fsc->args[i] = regs.r_rdi; break; 172240562Szont case 1: fsc->args[i] = regs.r_rsi; break; 173240562Szont case 2: fsc->args[i] = regs.r_rdx; break; 174240562Szont case 3: fsc->args[i] = regs.r_rcx; break; 175240562Szont case 4: fsc->args[i] = regs.r_r8; break; 176240562Szont case 5: fsc->args[i] = regs.r_r9; break; 177240005Szont } 178240005Szont } 179240005Szont if (nargs > i) { 180240005Szont iorequest.piod_op = PIOD_READ_D; 181240005Szont iorequest.piod_offs = (void *)(regs.r_rsp + sizeof(register_t)); 182240562Szont iorequest.piod_addr = &fsc->args[i]; 183240005Szont iorequest.piod_len = (nargs - i) * sizeof(register_t); 184240562Szont ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); 185240005Szont if (iorequest.piod_len == 0) 186240005Szont return; 187240005Szont } 188240005Szont 189240562Szont sc = get_syscall(fsc->name); 190240005Szont if (sc) 191240562Szont fsc->nargs = sc->nargs; 192240005Szont else { 193130394Sdwmalone#if DEBUG 194240005Szont fprintf(trussinfo->outfile, "unknown syscall %s -- setting " 195240562Szont "args to %d\n", fsc->name, nargs); 196130394Sdwmalone#endif 197240562Szont fsc->nargs = nargs; 198240005Szont } 199130394Sdwmalone 200240562Szont fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); 201240562Szont fsc->sc = sc; 202130394Sdwmalone 203240005Szont /* 204240005Szont * At this point, we set up the system call arguments. 205240005Szont * We ignore any OUT ones, however -- those are arguments that 206240005Szont * are set by the system call, and so are probably meaningless 207240005Szont * now. This doesn't currently support arguments that are 208240005Szont * passed in *and* out, however. 209240005Szont */ 210130394Sdwmalone 211240562Szont if (fsc->name) { 212130394Sdwmalone#if DEBUG 213240562Szont fprintf(stderr, "syscall %s(", fsc->name); 214130394Sdwmalone#endif 215240562Szont for (i = 0; i < fsc->nargs; i++) { 216130394Sdwmalone#if DEBUG 217240005Szont fprintf(stderr, "0x%lx%s", sc ? 218240562Szont fsc->args[sc->args[i].offset] : fsc->args[i], 219240562Szont i < (fsc->nargs - 1) ? "," : ""); 220130394Sdwmalone#endif 221240005Szont if (sc && !(sc->args[i].type & OUT)) { 222240562Szont fsc->s_args[i] = print_arg(&sc->args[i], 223240562Szont fsc->args, 0, trussinfo); 224240005Szont } 225240005Szont } 226130394Sdwmalone#if DEBUG 227240005Szont fprintf(stderr, ")\n"); 228130394Sdwmalone#endif 229240005Szont } 230130394Sdwmalone 231130394Sdwmalone#if DEBUG 232240005Szont fprintf(trussinfo->outfile, "\n"); 233130394Sdwmalone#endif 234130394Sdwmalone 235240562Szont if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || 236240562Szont strcmp(fsc->name, "exit") == 0)) { 237240005Szont /* 238240005Szont * XXX 239240005Szont * This could be done in a more general 240240005Szont * manner but it still wouldn't be very pretty. 241240005Szont */ 242240562Szont if (strcmp(fsc->name, "execve") == 0) { 243240005Szont if ((trussinfo->flags & EXECVEARGS) == 0) { 244240562Szont if (fsc->s_args[1]) { 245240562Szont free(fsc->s_args[1]); 246240562Szont fsc->s_args[1] = NULL; 247240005Szont } 248240005Szont } 249240005Szont if ((trussinfo->flags & EXECVEENVS) == 0) { 250240562Szont if (fsc->s_args[2]) { 251240562Szont free(fsc->s_args[2]); 252240562Szont fsc->s_args[2] = NULL; 253240005Szont } 254240005Szont } 255240005Szont } 256240005Szont } 257240562Szont trussinfo->curthread->fsc = fsc; 258130394Sdwmalone} 259130394Sdwmalone 260130394Sdwmalone/* 261130394Sdwmalone * And when the system call is done, we handle it here. 262130394Sdwmalone * Currently, no attempt is made to ensure that the system calls 263130394Sdwmalone * match -- this needs to be fixed (and is, in fact, why S_SCX includes 264213799Sbcr * the system call number instead of, say, an error status). 265130394Sdwmalone */ 266130394Sdwmalone 267130394Sdwmalonelong 268130394Sdwmaloneamd64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) 269130394Sdwmalone{ 270240005Szont struct reg regs; 271240562Szont struct freebsd_syscall *fsc; 272240005Szont struct syscall *sc; 273240562Szont lwpid_t tid; 274240005Szont long retval; 275240005Szont int errorp, i; 276130394Sdwmalone 277240562Szont if (trussinfo->curthread->fsc == NULL) 278240005Szont return (-1); 279171055Sdelphij 280240562Szont tid = trussinfo->curthread->tid; 281130394Sdwmalone 282240562Szont if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { 283240005Szont fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); 284240005Szont return (-1); 285240005Szont } 286130394Sdwmalone 287240005Szont retval = regs.r_rax; 288240005Szont errorp = !!(regs.r_rflags & PSL_C); 289130394Sdwmalone 290130394Sdwmalone /* 291240005Szont * This code, while simpler than the initial versions I used, could 292240005Szont * stand some significant cleaning. 293130394Sdwmalone */ 294130394Sdwmalone 295240562Szont fsc = trussinfo->curthread->fsc; 296240562Szont sc = fsc->sc; 297240005Szont if (!sc) { 298240562Szont for (i = 0; i < fsc->nargs; i++) 299240562Szont asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); 300240005Szont } else { 301240005Szont /* 302240005Szont * Here, we only look for arguments that have OUT masked in -- 303240005Szont * otherwise, they were handled in the syscall_entry function. 304240005Szont */ 305240005Szont for (i = 0; i < sc->nargs; i++) { 306240005Szont char *temp; 307240005Szont if (sc->args[i].type & OUT) { 308240005Szont /* 309240005Szont * If an error occurred, then don't bother 310240005Szont * getting the data; it may not be valid. 311240005Szont */ 312240005Szont if (errorp) { 313240005Szont asprintf(&temp, "0x%lx", 314240562Szont fsc->args[sc->args[i].offset]); 315240005Szont } else { 316240005Szont temp = print_arg(&sc->args[i], 317240562Szont fsc->args, retval, trussinfo); 318240005Szont } 319240562Szont fsc->s_args[i] = temp; 320240005Szont } 321240005Szont } 322240005Szont } 323171055Sdelphij 324240562Szont if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || 325240562Szont strcmp(fsc->name, "exit") == 0)) 326240005Szont trussinfo->curthread->in_syscall = 1; 327130394Sdwmalone 328240005Szont /* 329240005Szont * It would probably be a good idea to merge the error handling, 330240005Szont * but that complicates things considerably. 331240005Szont */ 332130394Sdwmalone 333240562Szont print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, 334240562Szont retval, fsc->sc); 335240562Szont free_fsc(fsc); 336240005Szont 337240005Szont return (retval); 338130394Sdwmalone} 339