i386-linux.c revision 225736
1218887Sdim/* 2218887Sdim * Copyright 1997 Sean Eric Fagan 3218887Sdim * 4218887Sdim * Redistribution and use in source and binary forms, with or without 5218887Sdim * modification, are permitted provided that the following conditions 6218887Sdim * are met: 7218887Sdim * 1. Redistributions of source code must retain the above copyright 8218887Sdim * notice, this list of conditions and the following disclaimer. 9218887Sdim * 2. Redistributions in binary form must reproduce the above copyright 10218887Sdim * notice, this list of conditions and the following disclaimer in the 11218887Sdim * documentation and/or other materials provided with the distribution. 12218887Sdim * 3. All advertising materials mentioning features or use of this software 13218887Sdim * must display the following acknowledgement: 14249423Sdim * This product includes software developed by Sean Eric Fagan 15243830Sdim * 4. Neither the name of the author may be used to endorse or promote 16218887Sdim * products derived from this software without specific prior written 17243830Sdim * permission. 18218887Sdim * 19249423Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20249423Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21218887Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22218887Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23249423Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24249423Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25218887Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26218887Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27218887Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28218887Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29218887Sdim * SUCH DAMAGE. 30218887Sdim */ 31218887Sdim 32226633Sdim#ifndef lint 33218887Sdimstatic const char rcsid[] = 34218887Sdim "$FreeBSD: stable/9/usr.bin/truss/i386-linux.c 204977 2010-03-10 20:31:30Z imp $"; 35234353Sdim#endif /* not lint */ 36218887Sdim 37249423Sdim/* 38249423Sdim * Linux/i386-specific system call handling. Given how much of this code 39249423Sdim * is taken from the freebsd equivalent, I can probably put even more of 40239462Sdim * it in support routines that can be used by any personality support. 41218887Sdim */ 42234353Sdim 43218887Sdim#include <sys/types.h> 44234353Sdim#include <sys/ptrace.h> 45239462Sdim 46218887Sdim#include <machine/reg.h> 47226633Sdim#include <machine/psl.h> 48218887Sdim 49218887Sdim#include <errno.h> 50218887Sdim#include <fcntl.h> 51239462Sdim#include <signal.h> 52218887Sdim#include <stdio.h> 53234353Sdim#include <stdlib.h> 54234353Sdim#include <string.h> 55234353Sdim#include <time.h> 56218887Sdim#include <unistd.h> 57218887Sdim 58218887Sdim#include "truss.h" 59249423Sdim#include "syscall.h" 60249423Sdim#include "extern.h" 61218887Sdim 62239462Sdimstatic int cpid = -1; 63249423Sdim 64249423Sdim#include "linux_syscalls.h" 65234353Sdim 66218887Sdimstatic int nsyscalls = 67249423Sdim sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]); 68249423Sdim 69239462Sdim/* 70239462Sdim * This is what this particular file uses to keep track of a system call. 71249423Sdim * It is probably not quite sufficient -- I can probably use the same 72249423Sdim * structure for the various syscall personalities, and I also probably 73218887Sdim * need to nest system calls (for signal handlers). 74218887Sdim * 75249423Sdim * 'struct syscall' describes the system call; it may be NULL, however, 76249423Sdim * if we don't know about this particular system call yet. 77239462Sdim */ 78239462Sdimstatic struct linux_syscall { 79249423Sdim struct syscall *sc; 80249423Sdim const char *name; 81234353Sdim int number; 82234353Sdim unsigned long args[5]; 83226633Sdim int nargs; /* number of arguments -- *not* number of words! */ 84218887Sdim char **s_args; /* the printable arguments */ 85218887Sdim} fsc; 86226633Sdim 87218887Sdim/* Clear up and free parts of the fsc structure. */ 88218887Sdimstatic __inline void 89218887Sdimclear_fsc(void) { 90218887Sdim if (fsc.s_args) { 91218887Sdim int i; 92218887Sdim for (i = 0; i < fsc.nargs; i++) 93218887Sdim if (fsc.s_args[i]) 94218887Sdim free(fsc.s_args[i]); 95226633Sdim free(fsc.s_args); 96218887Sdim } 97218887Sdim memset(&fsc, 0, sizeof(fsc)); 98218887Sdim} 99218887Sdim 100218887Sdim/* 101226633Sdim * Called when a process has entered a system call. nargs is the 102218887Sdim * number of words, not number of arguments (a necessary distinction 103218887Sdim * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c 104218887Sdim * is ever changed these functions need to keep up. 105218887Sdim */ 106226633Sdim 107218887Sdimvoid 108218887Sdimi386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs) { 109218887Sdim struct reg regs; 110218887Sdim int syscall_num; 111226633Sdim int i; 112218887Sdim struct syscall *sc; 113218887Sdim 114218887Sdim cpid = trussinfo->curthread->tid; 115218887Sdim 116218887Sdim clear_fsc(); 117218887Sdim 118218887Sdim if (ptrace(PT_GETREGS, cpid, (caddr_t)®s, 0) < 0) 119226633Sdim { 120218887Sdim fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); 121226633Sdim return; 122218887Sdim } 123218887Sdim syscall_num = regs.r_eax; 124218887Sdim 125218887Sdim fsc.number = syscall_num; 126218887Sdim fsc.name = 127226633Sdim (syscall_num < 0 || syscall_num >= nsyscalls) ? NULL : linux_syscallnames[syscall_num]; 128218887Sdim if (!fsc.name) { 129218887Sdim fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall_num); 130218887Sdim } 131218887Sdim 132218887Sdim if (fsc.name && (trussinfo->flags & FOLLOWFORKS) 133218887Sdim && ((!strcmp(fsc.name, "linux_fork") 134226633Sdim || !strcmp(fsc.name, "linux_vfork")))) 135218887Sdim { 136218887Sdim trussinfo->curthread->in_fork = 1; 137218887Sdim } 138218887Sdim 139218887Sdim if (nargs == 0) 140218887Sdim return; 141218887Sdim 142218887Sdim /* 143218887Sdim * Linux passes syscall arguments in registers, not 144234353Sdim * on the stack. Fortunately, we've got access to the 145218887Sdim * register set. Note that we don't bother checking the 146234353Sdim * number of arguments. And what does linux do for syscalls 147218887Sdim * that have more than five arguments? 148218887Sdim */ 149218887Sdim 150218887Sdim fsc.args[0] = regs.r_ebx; 151218887Sdim fsc.args[1] = regs.r_ecx; 152218887Sdim fsc.args[2] = regs.r_edx; 153218887Sdim fsc.args[3] = regs.r_esi; 154218887Sdim fsc.args[4] = regs.r_edi; 155218887Sdim 156218887Sdim sc = get_syscall(fsc.name); 157218887Sdim if (sc) { 158218887Sdim fsc.nargs = sc->nargs; 159218887Sdim } else { 160218887Sdim#if DEBUG 161226633Sdim fprintf(trussinfo->outfile, "unknown syscall %s -- setting args to %d\n", 162218887Sdim fsc.name, nargs); 163218887Sdim#endif 164218887Sdim fsc.nargs = nargs; 165218887Sdim } 166218887Sdim 167218887Sdim fsc.s_args = calloc(1, (1+fsc.nargs) * sizeof(char*)); 168218887Sdim fsc.sc = sc; 169218887Sdim 170218887Sdim /* 171218887Sdim * At this point, we set up the system call arguments. 172218887Sdim * We ignore any OUT ones, however -- those are arguments that 173218887Sdim * are set by the system call, and so are probably meaningless 174218887Sdim * now. This doesn't currently support arguments that are 175218887Sdim * passed in *and* out, however. 176218887Sdim */ 177218887Sdim 178218887Sdim if (fsc.name) { 179218887Sdim 180218887Sdim#if DEBUG 181218887Sdim fprintf(stderr, "syscall %s(", fsc.name); 182239462Sdim#endif 183239462Sdim for (i = 0; i < fsc.nargs; i++) { 184239462Sdim#if DEBUG 185239462Sdim fprintf(stderr, "0x%x%s", 186218887Sdim sc 187239462Sdim ? fsc.args[sc->args[i].offset] 188239462Sdim : fsc.args[i], 189239462Sdim i < (fsc.nargs - 1) ? "," : ""); 190218887Sdim#endif 191239462Sdim if (sc && !(sc->args[i].type & OUT)) { 192239462Sdim fsc.s_args[i] = print_arg(&sc->args[i], fsc.args, 0, trussinfo); 193239462Sdim } 194218887Sdim } 195218887Sdim#if DEBUG 196218887Sdim fprintf(stderr, ")\n"); 197218887Sdim#endif 198218887Sdim } 199218887Sdim 200218887Sdim#if DEBUG 201218887Sdim fprintf(trussinfo->outfile, "\n"); 202218887Sdim#endif 203218887Sdim 204218887Sdim if (fsc.name != NULL && 205218887Sdim (!strcmp(fsc.name, "linux_execve") || !strcmp(fsc.name, "exit"))) { 206218887Sdim 207218887Sdim /* XXX 208218887Sdim * This could be done in a more general 209218887Sdim * manner but it still wouldn't be very pretty. 210218887Sdim */ 211218887Sdim if (!strcmp(fsc.name, "linux_execve")) { 212226633Sdim if ((trussinfo->flags & EXECVEARGS) == 0) 213218887Sdim if (fsc.s_args[1]) { 214218887Sdim free(fsc.s_args[1]); 215218887Sdim fsc.s_args[1] = NULL; 216234353Sdim } 217263508Sdim if ((trussinfo->flags & EXECVEENVS) == 0) 218263508Sdim if (fsc.s_args[2]) { 219218887Sdim free(fsc.s_args[2]); 220218887Sdim fsc.s_args[2] = NULL; 221218887Sdim } 222218887Sdim } 223218887Sdim } 224218887Sdim 225263508Sdim return; 226263508Sdim} 227263508Sdim 228263508Sdim/* 229218887Sdim * Linux syscalls return negative errno's, we do positive and map them 230218887Sdim */ 231218887Sdimconst int bsd_to_linux_errno[] = { 232218887Sdim -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, 233218887Sdim -10, -35, -12, -13, -14, -15, -16, -17, -18, -19, 234218887Sdim -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, 235218887Sdim -30, -31, -32, -33, -34, -11,-115,-114, -88, -89, 236239462Sdim -90, -91, -92, -93, -94, -95, -96, -97, -98, -99, 237218887Sdim -100,-101,-102,-103,-104,-105,-106,-107,-108,-109, 238239462Sdim -110,-111, -40, -36,-112,-113, -39, -11, -87,-122, 239218887Sdim -116, -66, -6, -6, -6, -6, -6, -37, -38, -9, 240218887Sdim -6, 241218887Sdim}; 242239462Sdim 243239462Sdimlong 244239462Sdimi386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) 245239462Sdim{ 246218887Sdim struct reg regs; 247218887Sdim long retval; 248218887Sdim int i; 249234353Sdim int errorp; 250234353Sdim struct syscall *sc; 251234353Sdim 252234353Sdim if (fsc.name == NULL) 253218887Sdim return (-1); 254218887Sdim 255218887Sdim cpid = trussinfo->curthread->tid; 256218887Sdim if (ptrace(PT_GETREGS, cpid, (caddr_t)®s, 0) < 0) 257218887Sdim { 258218887Sdim fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); 259218887Sdim return (-1); 260218887Sdim } 261218887Sdim 262218887Sdim retval = regs.r_eax; 263243830Sdim errorp = !!(regs.r_eflags & PSL_C); 264218887Sdim 265234353Sdim /* 266218887Sdim * This code, while simpler than the initial versions I used, could 267218887Sdim * stand some significant cleaning. 268218887Sdim */ 269218887Sdim 270218887Sdim sc = fsc.sc; 271234353Sdim if (!sc) { 272234353Sdim for (i = 0; i < fsc.nargs; i++) 273234353Sdim asprintf(&fsc.s_args[i], "0x%lx", fsc.args[i]); 274234353Sdim } else { 275234353Sdim /* 276234353Sdim * Here, we only look for arguments that have OUT masked in -- 277263508Sdim * otherwise, they were handled in the syscall_entry function. 278263508Sdim */ 279234353Sdim for (i = 0; i < sc->nargs; i++) { 280234353Sdim char *temp; 281234353Sdim if (sc->args[i].type & OUT) { 282234353Sdim /* 283234353Sdim * If an error occurred, than don't bothe getting the data; 284234353Sdim * it may not be valid. 285234353Sdim */ 286234353Sdim if (errorp) 287234353Sdim asprintf(&temp, "0x%lx", fsc.args[sc->args[i].offset]); 288234353Sdim else 289234353Sdim temp = print_arg(&sc->args[i], fsc.args, retval, trussinfo); 290234353Sdim fsc.s_args[i] = temp; 291263508Sdim } 292263508Sdim } 293234353Sdim } 294234353Sdim 295234353Sdim /* 296234353Sdim * It would probably be a good idea to merge the error handling, 297234353Sdim * but that complicates things considerably. 298234353Sdim */ 299234353Sdim if (errorp) { 300234353Sdim for (i = 0; (size_t)i < sizeof(bsd_to_linux_errno) / sizeof(int); i++) 301234353Sdim if (retval == bsd_to_linux_errno[i]) 302234353Sdim break; 303234353Sdim } 304234353Sdim 305251662Sdim if (fsc.name != NULL && 306251662Sdim (!strcmp(fsc.name, "linux_execve") || !strcmp(fsc.name, "exit"))) { 307234353Sdim trussinfo->curthread->in_syscall = 1; 308234353Sdim } 309234353Sdim 310234353Sdim print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, 311234353Sdim errorp ? i : retval, fsc.sc); 312234353Sdim clear_fsc(); 313234353Sdim 314234353Sdim return (retval); 315226633Sdim} 316218887Sdim