/usr.bin/truss/amd64-fbsd32.c

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