i386-linux.c revision 101423
1181834Sroberto/* 2280849Scy * Copryight 1997 Sean Eric Fagan 3280849Scy * 4181834Sroberto * Redistribution and use in source and binary forms, with or without 5181834Sroberto * modification, are permitted provided that the following conditions 6181834Sroberto * are met: 7181834Sroberto * 1. Redistributions of source code must retain the above copyright 8181834Sroberto * notice, this list of conditions and the following disclaimer. 9280849Scy * 2. Redistributions in binary form must reproduce the above copyright 10280849Scy * notice, this list of conditions and the following disclaimer in the 11280849Scy * documentation and/or other materials provided with the distribution. 12181834Sroberto * 3. All advertising materials mentioning features or use of this software 13181834Sroberto * must display the following acknowledgement: 14280849Scy * This product includes software developed by Sean Eric Fagan 15280849Scy * 4. Neither the name of the author may be used to endorse or promote 16285169Scy * products derived from this software without specific prior written 17181834Sroberto * permission. 18280849Scy * 19280849Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20280849Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21181834Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22280849Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23280849Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24181834Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25280849Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26280849Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27181834Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28280849Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29181834Sroberto * SUCH DAMAGE. 30280849Scy */ 31280849Scy 32280849Scy#ifndef lint 33181834Srobertostatic const char rcsid[] = 34181834Sroberto "$FreeBSD: head/usr.bin/truss/i386-linux.c 101423 2002-08-06 12:46:14Z mdodd $"; 35280849Scy#endif /* not lint */ 36280849Scy 37280849Scy/* 38280849Scy * Linux/i386-specific system call handling. Given how much of this code 39181834Sroberto * is taken from the freebsd equivalent, I can probably put even more of 40280849Scy * it in support routines that can be used by any personality support. 41280849Scy */ 42280849Scy 43280849Scy#include <sys/types.h> 44181834Sroberto#include <sys/ioctl.h> 45280849Scy#include <sys/pioctl.h> 46280849Scy 47280849Scy#include <machine/reg.h> 48280849Scy#include <machine/psl.h> 49280849Scy 50181834Sroberto#include <errno.h> 51280849Scy#include <fcntl.h> 52280849Scy#include <signal.h> 53280849Scy#include <stdio.h> 54280849Scy#include <stdlib.h> 55181834Sroberto#include <string.h> 56280849Scy#include <time.h> 57280849Scy#include <unistd.h> 58280849Scy 59280849Scy#include "truss.h" 60280849Scy#include "syscall.h" 61181834Sroberto 62181834Srobertostatic int fd = -1; 63280849Scystatic int cpid = -1; 64181834Srobertoextern int Procfd; 65181834Sroberto 66181834Sroberto#include "linux_syscalls.h" 67280849Scy 68280849Scystatic int nsyscalls = 69181834Sroberto sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]); 70181834Sroberto 71181834Sroberto/* 72181834Sroberto * This is what this particular file uses to keep track of a system call. 73280849Scy * It is probably not quite sufficient -- I can probably use the same 74181834Sroberto * structure for the various syscall personalities, and I also probably 75181834Sroberto * need to nest system calls (for signal handlers). 76280849Scy * 77181834Sroberto * 'struct syscall' describes the system call; it may be NULL, however, 78280849Scy * if we don't know about this particular system call yet. 79181834Sroberto */ 80280849Scystatic struct linux_syscall { 81280849Scy struct syscall *sc; 82181834Sroberto char *name; 83181834Sroberto int number; 84181834Sroberto unsigned long args[5]; 85181834Sroberto int nargs; /* number of arguments -- *not* number of words! */ 86280849Scy char **s_args; /* the printable arguments */ 87181834Sroberto} fsc; 88181834Sroberto 89280849Scy/* Clear up and free parts of the fsc structure. */ 90181834Srobertostatic __inline void 91181834Srobertoclear_fsc() { 92181834Sroberto if (fsc.s_args) { 93280849Scy int i; 94280849Scy for (i = 0; i < fsc.nargs; i++) 95181834Sroberto if (fsc.s_args[i]) 96181834Sroberto free(fsc.s_args[i]); 97181834Sroberto free(fsc.s_args); 98181834Sroberto } 99280849Scy memset(&fsc, 0, sizeof(fsc)); 100181834Sroberto} 101280849Scy 102280849Scy/* 103181834Sroberto * Called when a process has entered a system call. nargs is the 104181834Sroberto * number of words, not number of arguments (a necessary distinction 105181834Sroberto * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c 106181834Sroberto * is ever changed these functions need to keep up. 107181834Sroberto */ 108280849Scy 109181834Srobertovoid 110181834Srobertoi386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs) { 111181834Sroberto char buf[32]; 112181834Sroberto struct reg regs = { 0 }; 113181834Sroberto int syscall; 114280849Scy int i; 115280849Scy struct syscall *sc; 116280849Scy 117280849Scy if (fd == -1 || trussinfo->pid != cpid) { 118181834Sroberto sprintf(buf, "/proc/%d/regs", trussinfo->pid); 119280849Scy fd = open(buf, O_RDWR); 120181834Sroberto if (fd == -1) { 121181834Sroberto fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); 122181834Sroberto return; 123181834Sroberto } 124181834Sroberto cpid = trussinfo->pid; 125280849Scy } 126280849Scy 127181834Sroberto clear_fsc(); 128181834Sroberto lseek(fd, 0L, 0); 129181834Sroberto i = read(fd, ®s, sizeof(regs)); 130181834Sroberto syscall = regs.r_eax; 131181834Sroberto 132181834Sroberto fsc.number = syscall; 133181834Sroberto fsc.name = 134280849Scy (syscall < 0 || syscall > nsyscalls) ? NULL : linux_syscallnames[syscall]; 135181834Sroberto if (!fsc.name) { 136181834Sroberto fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall); 137181834Sroberto } 138280849Scy 139280849Scy if (fsc.name && (trussinfo->flags & FOLLOWFORKS) 140181834Sroberto && ((!strcmp(fsc.name, "linux_fork") 141181834Sroberto || !strcmp(fsc.name, "linux_vfork")))) 142181834Sroberto { 143181834Sroberto trussinfo->in_fork = 1; 144181834Sroberto } 145181834Sroberto 146280849Scy if (nargs == 0) 147181834Sroberto return; 148181834Sroberto 149181834Sroberto /* 150181834Sroberto * Linux passes syscall arguments in registers, not 151181834Sroberto * on the stack. Fortunately, we've got access to the 152181834Sroberto * register set. Note that we don't bother checking the 153181834Sroberto * number of arguments. And what does linux do for syscalls 154181834Sroberto * that have more than five arguments? 155280849Scy */ 156280849Scy 157280849Scy fsc.args[0] = regs.r_ebx; 158181834Sroberto fsc.args[1] = regs.r_ecx; 159181834Sroberto fsc.args[2] = regs.r_edx; 160181834Sroberto fsc.args[3] = regs.r_esi; 161181834Sroberto fsc.args[4] = regs.r_edi; 162181834Sroberto 163181834Sroberto sc = get_syscall(fsc.name); 164181834Sroberto if (sc) { 165181834Sroberto fsc.nargs = sc->nargs; 166181834Sroberto } else { 167280849Scy#if DEBUG 168181834Sroberto fprintf(trussinfo->outfile, "unknown syscall %s -- setting args to %d\n", 169181834Sroberto fsc.name, nargs); 170280849Scy#endif 171181834Sroberto fsc.nargs = nargs; 172181834Sroberto } 173181834Sroberto 174181834Sroberto fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*)); 175280849Scy memset(fsc.s_args, 0, fsc.nargs * sizeof(char*)); 176280849Scy fsc.sc = sc; 177280849Scy 178181834Sroberto /* 179181834Sroberto * At this point, we set up the system call arguments. 180181834Sroberto * We ignore any OUT ones, however -- those are arguments that 181181834Sroberto * are set by the system call, and so are probably meaningless 182280849Scy * now. This doesn't currently support arguments that are 183181834Sroberto * passed in *and* out, however. 184280849Scy */ 185280849Scy 186181834Sroberto if (fsc.name) { 187181834Sroberto 188280849Scy#if DEBUG 189280849Scy fprintf(stderr, "syscall %s(", fsc.name); 190181834Sroberto#endif 191181834Sroberto for (i = 0; i < fsc.nargs; i++) { 192280849Scy#if DEBUG 193280849Scy fprintf(stderr, "0x%x%s", 194181834Sroberto sc 195181834Sroberto ? fsc.args[sc->args[i].offset] 196181834Sroberto : fsc.args[i], 197181834Sroberto i < (fsc.nargs - 1) ? "," : ""); 198181834Sroberto#endif 199280849Scy if (sc && !(sc->args[i].type & OUT)) { 200280849Scy fsc.s_args[i] = print_arg(Procfd, &sc->args[i], fsc.args); 201280849Scy } 202181834Sroberto } 203181834Sroberto#if DEBUG 204181834Sroberto fprintf(stderr, ")\n"); 205280849Scy#endif 206280849Scy } 207181834Sroberto 208181834Sroberto#if DEBUG 209181834Sroberto fprintf(trussinfo->outfile, "\n"); 210181834Sroberto#endif 211280849Scy 212181834Sroberto /* 213181834Sroberto * Some system calls should be printed out before they are done -- 214181834Sroberto * execve() and exit(), for example, never return. Possibly change 215181834Sroberto * this to work for any system call that doesn't have an OUT 216280849Scy * parameter? 217280849Scy */ 218280849Scy 219181834Sroberto if (!strcmp(fsc.name, "linux_execve") || !strcmp(fsc.name, "exit")) { 220280849Scy 221280849Scy /* XXX 222181834Sroberto * This could be done in a more general 223280849Scy * manner but it still wouldn't be very pretty. 224280849Scy */ 225280849Scy if (!strcmp(fsc.name, "linux_execve")) { 226280849Scy if ((trussinfo->flags & EXECVEARGS) == 0) 227280849Scy if (fsc.s_args[1]) { 228181834Sroberto free(fsc.s_args[1]); 229280849Scy fsc.s_args[1] = NULL; 230280849Scy } 231280849Scy if ((trussinfo->flags & EXECVEENVS) == 0) 232280849Scy if (fsc.s_args[2]) { 233181834Sroberto free(fsc.s_args[2]); 234181834Sroberto fsc.s_args[2] = NULL; 235280849Scy } 236181834Sroberto } 237181834Sroberto 238280849Scy print_syscall(trussinfo, fsc.name, fsc.nargs, fsc.s_args); 239280849Scy fprintf(trussinfo->outfile, "\n"); 240280849Scy } 241280849Scy 242280849Scy return; 243181834Sroberto} 244280849Scy 245280849Scy/* 246181834Sroberto * Linux syscalls return negative errno's, we do positive and map them 247181834Sroberto */ 248280849Scyconst int bsd_to_linux_errno[] = { 249181834Sroberto -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, 250280849Scy -10, -35, -12, -13, -14, -15, -16, -17, -18, -19, 251181834Sroberto -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, 252280849Scy -30, -31, -32, -33, -34, -11,-115,-114, -88, -89, 253181834Sroberto -90, -91, -92, -93, -94, -95, -96, -97, -98, -99, 254280849Scy -100,-101,-102,-103,-104,-105,-106,-107,-108,-109, 255280849Scy -110,-111, -40, -36,-112,-113, -39, -11, -87,-122, 256181834Sroberto -116, -66, -6, -6, -6, -6, -6, -37, -38, -9, 257181834Sroberto -6, 258181834Sroberto}; 259181834Sroberto 260181834Srobertoint 261280849Scyi386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall) { 262181834Sroberto char buf[32]; 263181834Sroberto struct reg regs; 264280849Scy int retval; 265280849Scy int i; 266181834Sroberto int errorp; 267280849Scy struct syscall *sc; 268181834Sroberto 269181834Sroberto if (fd == -1 || trussinfo->pid != cpid) { 270280849Scy sprintf(buf, "/proc/%d/regs", trussinfo->pid); 271181834Sroberto fd = open(buf, O_RDONLY); 272280849Scy if (fd == -1) { 273181834Sroberto fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); 274280849Scy return; 275280849Scy } 276181834Sroberto cpid = trussinfo->pid; 277280849Scy } 278181834Sroberto 279181834Sroberto lseek(fd, 0L, 0); 280181834Sroberto if (read(fd, ®s, sizeof(regs)) != sizeof(regs)) { 281181834Sroberto fprintf(trussinfo->outfile, "\n"); 282181834Sroberto return; 283181834Sroberto } 284181834Sroberto retval = regs.r_eax; 285181834Sroberto errorp = !!(regs.r_eflags & PSL_C); 286181834Sroberto 287181834Sroberto /* 288181834Sroberto * This code, while simpler than the initial versions I used, could 289181834Sroberto * stand some significant cleaning. 290181834Sroberto */ 291181834Sroberto 292181834Sroberto sc = fsc.sc; 293181834Sroberto if (!sc) { 294181834Sroberto for (i = 0; i < fsc.nargs; i++) { 295181834Sroberto fsc.s_args[i] = malloc(12); 296181834Sroberto sprintf(fsc.s_args[i], "0x%lx", fsc.args[i]); 297181834Sroberto } 298181834Sroberto } else { 299181834Sroberto /* 300181834Sroberto * Here, we only look for arguments that have OUT masked in -- 301181834Sroberto * otherwise, they were handled in the syscall_entry function. 302285169Scy */ 303285169Scy for (i = 0; i < sc->nargs; i++) { 304285169Scy char *temp; 305181834Sroberto if (sc->args[i].type & OUT) { 306181834Sroberto /* 307181834Sroberto * If an error occurred, than don't bothe getting the data; 308181834Sroberto * it may not be valid. 309181834Sroberto */ 310181834Sroberto if (errorp) { 311181834Sroberto temp = malloc(12); 312181834Sroberto sprintf(temp, "0x%lx", fsc.args[sc->args[i].offset]); 313181834Sroberto } else { 314181834Sroberto temp = print_arg(Procfd, &sc->args[i], fsc.args); 315181834Sroberto } 316181834Sroberto fsc.s_args[i] = temp; 317181834Sroberto } 318181834Sroberto } 319181834Sroberto } 320181834Sroberto 321181834Sroberto /* 322181834Sroberto * It would probably be a good idea to merge the error handling, 323181834Sroberto * but that complicates things considerably. 324181834Sroberto */ 325181834Sroberto if (errorp) { 326181834Sroberto for (i = 0; i < sizeof(bsd_to_linux_errno) / sizeof(int); i++) 327181834Sroberto if (retval == bsd_to_linux_errno[i]) 328181834Sroberto break; 329181834Sroberto } 330181834Sroberto print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, 331181834Sroberto errorp ? i : retval); 332181834Sroberto clear_fsc(); 333280849Scy 334181834Sroberto return (retval); 335280849Scy} 336280849Scy