1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include <sys/ptrace.h>
22#include <sys/user.h>
23
24#define FOR_strace
25#include "toys.h"
26
27GLOBALS(
28 long s, p;
29
30 char ioctl[32], *fmt;
31 long regs[256/sizeof(long)], syscall;
32 pid_t pid;
33 int arg;
34)
35
36 struct user_regs_struct regs;
37
38
39
40
41#if defined(__ARM_EABI__)
42static const char REG_ORDER[] = {0,1,2,3,4,5,7,0};
43#elif defined(__ARM_ARCH) && __ARM_ARCH == 8
44static const char REG_ORDER[] = {0,1,2,3,4,5,8,0};
45#elif defined(__i386__)
46
47static const char REG_ORDER[] = {0,1,2,3,4,5,11,6};
48#elif defined(__m68k__)
49
50static const char REG_ORDER[] = {0,1,2,3,4,7,16,14};
51#elif defined(__PPC__) || defined(__PPC64__)
52static const char REG_ORDER[] = {3,4,5,6,7,8,0,3};
53#elif defined(__s390__)
54
55static const char REG_ORDER[] = {4,5,6,7,8,9,3,4};
56#elif defined(__sh__)
57static const char REG_ORDER[] = {4,5,6,7,0,1,3,0};
58#elif defined(__x86_64__)
59
60static const char REG_ORDER[] = {14,13,12,7,9,8,15,10};
61#else
62#error unsupported architecture
63#endif
64
65#define C(x) case x: return #x
66
67#define FS_IOC_FSGETXATTR 0x801c581f
68#define FS_IOC_FSSETXATTR 0x401c5820
69#define FS_IOC_GETFLAGS 0x80086601
70#define FS_IOC_SETFLAGS 0x40086602
71#define FS_IOC_GETVERSION 0x80087601
72#define FS_IOC_SETVERSION 0x40047602
73struct fsxattr {
74 unsigned fsx_xflags;
75 unsigned fsx_extsize;
76 unsigned fsx_nextents;
77 unsigned fsx_projid;
78 unsigned fsx_cowextsize;
79 char fsx_pad[8];
80};
81
82static char *strioctl(int i)
83{
84 switch (i) {
85 C(FS_IOC_FSGETXATTR);
86 C(FS_IOC_FSSETXATTR);
87 C(FS_IOC_GETFLAGS);
88 C(FS_IOC_GETVERSION);
89 C(FS_IOC_SETFLAGS);
90 C(FS_IOC_SETVERSION);
91 C(SIOCGIFADDR);
92 C(SIOCGIFBRDADDR);
93 C(SIOCGIFCONF);
94 C(SIOCGIFDSTADDR);
95 C(SIOCGIFFLAGS);
96 C(SIOCGIFHWADDR);
97 C(SIOCGIFMAP);
98 C(SIOCGIFMTU);
99 C(SIOCGIFNETMASK);
100 C(SIOCGIFTXQLEN);
101 C(TCGETS);
102 C(TCSETS);
103 C(TIOCGWINSZ);
104 C(TIOCSWINSZ);
105 }
106 sprintf(toybuf, "%#x", i);
107 return toybuf;
108}
109
110
111static char *strerrno(int e)
112{
113 switch (e) {
114
115 C(EPERM);
116 C(ENOENT);
117 C(ESRCH);
118 C(EINTR);
119 C(EIO);
120 C(ENXIO);
121 C(E2BIG);
122 C(ENOEXEC);
123 C(EBADF);
124 C(ECHILD);
125 C(EAGAIN);
126 C(ENOMEM);
127 C(EACCES);
128 C(EFAULT);
129 C(ENOTBLK);
130 C(EBUSY);
131 C(EEXIST);
132 C(EXDEV);
133 C(ENODEV);
134 C(ENOTDIR);
135 C(EISDIR);
136 C(EINVAL);
137 C(ENFILE);
138 C(EMFILE);
139 C(ENOTTY);
140 C(ETXTBSY);
141 C(EFBIG);
142 C(ENOSPC);
143 C(ESPIPE);
144 C(EROFS);
145 C(EMLINK);
146 C(EPIPE);
147 C(EDOM);
148 C(ERANGE);
149
150 C(EDEADLK);
151 C(ENAMETOOLONG);
152 C(ENOLCK);
153 C(ENOSYS);
154 C(ENOTEMPTY);
155 C(ELOOP);
156 C(ENOMSG);
157
158 }
159 sprintf(toybuf, "%d", e);
160 return toybuf;
161}
162
163#undef C
164
165static void xptrace(int req, pid_t pid, void *addr, void *data)
166{
167 if (ptrace(req, pid, addr, data)) perror_exit("ptrace pid %d", pid);
168}
169
170static void get_regs()
171{
172 xptrace(PTRACE_GETREGS, TT.pid, 0, TT.regs);
173}
174
175static void ptrace_struct(long addr, void *dst, size_t bytes)
176{
177 int offset = 0, i;
178 long v;
179
180 for (i=0; i<bytes; i+=sizeof(long)) {
181 errno = 0;
182 v = ptrace(PTRACE_PEEKDATA, TT.pid, addr + offset);
183 if (errno) perror_exit("PTRACE_PEEKDATA failed");
184 memcpy(dst + offset, &v, sizeof(v));
185 offset += sizeof(long);
186 }
187}
188
189
190
191static void print_struct(long addr)
192{
193 if (!addr) {
194 fprintf(stderr, "NULL");
195 while (*TT.fmt != '}') ++TT.fmt;
196 ++TT.fmt;
197 } else if (strstart(&TT.fmt, "ifreq}")) {
198 struct ifreq ir;
199
200 ptrace_struct(addr, &ir, sizeof(ir));
201
202 fprintf(stderr, "{...}");
203 } else if (strstart(&TT.fmt, "fsxattr}")) {
204 struct fsxattr fx;
205
206 ptrace_struct(addr, &fx, sizeof(fx));
207 fprintf(stderr, "{fsx_xflags=%#x, fsx_extsize=%d, fsx_nextents=%d, "
208 "fsx_projid=%d, fsx_cowextsize=%d}", fx.fsx_xflags, fx.fsx_extsize,
209 fx.fsx_nextents, fx.fsx_projid, fx.fsx_cowextsize);
210 } else if (strstart(&TT.fmt, "long}")) {
211 long l;
212
213 ptrace_struct(addr, &l, sizeof(l));
214 fprintf(stderr, "%ld", l);
215 } else if (strstart(&TT.fmt, "longx}")) {
216 long l;
217
218 ptrace_struct(addr, &l, sizeof(l));
219 fprintf(stderr, "%#lx", l);
220 } else if (strstart(&TT.fmt, "rlimit}")) {
221 struct rlimit rl;
222
223 ptrace_struct(addr, &rl, sizeof(rl));
224 fprintf(stderr, "{rlim_cur=%lld, rlim_max=%lld}",
225 (long long)rl.rlim_cur, (long long)rl.rlim_max);
226 } else if (strstart(&TT.fmt, "sigset}")) {
227 long long ss;
228 int i;
229
230 ptrace_struct(addr, &ss, sizeof(ss));
231 fprintf(stderr, "[");
232 for (i=0; i<64;++i) {
233
234 if (ss & (1ULL<<i)) fprintf(stderr, "%d ", i);
235 }
236 fprintf(stderr, "]");
237 } else if (strstart(&TT.fmt, "stat}")) {
238 struct stat sb;
239
240 ptrace_struct(addr, &sb, sizeof(sb));
241
242 if (FLAG(v)) {
243
244 fprintf(stderr, "{st_dev=makedev(%#x, %#x), st_ino=%ld, st_mode=%o, "
245 "st_nlink=%ld, st_uid=%d, st_gid=%d, st_blksize=%ld, st_blocks=%ld, "
246 "st_size=%lld, st_atime=%ld, st_mtime=%ld, st_ctime=%ld}",
247 dev_major(sb.st_dev), dev_minor(sb.st_dev), sb.st_ino, sb.st_mode,
248 (long) sb.st_nlink, sb.st_uid, sb.st_gid, sb.st_blksize, sb.st_blocks,
249 (long long)sb.st_size, sb.st_atime, sb.st_mtime, sb.st_ctime);
250 } else {
251 fprintf(stderr, "{st_mode=%o, st_size=%lld, ...}", sb.st_mode,
252 (long long)sb.st_size);
253 }
254 } else if (strstart(&TT.fmt, "termios}")) {
255 struct termios to;
256
257 ptrace_struct(addr, &to, sizeof(to));
258 fprintf(stderr, "{c_iflag=%#lx, c_oflag=%#lx, c_cflag=%#lx, c_lflag=%#lx}",
259 (long)to.c_iflag, (long)to.c_oflag, (long)to.c_cflag, (long)to.c_lflag);
260 } else if (strstart(&TT.fmt, "timespec}")) {
261 struct timespec ts;
262
263 ptrace_struct(addr, &ts, sizeof(ts));
264 fprintf(stderr, "{tv_sec=%lld, tv_nsec=%lld}",
265 (long long)ts.tv_sec, (long long)ts.tv_nsec);
266 } else if (strstart(&TT.fmt, "winsize}")) {
267 struct winsize ws;
268
269 ptrace_struct(addr, &ws, sizeof(ws));
270 fprintf(stderr, "{ws_row=%hu, ws_col=%hu, ws_xpixel=%hu, ws_ypixel=%hu}",
271 ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
272 } else abort();
273}
274
275static void print_ptr(long addr)
276{
277 if (!addr) fprintf(stderr, "NULL");
278 else fprintf(stderr, "0x%lx", addr);
279}
280
281static void print_string(long addr)
282{
283 long offset = 0, total = 0;
284 int done = 0, i;
285
286 fputc('"', stderr);
287 while (!done) {
288 errno = 0;
289 long v = ptrace(PTRACE_PEEKDATA, TT.pid, addr + offset);
290 if (errno) return;
291 memcpy(toybuf, &v, sizeof(v));
292 for (i=0; i<sizeof(v); ++i) {
293 if (!toybuf[i]) {
294
295
296 done = 1;
297 break;
298 }
299 if (isprint(toybuf[i])) fputc(toybuf[i], stderr);
300 else {
301
302 fputc('\\', stderr);
303 if (toybuf[i] == '\n') fputc('n', stderr);
304 else if (toybuf[i] == '\r') fputc('r', stderr);
305 else if (toybuf[i] == '\t') fputc('t', stderr);
306 else fprintf(stderr, "x%2.2x", toybuf[i]);
307 }
308 if (++total >= TT.s) {
309 done = 1;
310 break;
311 }
312 }
313 offset += sizeof(v);
314 }
315 fputc('"', stderr);
316}
317
318static void print_bitmask(int bitmask, long v, char *zero, ...)
319{
320 va_list ap;
321 int first = 1;
322
323 if (!v && zero) {
324 fprintf(stderr, "%s", zero);
325 return;
326 }
327
328 va_start(ap, zero);
329 for (;;) {
330 int this = va_arg(ap, int);
331 char *name;
332
333 if (!this) break;
334 name = va_arg(ap, char*);
335 if (bitmask) {
336 if (v & this) {
337 fprintf(stderr, "%s%s", first?"":"|", name);
338 first = 0;
339 v &= ~this;
340 }
341 } else {
342 if (v == this) {
343 fprintf(stderr, "%s", name);
344 v = 0;
345 break;
346 }
347 }
348 }
349 va_end(ap);
350 if (v) fprintf(stderr, "%s%#lx", first?"":"|", v);
351}
352
353static void print_flags(long v)
354{
355#define C(n) n, #n
356 if (strstart(&TT.fmt, "access|")) {
357 print_bitmask(1, v, "F_OK", C(R_OK), C(W_OK), C(X_OK), 0);
358 } else if (strstart(&TT.fmt, "mmap|")) {
359 print_bitmask(1, v, 0, C(MAP_SHARED), C(MAP_PRIVATE), C(MAP_32BIT),
360 C(MAP_ANONYMOUS), C(MAP_FIXED), C(MAP_GROWSDOWN), C(MAP_HUGETLB),
361 C(MAP_DENYWRITE), 0);
362 } else if (strstart(&TT.fmt, "open|")) {
363 print_bitmask(1, v, "O_RDONLY", C(O_WRONLY), C(O_RDWR), C(O_CLOEXEC),
364 C(O_CREAT), C(O_DIRECTORY), C(O_EXCL), C(O_NOCTTY), C(O_NOFOLLOW),
365 C(O_TRUNC), C(O_ASYNC), C(O_APPEND), C(O_DSYNC), C(O_EXCL),
366 C(O_NOATIME), C(O_NONBLOCK), C(O_PATH), C(O_SYNC),
367 0x4000, "O_DIRECT", 0x8000, "O_LARGEFILE", 0x410000, "O_TMPFILE", 0);
368 } else if (strstart(&TT.fmt, "prot|")) {
369 print_bitmask(1,v,"PROT_NONE",C(PROT_READ),C(PROT_WRITE),C(PROT_EXEC),0);
370 } else abort();
371}
372
373static void print_alternatives(long v)
374{
375 if (strstart(&TT.fmt, "rlimit^")) {
376 print_bitmask(0, v, "RLIMIT_CPU", C(RLIMIT_FSIZE), C(RLIMIT_DATA),
377 C(RLIMIT_STACK), C(RLIMIT_CORE), C(RLIMIT_RSS), C(RLIMIT_NPROC),
378 C(RLIMIT_NOFILE), C(RLIMIT_MEMLOCK), C(RLIMIT_AS), C(RLIMIT_LOCKS),
379 C(RLIMIT_SIGPENDING), C(RLIMIT_MSGQUEUE), C(RLIMIT_NICE),
380 C(RLIMIT_RTPRIO), C(RLIMIT_RTTIME), 0);
381 } else if (strstart(&TT.fmt, "seek^")) {
382 print_bitmask(0, v, "SEEK_SET", C(SEEK_CUR), C(SEEK_END), C(SEEK_DATA),
383 C(SEEK_HOLE), 0);
384 } else if (strstart(&TT.fmt, "sig^")) {
385 print_bitmask(0, v, "SIG_BLOCK", C(SIG_UNBLOCK), C(SIG_SETMASK), 0);
386 } else abort();
387}
388
389static void print_args()
390{
391 int i;
392
393
394 for (i = 0; *TT.fmt; i++, TT.arg++) {
395 long v = TT.regs[REG_ORDER[TT.arg]];
396 char *s, ch;
397
398 if (i) fprintf(stderr, ", ");
399 switch (ch = *TT.fmt++) {
400 case 'd': fprintf(stderr, "%ld", v); break;
401 case 'f': if ((int) v == AT_FDCWD) fprintf(stderr, "AT_FDCWD");
402 else fprintf(stderr, "%ld", v);
403 break;
404 case 'i': fprintf(stderr, "%s", strioctl(v)); break;
405 case 'm': fprintf(stderr, "%03o", (unsigned) v); break;
406 case 'o': fprintf(stderr, "%ld", v); break;
407 case 'p': print_ptr(v); break;
408 case 's': print_string(v); break;
409 case 'S':
410
411 if (!(s = num_to_sig(v))) fprintf(stderr, "%ld", v);
412 else fprintf(stderr, "SIG%s", s);
413 break;
414 case 'z': fprintf(stderr, "%ld", (long) v); break;
415 case 'x': fprintf(stderr, "%lx", v); break;
416
417 case '{': print_struct(v); break;
418 case '|': print_flags(v); break;
419 case '^': print_alternatives(v); break;
420
421 case '/': return;
422
423 default: fprintf(stderr, "?%c<0x%lx>", ch, v); break;
424 }
425 }
426}
427
428static void print_enter(void)
429{
430 char *name;
431
432 get_regs();
433 TT.syscall = TT.regs[REG_ORDER[6]];
434 if (TT.syscall == __NR_ioctl) {
435 name = "ioctl";
436 switch (TT.regs[REG_ORDER[1]]) {
437 case FS_IOC_FSGETXATTR: TT.fmt = "fi/{fsxattr}"; break;
438 case FS_IOC_FSSETXATTR: TT.fmt = "fi{fsxattr}"; break;
439 case FS_IOC_GETFLAGS: TT.fmt = "fi/{longx}"; break;
440 case FS_IOC_GETVERSION: TT.fmt = "fi/{long}"; break;
441 case FS_IOC_SETFLAGS: TT.fmt = "fi{long}"; break;
442 case FS_IOC_SETVERSION: TT.fmt = "fi{long}"; break;
443
444 case SIOCGIFADDR:
445 case SIOCGIFBRDADDR:
446 case SIOCGIFDSTADDR:
447 case SIOCGIFFLAGS:
448 case SIOCGIFHWADDR:
449 case SIOCGIFMAP:
450 case SIOCGIFMTU:
451 case SIOCGIFNETMASK:
452 case SIOCGIFTXQLEN: TT.fmt = "fi/{ifreq}"; break;
453 case SIOCSIFADDR:
454 case SIOCSIFBRDADDR:
455 case SIOCSIFDSTADDR:
456 case SIOCSIFFLAGS:
457 case SIOCSIFHWADDR:
458 case SIOCSIFMAP:
459 case SIOCSIFMTU:
460 case SIOCSIFNETMASK:
461 case SIOCSIFTXQLEN: TT.fmt = "fi{ifreq}"; break;
462 case TCGETS: TT.fmt = "fi/{termios}"; break;
463 case TCSETS: TT.fmt = "fi{termios}"; break;
464 case TIOCGWINSZ: TT.fmt = "fi/{winsize}"; break;
465 case TIOCSWINSZ: TT.fmt = "fi{winsize}"; break;
466 default:
467 TT.fmt = (TT.regs[REG_ORDER[0]]&1) ? "fip" : "fi/p";
468 break;
469 }
470 } else switch (TT.syscall) {
471#define SC(n,f) case __NR_ ## n: name = #n; TT.fmt = f; break
472 SC(access, "s|access|");
473 SC(arch_prctl, "dp");
474 SC(brk, "p");
475 SC(close, "d");
476 SC(connect, "fpd");
477 SC(dup, "f");
478 SC(dup2, "ff");
479 SC(dup3, "ff|open|");
480 SC(execve, "spp");
481 SC(exit_group, "d");
482 SC(fcntl, "fdp");
483 SC(fstat, "f/{stat}");
484 SC(futex, "pdxppx");
485 SC(getdents64, "dpz");
486 SC(geteuid, "");
487 SC(getuid, "");
488
489 SC(getxattr, "sspz");
490 SC(lgetxattr, "sspz");
491 SC(fgetxattr, "fspz");
492
493 SC(lseek, "fo^seek^");
494 SC(lstat, "s/{stat}");
495 SC(mmap, "pz|prot||mmap|fx");
496 SC(mprotect, "pz|prot|");
497 SC(mremap, "pzzdp");
498 SC(munmap, "pz");
499 SC(nanosleep, "{timespec}/{timespec}");
500#ifdef __NR_newfstatat
501 SC(newfstatat, "fs/{stat}d");
502#endif
503 SC(open, "sd|open|m");
504 SC(openat, "fs|open|m");
505 SC(poll, "pdd");
506 SC(prlimit64, "d^rlimit^{rlimit}/{rlimit}");
507 SC(read, "d/sz");
508 SC(readlinkat, "s/sz");
509 SC(rt_sigaction, "Sppz");
510 SC(rt_sigprocmask, "^sig^{sigset}/{sigset}z");
511 SC(set_robust_list, "pd");
512 SC(set_tid_address, "p");
513 SC(socket, "ddd");
514 SC(stat, "s/{stat}");
515 SC(statfs, "sp");
516 SC(sysinfo, "p");
517 SC(umask, "m");
518 SC(uname, "p");
519 SC(write, "dsz");
520 default:
521 sprintf(name = toybuf, "SYS_%ld", TT.syscall);
522 TT.fmt = "pppppp";
523 break;
524 }
525
526 fprintf(stderr, "%s(", name);
527 TT.arg = 0;
528 print_args();
529}
530
531static void print_exit(void)
532{
533 long result;
534
535 get_regs();
536 result = TT.regs[REG_ORDER[7]];
537 if (*TT.fmt) print_args();
538 fprintf(stderr, ") = ");
539 if (result >= -4095UL)
540 fprintf(stderr, "-1 %s (%s)", strerrno(-result), strerror(-result));
541 else if (TT.syscall==__NR_mmap || TT.syscall==__NR_brk) print_ptr(result);
542 else fprintf(stderr, "%ld", result);
543 fputc('\n', stderr);
544}
545
546static int next(void)
547{
548 int status;
549
550 for (;;) {
551 ptrace(PTRACE_SYSCALL, TT.pid, 0, 0);
552 waitpid(TT.pid, &status, 0);
553
554 if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80) return 1;
555 if (WIFEXITED(status)) return 0;
556 fprintf(stderr, "[stopped %d (%x)]\n", status, WSTOPSIG(status));
557 }
558}
559
560static void strace_detach(int s)
561{
562 xptrace(PTRACE_DETACH, TT.pid, 0, 0);
563 exit(1);
564}
565
566void strace_main(void)
567{
568 int status;
569
570 if (!FLAG(s)) TT.s = 32;
571
572 if (FLAG(p)) {
573 if (*toys.optargs) help_exit("No arguments with -p");
574 TT.pid = TT.p;
575 signal(SIGINT, strace_detach);
576
577 xptrace(PTRACE_ATTACH, TT.pid, 0, 0);
578 } else {
579 if (!*toys.optargs) help_exit("Needs 1 argument");
580 TT.pid = xfork();
581 if (!TT.pid) {
582 errno = 0;
583 ptrace(PTRACE_TRACEME);
584 if (errno) perror_exit("PTRACE_TRACEME failed");
585 raise(SIGSTOP);
586 toys.stacktop = 0;
587 xexec(toys.optargs);
588 }
589 }
590
591 do {
592 waitpid(TT.pid, &status, 0);
593 } while (!WIFSTOPPED(status));
594
595
596
597 errno = 0;
598 ptrace(PTRACE_SETOPTIONS, TT.pid, 0, PTRACE_O_TRACESYSGOOD);
599 if (errno) perror_exit("PTRACE_SETOPTIONS PTRACE_O_TRACESYSGOOD failed");
600
601
602
603 for (;;) {
604 if (!next()) break;
605 print_enter();
606 if (!next()) break;
607 print_exit();
608 }
609
610
611 waitpid(TT.pid, &status, 0);
612 if (WIFEXITED(status))
613 fprintf(stderr, "+++ exited with %d +++\n", WEXITSTATUS(status));
614 if (WIFSTOPPED(status))
615 fprintf(stderr, "+++ stopped with %d +++\n", WSTOPSIG(status));
616}
617