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)®s, 0) < 0) { 130247975Scognet fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); 131247975Scognet return; 132247975Scognet } 133247975Scognet ap = ®s.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)®s, 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