i386-fbsd.c revision 31567
11558Srgrimes/* 244317Sjkh * FreeBSD/386-specific system call handling. This is probably the most 31558Srgrimes * complex part of the entire truss program, although I've got lots of 412481Speter * it handled relatively cleanly now. The system call names are generated 51558Srgrimes * automatically, thanks to /usr/src/sys/kern/syscalls.master. The 641061Sbde * names used for the various structures are confusing, I sadly admit. 741061Sbde */ 839271Sphk/* 939255Sgibbs * $Id$ 1038653Sgpalmer */ 1138653Sgpalmer 1243859Sobrien#include <stdio.h> 1338653Sgpalmer#include <stdlib.h> 1438653Sgpalmer#include <string.h> 1538653Sgpalmer#include <errno.h> 1638653Sgpalmer#include <err.h> 1738653Sgpalmer#include <signal.h> 1838653Sgpalmer#include <fcntl.h> 1938653Sgpalmer#include <unistd.h> 2038653Sgpalmer#include <sys/ioctl.h> 2138653Sgpalmer#include <sys/pioctl.h> 2238653Sgpalmer#include <machine/reg.h> 2338653Sgpalmer#include <machine/psl.h> 2438653Sgpalmer#include <sys/syscall.h> 2538653Sgpalmer 2638653Sgpalmer#include "syscall.h" 2738653Sgpalmer 2838843Sjbstatic int fd = -1; 2938653Sgpalmerstatic int cpid = -1; 3038653Sgpalmerextern int Procfd; 3138653Sgpalmer 3238653Sgpalmerextern FILE *outfile; 3338653Sgpalmer#include "syscalls.h" 3438653Sgpalmer 3538653Sgpalmerstatic int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); 3638653Sgpalmer 3743557Ssemenu/* 3838653Sgpalmer * This is what this particular file uses to keep track of a system call. 3938653Sgpalmer * It is probably not quite sufficient -- I can probably use the same 4038653Sgpalmer * structure for the various syscall personalities, and I also probably 4138653Sgpalmer * need to nest system calls (for signal handlers). 4238653Sgpalmer * 4338653Sgpalmer * 'struct syscall' describes the system call; it may be NULL, however, 4438653Sgpalmer * if we don't know about this particular system call yet. 4538653Sgpalmer */ 4638653Sgpalmerstatic struct freebsd_syscall { 4738653Sgpalmer struct syscall *sc; 4838653Sgpalmer char *name; 4938653Sgpalmer int number; 5038653Sgpalmer unsigned long *args; 5138653Sgpalmer int nargs; /* number of arguments -- *not* number of words! */ 5238653Sgpalmer char **s_args; /* the printable arguments */ 5338653Sgpalmer} fsc; 5438653Sgpalmer 5538653Sgpalmer/* Clear up and free parts of the fsc structure. */ 5638653Sgpalmerstatic inline void 5738653Sgpalmerclear_fsc() { 5838653Sgpalmer if (fsc.args) { 5941061Sbde free(fsc.args); 6038653Sgpalmer } 6138653Sgpalmer if (fsc.s_args) { 6238653Sgpalmer int i; 6342117Ssos for (i = 0; i < fsc.nargs; i++) 6442117Ssos if (fsc.s_args[i]) 6510855Sjoerg free(fsc.s_args[i]); 6644317Sjkh free(fsc.s_args); 6744317Sjkh } 6844317Sjkh memset(&fsc, 0, sizeof(fsc)); 6944317Sjkh} 7038852Sjb 7138852Sjb/* 7238458Sjb * Called when a process has entered a system call. nargs is the 7338458Sjb * number of words, not number of arguments (a necessary distinction 741558Srgrimes * 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