/usr.bin/truss/i386-fbsd.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 332 lines · 200 code · 40 blank · 92 comment · 52 complexity · a3f847a6fd4ed2f6f5c68a6d41d4d155 MD5 · raw file

  1. /*
  2. * Copyright 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. #ifndef lint
  32. static const char rcsid[] =
  33. "$FreeBSD$";
  34. #endif /* not lint */
  35. /*
  36. * FreeBSD/i386-specific system call handling. This is probably the most
  37. * complex part of the entire truss program, although I've got lots of
  38. * it handled relatively cleanly now. The system call names are generated
  39. * automatically, thanks to /usr/src/sys/kern/syscalls.master. The
  40. * names used for the various structures are confusing, I sadly admit.
  41. */
  42. #include <sys/types.h>
  43. #include <sys/ptrace.h>
  44. #include <sys/syscall.h>
  45. #include <machine/reg.h>
  46. #include <machine/psl.h>
  47. #include <errno.h>
  48. #include <fcntl.h>
  49. #include <signal.h>
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52. #include <string.h>
  53. #include <time.h>
  54. #include <unistd.h>
  55. #include "truss.h"
  56. #include "syscall.h"
  57. #include "extern.h"
  58. #include "syscalls.h"
  59. static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]);
  60. /*
  61. * This is what this particular file uses to keep track of a system call.
  62. * It is probably not quite sufficient -- I can probably use the same
  63. * structure for the various syscall personalities, and I also probably
  64. * need to nest system calls (for signal handlers).
  65. *
  66. * 'struct syscall' describes the system call; it may be NULL, however,
  67. * if we don't know about this particular system call yet.
  68. */
  69. struct freebsd_syscall {
  70. struct syscall *sc;
  71. const char *name;
  72. int number;
  73. unsigned long *args;
  74. int nargs; /* number of arguments -- *not* number of words! */
  75. char **s_args; /* the printable arguments */
  76. };
  77. static struct freebsd_syscall *
  78. alloc_fsc(void)
  79. {
  80. return (malloc(sizeof(struct freebsd_syscall)));
  81. }
  82. /* Clear up and free parts of the fsc structure. */
  83. static void
  84. free_fsc(struct freebsd_syscall *fsc)
  85. {
  86. int i;
  87. free(fsc->args);
  88. if (fsc->s_args) {
  89. for (i = 0; i < fsc->nargs; i++)
  90. free(fsc->s_args[i]);
  91. free(fsc->s_args);
  92. }
  93. free(fsc);
  94. }
  95. /*
  96. * Called when a process has entered a system call. nargs is the
  97. * number of words, not number of arguments (a necessary distinction
  98. * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c
  99. * is ever changed these functions need to keep up.
  100. */
  101. void
  102. i386_syscall_entry(struct trussinfo *trussinfo, int nargs)
  103. {
  104. struct ptrace_io_desc iorequest;
  105. struct reg regs;
  106. struct freebsd_syscall *fsc;
  107. struct syscall *sc;
  108. lwpid_t tid;
  109. unsigned int parm_offset;
  110. int i, syscall_num;
  111. tid = trussinfo->curthread->tid;
  112. if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
  113. fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
  114. return;
  115. }
  116. parm_offset = regs.r_esp + sizeof(int);
  117. /*
  118. * FreeBSD has two special kinds of system call redirctions --
  119. * SYS_syscall, and SYS___syscall. The former is the old syscall()
  120. * routine, basically; the latter is for quad-aligned arguments.
  121. */
  122. syscall_num = regs.r_eax;
  123. switch (syscall_num) {
  124. case SYS_syscall:
  125. syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0);
  126. parm_offset += sizeof(int);
  127. break;
  128. case SYS___syscall:
  129. syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0);
  130. parm_offset += sizeof(quad_t);
  131. break;
  132. }
  133. fsc = alloc_fsc();
  134. if (fsc == NULL)
  135. return;
  136. fsc->number = syscall_num;
  137. fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
  138. NULL : syscallnames[syscall_num];
  139. if (!fsc->name) {
  140. fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
  141. syscall_num);
  142. }
  143. if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
  144. (strcmp(fsc->name, "fork") == 0 ||
  145. strcmp(fsc->name, "rfork") == 0 ||
  146. strcmp(fsc->name, "vfork") == 0))
  147. trussinfo->curthread->in_fork = 1;
  148. if (nargs == 0)
  149. return;
  150. fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
  151. iorequest.piod_op = PIOD_READ_D;
  152. iorequest.piod_offs = (void *)parm_offset;
  153. iorequest.piod_addr = fsc->args;
  154. iorequest.piod_len = (1 + nargs) * sizeof(unsigned long);
  155. ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
  156. if (iorequest.piod_len == 0)
  157. return;
  158. sc = NULL;
  159. if (fsc->name)
  160. sc = get_syscall(fsc->name);
  161. if (sc)
  162. fsc->nargs = sc->nargs;
  163. else {
  164. #if DEBUG
  165. fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
  166. "args to %d\n", fsc->name, nargs);
  167. #endif
  168. fsc->nargs = nargs;
  169. }
  170. fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
  171. fsc->sc = sc;
  172. /*
  173. * At this point, we set up the system call arguments.
  174. * We ignore any OUT ones, however -- those are arguments that
  175. * are set by the system call, and so are probably meaningless
  176. * now. This doesn't currently support arguments that are
  177. * passed in *and* out, however.
  178. */
  179. if (fsc->name) {
  180. #if DEBUG
  181. fprintf(stderr, "syscall %s(", fsc->name);
  182. #endif
  183. for (i = 0; i < fsc->nargs; i++) {
  184. #if DEBUG
  185. fprintf(stderr, "0x%x%s", sc ?
  186. fsc->args[sc->args[i].offset] : fsc->args[i],
  187. i < (fsc->nargs - 1) ? "," : "");
  188. #endif
  189. if (sc && !(sc->args[i].type & OUT)) {
  190. fsc->s_args[i] = print_arg(&sc->args[i],
  191. fsc->args, 0, trussinfo);
  192. }
  193. }
  194. #if DEBUG
  195. fprintf(stderr, ")\n");
  196. #endif
  197. }
  198. #if DEBUG
  199. fprintf(trussinfo->outfile, "\n");
  200. #endif
  201. if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
  202. strcmp(fsc->name, "exit") == 0)) {
  203. /*
  204. * XXX
  205. * This could be done in a more general
  206. * manner but it still wouldn't be very pretty.
  207. */
  208. if (strcmp(fsc->name, "execve") == 0) {
  209. if ((trussinfo->flags & EXECVEARGS) == 0) {
  210. if (fsc->s_args[1]) {
  211. free(fsc->s_args[1]);
  212. fsc->s_args[1] = NULL;
  213. }
  214. }
  215. if ((trussinfo->flags & EXECVEENVS) == 0) {
  216. if (fsc->s_args[2]) {
  217. free(fsc->s_args[2]);
  218. fsc->s_args[2] = NULL;
  219. }
  220. }
  221. }
  222. }
  223. trussinfo->curthread->fsc = fsc;
  224. }
  225. /*
  226. * And when the system call is done, we handle it here.
  227. * Currently, no attempt is made to ensure that the system calls
  228. * match -- this needs to be fixed (and is, in fact, why S_SCX includes
  229. * the system call number instead of, say, an error status).
  230. */
  231. long
  232. i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
  233. {
  234. struct reg regs;
  235. struct freebsd_syscall *fsc;
  236. struct syscall *sc;
  237. lwpid_t tid;
  238. long retval;
  239. int errorp, i;
  240. if (trussinfo->curthread->fsc == NULL)
  241. return (-1);
  242. tid = trussinfo->curthread->tid;
  243. if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
  244. fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
  245. return (-1);
  246. }
  247. retval = regs.r_eax;
  248. errorp = !!(regs.r_eflags & PSL_C);
  249. /*
  250. * This code, while simpler than the initial versions I used, could
  251. * stand some significant cleaning.
  252. */
  253. fsc = trussinfo->curthread->fsc;
  254. sc = fsc->sc;
  255. if (!sc) {
  256. for (i = 0; i < fsc->nargs; i++)
  257. asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
  258. } else {
  259. /*
  260. * Here, we only look for arguments that have OUT masked in --
  261. * otherwise, they were handled in the syscall_entry function.
  262. */
  263. for (i = 0; i < sc->nargs; i++) {
  264. char *temp;
  265. if (sc->args[i].type & OUT) {
  266. /*
  267. * If an error occurred, then don't bother
  268. * getting the data; it may not be valid.
  269. */
  270. if (errorp) {
  271. asprintf(&temp, "0x%lx",
  272. fsc->args[sc->args[i].offset]);
  273. } else {
  274. temp = print_arg(&sc->args[i],
  275. fsc->args, retval, trussinfo);
  276. }
  277. fsc->s_args[i] = temp;
  278. }
  279. }
  280. }
  281. if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
  282. strcmp(fsc->name, "exit") == 0))
  283. trussinfo->curthread->in_syscall = 1;
  284. /*
  285. * It would probably be a good idea to merge the error handling,
  286. * but that complicates things considerably.
  287. */
  288. print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
  289. retval, fsc->sc);
  290. free_fsc(fsc);
  291. return (retval);
  292. }