i386-fbsd.c revision 31567
1/* 2 * FreeBSD/386-specific system call handling. This is probably the most 3 * complex part of the entire truss program, although I've got lots of 4 * it handled relatively cleanly now. The system call names are generated 5 * automatically, thanks to /usr/src/sys/kern/syscalls.master. The 6 * names used for the various structures are confusing, I sadly admit. 7 */ 8/* 9 * $Id$ 10 */ 11 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15#include <errno.h> 16#include <err.h> 17#include <signal.h> 18#include <fcntl.h> 19#include <unistd.h> 20#include <sys/ioctl.h> 21#include <sys/pioctl.h> 22#include <machine/reg.h> 23#include <machine/psl.h> 24#include <sys/syscall.h> 25 26#include "syscall.h" 27 28static int fd = -1; 29static int cpid = -1; 30extern int Procfd; 31 32extern FILE *outfile; 33#include "syscalls.h" 34 35static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); 36 37/* 38 * This is what this particular file uses to keep track of a system call. 39 * It is probably not quite sufficient -- I can probably use the same 40 * structure for the various syscall personalities, and I also probably 41 * need to nest system calls (for signal handlers). 42 * 43 * 'struct syscall' describes the system call; it may be NULL, however, 44 * if we don't know about this particular system call yet. 45 */ 46static struct freebsd_syscall { 47 struct syscall *sc; 48 char *name; 49 int number; 50 unsigned long *args; 51 int nargs; /* number of arguments -- *not* number of words! */ 52 char **s_args; /* the printable arguments */ 53} fsc; 54 55/* Clear up and free parts of the fsc structure. */ 56static inline void 57clear_fsc() { 58 if (fsc.args) { 59 free(fsc.args); 60 } 61 if (fsc.s_args) { 62 int i; 63 for (i = 0; i < fsc.nargs; i++) 64 if (fsc.s_args[i]) 65 free(fsc.s_args[i]); 66 free(fsc.s_args); 67 } 68 memset(&fsc, 0, sizeof(fsc)); 69} 70 71/* 72 * Called when a process has entered a system call. nargs is the 73 * number of words, not number of arguments (a necessary distinction 74 * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c 75 * is ever changed these functions need to keep up. 76 */ 77 78void 79i386_syscall_entry(int pid, int nargs) { 80 char buf[32]; 81 struct reg regs = { 0 }; 82 int syscall; 83 int i; 84 int memfd; 85 unsigned int parm_offset; 86 struct syscall *sc; 87 88 if (fd == -1 || pid != cpid) { 89 sprintf(buf, "/proc/%d/regs", pid); 90 fd = open(buf, O_RDWR); 91 if (fd == -1) { 92 fprintf(outfile, "-- CANNOT READ REGISTERS --\n"); 93 return; 94 } 95 cpid = pid; 96 } 97 98 clear_fsc(); 99 lseek(fd, 0L, 0); 100 i = read(fd, ®s, sizeof(regs)); 101 parm_offset = regs.r_esp + sizeof(int); 102 103 /* 104 * FreeBSD has two special kinds of system call redirctions -- 105 * SYS_syscall, and SYS___syscall. The former is the old syscall() 106 * routine, basicly; the latter is for quad-aligned arguments. 107 */ 108 syscall = regs.r_eax; 109 switch (syscall) { 110 case SYS_syscall: 111 lseek(Procfd, parm_offset, SEEK_SET); 112 read(Procfd, &syscall, sizeof(int)); 113 parm_offset += sizeof(int); 114 break; 115 case SYS___syscall: 116 lseek(Procfd, parm_offset, SEEK_SET); 117 read(Procfd, &syscall, sizeof(int)); 118 parm_offset += sizeof(quad_t); 119 break; 120 } 121 122 fsc.number = syscall; 123 fsc.name = 124 (syscall < 0 || syscall > nsyscalls) ? NULL : syscallnames[syscall]; 125 if (!fsc.name) { 126 fprintf(outfile, "-- UNKNOWN SYSCALL %d --\n", syscall); 127 } 128 129 if (nargs == 0) 130 return; 131 132 fsc.args = malloc((1+nargs) * sizeof(unsigned long)); 133 lseek(Procfd, parm_offset, SEEK_SET); 134 if (read(Procfd, fsc.args, nargs * sizeof(unsigned long)) == -1) 135 return; 136 137 sc = get_syscall(fsc.name); 138 if (sc) { 139 fsc.nargs = sc->nargs; 140 } else { 141#if DEBUG 142 fprintf(outfile, "unknown syscall %s -- setting args to %d\n", 143 fsc.name, nargs); 144#endif 145 fsc.nargs = nargs; 146 } 147 148 fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*)); 149 memset(fsc.s_args, 0, fsc.nargs * sizeof(char*)); 150 fsc.sc = sc; 151 152 /* 153 * At this point, we set up the system call arguments. 154 * We ignore any OUT ones, however -- those are arguments that 155 * are set by the system call, and so are probably meaningless 156 * now. This doesn't currently support arguments that are 157 * passed in *and* out, however. 158 */ 159 160 if (fsc.name) { 161 char *tmp; 162 163#if DEBUG 164 fprintf(stderr, "syscall %s(", fsc.name); 165#endif 166 for (i = 0; i < fsc.nargs; i++) { 167#if DEBUG 168 fprintf(stderr, "0x%x%s", 169 sc 170 ? fsc.args[sc->args[i].offset] 171 : fsc.args[i], 172 i < (fsc.nargs -1) ? "," : ""); 173#endif 174 if (sc && !(sc->args[i].type & OUT)) { 175 fsc.s_args[i] = print_arg(Procfd, &sc->args[i], fsc.args); 176 } 177 } 178#if DEBUG 179 fprintf(stderr, ")\n"); 180#endif 181 } 182 183#if DEBUG 184 fprintf(outfile, "\n"); 185#endif 186 187 /* 188 * Some system calls should be printed out before they are done -- 189 * execve() and exit(), for example, never return. Possibly change 190 * this to work for any system call that doesn't have an OUT 191 * parameter? 192 */ 193 194 if (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit")) { 195 print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args); 196 } 197 198 return; 199} 200 201/* 202 * And when the system call is done, we handle it here. 203 * Currently, no attempt is made to ensure that the system calls 204 * match -- this needs to be fixed (and is, in fact, why S_SCX includes 205 * the sytem call number instead of, say, an error status). 206 */ 207 208void 209i386_syscall_exit(int pid, int syscall) { 210 char buf[32]; 211 struct reg regs; 212 int retval; 213 int i; 214 int errorp; 215 struct syscall *sc; 216 217 if (fd == -1 || pid != cpid) { 218 sprintf(buf, "/proc/%d/regs", pid); 219 fd = open(buf, O_RDONLY); 220 if (fd == -1) { 221 fprintf(outfile, "-- CANNOT READ REGISTERS --\n"); 222 return; 223 } 224 cpid = pid; 225 } 226 227 lseek(fd, 0L, 0); 228 if (read(fd, ®s, sizeof(regs)) != sizeof(regs)) 229 return; 230 retval = regs.r_eax; 231 errorp = !!(regs.r_eflags & PSL_C); 232 233 /* 234 * This code, while simpler than the initial versions I used, could 235 * stand some significant cleaning. 236 */ 237 238 sc = fsc.sc; 239 if (!sc) { 240 for (i = 0; i < fsc.nargs; i++) { 241 fsc.s_args[i] = malloc(12); 242 sprintf(fsc.s_args[i], "0x%x", fsc.args[i]); 243 } 244 } else { 245 /* 246 * Here, we only look for arguments that have OUT masked in -- 247 * otherwise, they were handled in the syscall_entry function. 248 */ 249 for (i = 0; i < sc->nargs; i++) { 250 char *temp; 251 if (sc->args[i].type & OUT) { 252 /* 253 * If an error occurred, than don't bothe getting the data; 254 * it may not be valid. 255 */ 256 if (errorp) { 257 temp = malloc(12); 258 sprintf(temp, "0x%x", fsc.args[sc->args[i].offset]); 259 } else { 260 temp = print_arg(Procfd, &sc->args[i], fsc.args); 261 } 262 fsc.s_args[i] = temp; 263 } 264 } 265 } 266 267 /* 268 * It would probably be a good idea to merge the error handling, 269 * but that complicates things considerably. 270 */ 271 272 print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args); 273 if (errorp) { 274 fprintf(outfile, "errno %d '%s'\n", retval, strerror(retval)); 275 } else { 276 fprintf(outfile, "returns %d (0x%x)\n", retval, retval); 277 } 278 clear_fsc(); 279 280 return; 281} 282