i386-linux.c revision 101289
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 101289 2002-08-04 02:24:21Z mdodd $"; 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 "truss.h" 59#include "extern.h" 60#include "syscall.h" 61 62static int fd = -1; 63static int cpid = -1; 64extern int Procfd; 65 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(struct trussinfo *trussinfo, 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 || trussinfo->pid != cpid) { 102 sprintf(buf, "/proc/%d/regs", trussinfo->pid); 103 fd = open(buf, O_RDWR); 104 if (fd == -1) { 105 fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); 106 return; 107 } 108 cpid = trussinfo->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(trussinfo->outfile, "-- UNKNOWN SYSCALL %d\n", syscall); 121 } 122 123 if (lsc.name && (trussinfo->flags & FOLLOWFORKS) 124 && ((!strcmp(lsc.name, "linux_fork") 125 || !strcmp(lsc.name, "linux_vfork")))) 126 { 127 trussinfo->in_fork = 1; 128 } 129 130 if (nargs == 0) 131 return; 132 133 /* 134 * Linux passes syscall arguments in registers, not 135 * on the stack. Fortunately, we've got access to the 136 * register set. Note that we don't bother checking the 137 * number of arguments. And what does linux do for syscalls 138 * that have more than five arguments? 139 */ 140 141 lsc.args[0] = regs.r_ebx; 142 lsc.args[1] = regs.r_ecx; 143 lsc.args[2] = regs.r_edx; 144 lsc.args[3] = regs.r_esi; 145 lsc.args[4] = regs.r_edi; 146 147 sc = get_syscall(lsc.name); 148 if (sc) { 149 lsc.nargs = sc->nargs; 150 } else { 151#ifdef DEBUG 152 fprintf(trussinfo->outfile, "unknown syscall %s -- setting args to %d\n", 153 lsc.name, nargs); 154#endif 155 lsc.nargs = nargs; 156 } 157 158 lsc.s_args = malloc((1+lsc.nargs) * sizeof(char*)); 159 memset(lsc.s_args, 0, lsc.nargs * sizeof(char*)); 160 lsc.sc = sc; 161 162 if (lsc.name) { 163 164#ifdef DEBUG 165 fprintf(stderr, "syscall %s(", lsc.name); 166#endif 167 for (i = 0; i < lsc.nargs ; i++) { 168#ifdef DEBUG 169 fprintf(stderr, "0x%x%s", 170 sc ? 171 lsc.args[sc->args[i].offset] 172 : lsc.args[i], 173 i < (lsc.nargs - 1) ? "," : ""); 174#endif 175 if (sc && !(sc->args[i].type & OUT)) { 176 lsc.s_args[i] = print_arg(Procfd, &sc->args[i], lsc.args); 177 } 178 } 179#ifdef DEBUG 180 fprintf(stderr, ")\n"); 181#endif 182 } 183 184 if (!strcmp(lsc.name, "linux_execve") || !strcmp(lsc.name, "exit")) { 185 186 /* XXX 187 * This could be done in a more general 188 * manner but it still wouldn't be very pretty. 189 */ 190 if (!strcmp(lsc.name, "linux_execve")) { 191 if ((trussinfo->flags & EXECVEARGS) == 0) 192 if (lsc.s_args[1]) { 193 free(lsc.s_args[1]); 194 lsc.s_args[1] = NULL; 195 } 196 if ((trussinfo->flags & EXECVEENVS) == 0) 197 if (lsc.s_args[2]) { 198 free(lsc.s_args[2]); 199 lsc.s_args[2] = NULL; 200 } 201 } 202 203 print_syscall(trussinfo, lsc.name, lsc.nargs, lsc.s_args); 204 fprintf(trussinfo->outfile, "\n"); 205 } 206 207 return; 208} 209 210/* 211 * Linux syscalls return negative errno's, we do positive and map them 212 */ 213const int bsd_to_linux_errno[] = { 214 -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, 215 -10, -35, -12, -13, -14, -15, -16, -17, -18, -19, 216 -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, 217 -30, -31, -32, -33, -34, -11,-115,-114, -88, -89, 218 -90, -91, -92, -93, -94, -95, -96, -97, -98, -99, 219 -100,-101,-102,-103,-104,-105,-106,-107,-108,-109, 220 -110,-111, -40, -36,-112,-113, -39, -11, -87,-122, 221 -116, -66, -6, -6, -6, -6, -6, -37, -38, -9, 222 -6, 223}; 224 225int 226i386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall) { 227 char buf[32]; 228 struct reg regs; 229 int retval; 230 int i; 231 int errorp; 232 struct syscall *sc; 233 234 if (fd == -1 || trussinfo->pid != cpid) { 235 sprintf(buf, "/proc/%d/regs", trussinfo->pid); 236 fd = open(buf, O_RDONLY); 237 if (fd == -1) { 238 fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); 239 return; 240 } 241 cpid = trussinfo->pid; 242 } 243 244 lseek(fd, 0L, 0); 245 if (read(fd, ®s, sizeof(regs)) != sizeof(regs)) { 246 fprintf(trussinfo->outfile, "\n"); 247 return; 248 } 249 retval = regs.r_eax; 250 errorp = !!(regs.r_eflags & PSL_C); 251 252 sc = lsc.sc; 253 if (!sc) { 254 for (i = 0; i < lsc.nargs; i++) { 255 lsc.s_args[i] = malloc(12); 256 sprintf(lsc.s_args[i], "0x%lx", lsc.args[i]); 257 } 258 } else { 259 for (i = 0; i < sc->nargs; i++) { 260 char *temp; 261 if (sc->args[i].type & OUT) { 262 if (errorp) { 263 temp = malloc(12); 264 sprintf(temp, "0x%lx", lsc.args[sc->args[i].offset]); 265 } else { 266 temp = print_arg(Procfd, &sc->args[i], lsc.args); 267 } 268 lsc.s_args[i] = temp; 269 } 270 } 271 } 272 if (errorp) { 273 for (i = 0; i < sizeof(bsd_to_linux_errno) / sizeof(int); i++) 274 if (retval == bsd_to_linux_errno[i]) 275 break; 276 } 277 print_syscall_ret(trussinfo, lsc.name, lsc.nargs, lsc.s_args, errorp, 278 errorp ? i : retval); 279 clear_lsc(); 280 281 return (retval); 282} 283