i386-linux.c revision 87703
1/* 2 * Copryight 1997 Sean Eric Fagan 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by Sean Eric Fagan 15 * 4. Neither the name of the author may be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#ifndef lint 33static const char rcsid[] = 34 "$FreeBSD: head/usr.bin/truss/i386-linux.c 87703 2001-12-11 23:34:02Z markm $"; 35#endif /* not lint */ 36 37/* 38 * Linux/i386-specific system call handling. Given how much of this code 39 * is taken from the freebsd equivalent, I can probably put even more of 40 * it in support routines that can be used by any personality support. 41 */ 42 43#include <sys/types.h> 44#include <sys/ioctl.h> 45#include <sys/pioctl.h> 46 47#include <machine/reg.h> 48#include <machine/psl.h> 49 50#include <errno.h> 51#include <fcntl.h> 52#include <signal.h> 53#include <stdio.h> 54#include <stdlib.h> 55#include <string.h> 56#include <unistd.h> 57 58#include "extern.h" 59#include "syscall.h" 60 61static int fd = -1; 62static int cpid = -1; 63extern int Procfd; 64 65extern FILE *outfile; 66#include "linux_syscalls.h" 67 68static int nsyscalls = 69 sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]); 70 71/* See the comment in i386-fbsd.c about this structure. */ 72static struct linux_syscall { 73 struct syscall *sc; 74 char *name; 75 int number; 76 unsigned long args[5]; 77 int nargs; /* number of arguments -- *not* number of words! */ 78 char **s_args; /* the printable arguments */ 79} lsc; 80 81static inline void 82clear_lsc() { 83 if (lsc.s_args) { 84 int i; 85 for (i = 0; i < lsc.nargs; i++) 86 if (lsc.s_args[i]) 87 free(lsc.s_args[i]); 88 free(lsc.s_args); 89 } 90 memset(&lsc, 0, sizeof(lsc)); 91} 92 93void 94i386_linux_syscall_entry(int pid, int nargs) { 95 char buf[32]; 96 struct reg regs = { 0 }; 97 int syscall; 98 int i; 99 struct syscall *sc; 100 101 if (fd == -1 || pid != cpid) { 102 sprintf(buf, "/proc/%d/regs", pid); 103 fd = open(buf, O_RDWR); 104 if (fd == -1) { 105 fprintf(outfile, "-- CANNOT READ REGISTERS --\n"); 106 return; 107 } 108 cpid = pid; 109 } 110 111 clear_lsc(); 112 lseek(fd, 0L, 0); 113 i = read(fd, ®s, sizeof(regs)); 114 syscall = regs.r_eax; 115 116 lsc.number = syscall; 117 lsc.name = 118 (syscall < 0 || syscall > nsyscalls) ? NULL : linux_syscallnames[syscall]; 119 if (!lsc.name) { 120 fprintf (outfile, "-- UNKNOWN SYSCALL %d\n", syscall); 121 } 122 123 if (nargs == 0) 124 return; 125 126 /* 127 * Linux passes syscall arguments in registers, not 128 * on the stack. Fortunately, we've got access to the 129 * register set. Note that we don't bother checking the 130 * number of arguments. And what does linux do for syscalls 131 * that have more than five arguments? 132 */ 133 134 lsc.args[0] = regs.r_ebx; 135 lsc.args[1] = regs.r_ecx; 136 lsc.args[2] = regs.r_edx; 137 lsc.args[3] = regs.r_esi; 138 lsc.args[4] = regs.r_edi; 139 140 sc = get_syscall(lsc.name); 141 if (sc) { 142 lsc.nargs = sc->nargs; 143 } else { 144#ifdef DEBUG 145 fprintf(outfile, "unknown syscall %s -- setting args to %d\n", 146 lsc.name, nargs); 147#endif 148 lsc.nargs = nargs; 149 } 150 151 lsc.s_args = malloc((1+lsc.nargs) * sizeof(char*)); 152 memset(lsc.s_args, 0, lsc.nargs * sizeof(char*)); 153 lsc.sc = sc; 154 155 if (lsc.name) { 156 157#ifdef DEBUG 158 fprintf(stderr, "syscall %s(", lsc.name); 159#endif 160 for (i = 0; i < lsc.nargs ; i++) { 161#ifdef DEBUG 162 fprintf(stderr, "0x%x%s", 163 sc ? 164 lsc.args[sc->args[i].offset] 165 : lsc.args[i], 166 i < (lsc.nargs - 1) ? "," : ""); 167#endif 168 if (sc && !(sc->args[i].type & OUT)) { 169 lsc.s_args[i] = print_arg(Procfd, &sc->args[i], lsc.args); 170 } 171 } 172#ifdef DEBUG 173 fprintf(stderr, ")\n"); 174#endif 175 } 176 177 if (!strcmp(lsc.name, "linux_execve") || !strcmp(lsc.name, "exit")) { 178 print_syscall(outfile, lsc.name, lsc.nargs, lsc.s_args); 179 } 180 181 return; 182} 183 184/* 185 * Linux syscalls return negative errno's, we do positive and map them 186 */ 187const int bsd_to_linux_errno[] = { 188 -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, 189 -10, -35, -12, -13, -14, -15, -16, -17, -18, -19, 190 -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, 191 -30, -31, -32, -33, -34, -11,-115,-114, -88, -89, 192 -90, -91, -92, -93, -94, -95, -96, -97, -98, -99, 193 -100,-101,-102,-103,-104,-105,-106,-107,-108,-109, 194 -110,-111, -40, -36,-112,-113, -39, -11, -87,-122, 195 -116, -66, -6, -6, -6, -6, -6, -37, -38, -9, 196 -6, 197}; 198 199void 200i386_linux_syscall_exit(int pid, int syscall) { 201 char buf[32]; 202 struct reg regs; 203 int retval; 204 int i; 205 int errorp; 206 struct syscall *sc; 207 208 if (fd == -1 || pid != cpid) { 209 sprintf(buf, "/proc/%d/regs", pid); 210 fd = open(buf, O_RDONLY); 211 if (fd == -1) { 212 fprintf(outfile, "-- CANNOT READ REGISTERS --\n"); 213 return; 214 } 215 cpid = pid; 216 } 217 218 lseek(fd, 0L, 0); 219 if (read(fd, ®s, sizeof(regs)) != sizeof(regs)) 220 return; 221 222 retval = regs.r_eax; 223 errorp = !!(regs.r_eflags & PSL_C); 224 225 sc = lsc.sc; 226 if (!sc) { 227 for (i = 0; i < lsc.nargs; i++) { 228 lsc.s_args[i] = malloc(12); 229 sprintf(lsc.s_args[i], "0x%lx", lsc.args[i]); 230 } 231 } else { 232 for (i = 0; i < sc->nargs; i++) { 233 char *temp; 234 if (sc->args[i].type & OUT) { 235 if (errorp) { 236 temp = malloc(12); 237 sprintf(temp, "0x%lx", lsc.args[sc->args[i].offset]); 238 } else { 239 temp = print_arg(Procfd, &sc->args[i], lsc.args); 240 } 241 lsc.s_args[i] = temp; 242 } 243 } 244 } 245 if (errorp) { 246 for (i = 0; i < sizeof(bsd_to_linux_errno) / sizeof(int); i++) 247 if (retval == bsd_to_linux_errno[i]) 248 break; 249 } 250 print_syscall_ret(outfile, lsc.name, lsc.nargs, lsc.s_args, errorp, 251 errorp ? i : retval); 252 clear_lsc(); 253 return; 254} 255