source: neercs/trunk/src/mytrace.c @ 2786

Last change on this file since 2786 was 2786, checked in by Sam Hocevar, 14 years ago
  • mytrace.c: amd64 build fix.
  • Property svn:eol-style set to native
File size: 15.3 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2008 Pascal Terjan
4 *            (c) 2008 Sam Hocevar <sam@zoy.org>
5 *                All Rights Reserved
6 *
7 *  $Id$
8 *
9 *  This program is free software. It comes without any warranty, to
10 *  the extent permitted by applicable law. You can redistribute it
11 *  and/or modify it under the terms of the Do What The Fuck You Want
12 *  To Public License, Version 2, as published by Sam Hocevar. See
13 *  http://sam.zoy.org/wtfpl/COPYING for more details.
14 */
15
16#include "config.h"
17
18#include <errno.h>
19#include <fcntl.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#if defined USE_GRAB
25#   include <sys/ioctl.h>
26#   include <sys/ptrace.h>
27#   include <sys/stat.h>
28#   include <sys/syscall.h>
29#   include <sys/user.h>
30#   include <sys/wait.h>
31#endif
32
33#include "neercs.h"
34#include "mytrace.h"
35
36#if defined(__APPLE__)
37
38struct mytrace* mytrace_attach(long int pid)
39{
40    errno = ENOSYS;
41    return NULL;
42}
43
44struct mytrace* mytrace_fork(struct mytrace *t)
45{
46    errno = ENOSYS;
47    return NULL;
48}
49
50int mytrace_detach(struct mytrace *t)
51{
52    errno = ENOSYS;
53    return -1;
54}
55
56long mytrace_getpid(struct mytrace *t)
57{
58    errno = ENOSYS;
59    return -1;
60}
61
62int mytrace_open(struct mytrace *t, char const *path, int mode)
63{
64    errno = ENOSYS;
65    return -1;
66}
67
68int mytrace_write(struct mytrace *t, int fd, char const *data, size_t len)
69{
70    errno = ENOSYS;
71    return -1;
72}
73
74int mytrace_close(struct mytrace *t, int fd)
75{
76    errno = ENOSYS;
77    return -1;
78}
79
80int mytrace_dup2(struct mytrace *t, int oldfd, int newfd)
81{
82    errno = ENOSYS;
83    return -1;
84}
85
86int mytrace_setpgid(struct mytrace *t, long pid, long pgid)
87{
88    errno = ENOSYS;
89    return -1;
90}
91
92int mytrace_setsid(struct mytrace *t)
93{
94    errno = ENOSYS;
95    return -1;
96}
97
98int mytrace_kill(struct mytrace *t, long pid, int sig)
99{
100    errno = ENOSYS;
101    return -1;
102}
103
104int mytrace_exit(struct mytrace *t, int status)
105{
106    errno = ENOSYS;
107    return -1;
108}
109
110#else
111
112#if defined USE_GRAB
113static int memcpy_from_target(struct mytrace *t,
114                              char* dest, long src, size_t n);
115static int memcpy_into_target(struct mytrace *t,
116                              long dest, char const *src, size_t n);
117static long remote_syscall(struct mytrace *t, long call,
118                           long arg1, long arg2, long arg3);
119#   if defined DEBUG
120static void print_registers(pid_t pid);
121#   else
122#       define print_registers(x) do {} while(0)
123#   endif
124#endif
125
126#define X(x) #x
127#define STRINGIFY(x) X(x)
128
129#define SYSCALL_X86     0x80cd  /* CD 80 = int $0x80 */
130#define SYSCALL_X86_NEW 0xf3eb  /* EB F3 = jmp <__kernel_vsyscall+0x3> */
131#define SYSENTER        0x340f  /* 0F 34 = sysenter */
132#define SYSCALL_AMD64   0x050fL /* 0F 05 = syscall */
133
134#if defined __x86_64__
135#   define RAX rax
136#   define RBX rbx
137#   define RCX rcx
138#   define RDX rdx
139#   define RSP rsp
140#   define RBP rbp
141#   define RIP rip
142#   define RDI rdi
143#   define RSI rsi
144#   define FMT "%016lx"
145#else
146#   define RAX eax
147#   define RBX ebx
148#   define RCX ecx
149#   define RDX edx
150#   define RSP esp
151#   define RBP ebp
152#   define RIP eip
153#   define RDI edi
154#   define RSI esi
155#   define FMT "%08lx"
156#endif
157
158#define MYCALL_OPEN     0
159#define MYCALL_CLOSE    1
160#define MYCALL_WRITE    2
161#define MYCALL_DUP2     3
162#define MYCALL_SETPGID  4
163#define MYCALL_SETSID   5
164#define MYCALL_KILL     6
165#define MYCALL_FORK     7
166#define MYCALL_EXIT     8
167
168#if defined __x86_64__
169/* from unistd_32.h on an amd64 system */
170int syscalls32[] = { 5, 6, 4, 63, 57, 66, 37, 2, 1 };
171int syscalls64[] =
172#else
173int syscalls32[] =
174#endif
175    { SYS_open, SYS_close, SYS_write, SYS_dup2, SYS_setpgid, SYS_setsid,
176      SYS_kill, SYS_fork, SYS_exit };
177
178char const *syscallnames[] =
179    { "open", "close", "write", "dup2", "setpgid", "setsid", "kill", "fork",
180      "exit" };
181
182struct mytrace
183{
184    pid_t pid, child;
185};
186
187struct mytrace* mytrace_attach(long int pid)
188{
189#if defined USE_GRAB
190    struct mytrace *t;
191    int status;
192
193    if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
194    {
195        perror("ptrace_attach");
196        return NULL;
197    }
198    if(waitpid(pid, &status, 0) < 0)
199    {
200        perror("waitpid");
201        return NULL;
202    }
203    if(!WIFSTOPPED(status))
204    {
205        fprintf(stderr, "traced process was not stopped\n");
206        ptrace(PTRACE_DETACH, pid, 0, 0);
207        return NULL;
208    }
209
210    t = malloc(sizeof(struct mytrace));
211    t->pid = pid;
212    t->child = 0;
213
214    return t;
215#else
216    errno = ENOSYS;
217    return NULL;
218#endif
219}
220
221struct mytrace* mytrace_fork(struct mytrace *t)
222{
223#if defined USE_GRAB
224    struct mytrace *child;
225
226    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEFORK);
227    remote_syscall(t, MYCALL_FORK, 0, 0, 0);
228    waitpid(t->child, NULL, 0);
229
230    child = malloc(sizeof(struct mytrace));
231    child->pid = t->child;
232    child->child = 0;
233
234    return child;
235#else
236    errno = ENOSYS;
237    return NULL;
238#endif
239}
240
241int mytrace_detach(struct mytrace *t)
242{
243#if defined USE_GRAB
244    ptrace(PTRACE_DETACH, t->pid, 0, 0);
245    free(t);
246
247    return 0;
248#else
249    errno = ENOSYS;
250    return -1;
251#endif
252}
253
254long mytrace_getpid(struct mytrace *t)
255{
256#if defined USE_GRAB
257    return t->pid;
258#else
259    errno = ENOSYS;
260    return -1;
261#endif
262}
263
264int mytrace_open(struct mytrace *t, char const *path, int mode)
265{
266#if defined USE_GRAB
267    char backup_data[4096];
268    struct user_regs_struct regs;
269    size_t size = strlen(path) + 1;
270    int ret;
271
272    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
273    {
274        fprintf(stderr, "PTRACE_GETREGS failed\n");
275        return errno;
276    }
277
278    /* Backup the data that we will use */
279    if(memcpy_from_target(t, backup_data, regs.RSP, size) < 0)
280        return -1;
281
282    memcpy_into_target(t, regs.RSP, path, size);
283
284    ret = remote_syscall(t, MYCALL_OPEN, regs.RSP, O_RDWR, 0755);
285
286    /* Restore the data */
287    memcpy_into_target(t, regs.RSP, backup_data, size);
288
289    if(ret < 0)
290    {
291        errno = ret;
292        return -1;
293    }
294
295    return ret;
296#else
297    errno = ENOSYS;
298    return -1;
299#endif
300}
301
302int mytrace_close(struct mytrace *t, int fd)
303{
304#if defined USE_GRAB
305    return remote_syscall(t, MYCALL_CLOSE, fd, 0, 0);
306#else
307    errno = ENOSYS;
308    return -1;
309#endif
310}
311
312int mytrace_write(struct mytrace *t, int fd, char const *data, size_t len)
313{
314#if defined USE_GRAB
315    struct user_regs_struct regs;
316    char *backup_data;
317    int ret;
318
319    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
320    {
321        fprintf(stderr, "PTRACE_GETREGS failed\n");
322        return errno;
323    }
324
325    backup_data = malloc(len);
326
327    /* Backup the data that we will use */
328    if(memcpy_from_target(t, backup_data, regs.RSP, len) < 0)
329        return -1;
330
331    memcpy_into_target(t, regs.RSP, data, len);
332
333    ret = remote_syscall(t, MYCALL_WRITE, fd, regs.RSP, len);
334
335    /* Restore the data */
336    memcpy_into_target(t, regs.RSP, backup_data, len);
337
338    if(ret < 0)
339    {
340        errno = ret;
341        return -1;
342    }
343
344    return ret;
345#else
346    errno = ENOSYS;
347    return -1;
348#endif
349}
350
351int mytrace_dup2(struct mytrace *t, int oldfd, int newfd)
352{
353#if defined USE_GRAB
354    return remote_syscall(t, MYCALL_DUP2, oldfd, newfd, 0);
355#else
356    errno = ENOSYS;
357    return -1;
358#endif
359}
360
361int mytrace_setpgid(struct mytrace *t, long pid, long pgid)
362{
363#if defined USE_GRAB
364    return remote_syscall(t, MYCALL_SETPGID, pid, pgid, 0);
365#else
366    errno = ENOSYS;
367    return -1;
368#endif
369}
370
371int mytrace_setsid(struct mytrace *t)
372{
373#if defined USE_GRAB
374    return remote_syscall(t, MYCALL_SETSID, 0, 0, 0);
375#else
376    errno = ENOSYS;
377    return -1;
378#endif
379}
380
381int mytrace_kill(struct mytrace *t, long pid, int sig)
382{
383#if defined USE_GRAB
384    return remote_syscall(t, MYCALL_KILL, pid, sig, 0);
385#else
386    errno = ENOSYS;
387    return -1;
388#endif
389}
390
391int mytrace_exit(struct mytrace *t, int status)
392{
393#if defined USE_GRAB
394    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEEXIT);
395    return remote_syscall(t, MYCALL_EXIT, status, 0, 0);
396#else
397    errno = ENOSYS;
398    return -1;
399#endif
400}
401
402/*
403 * XXX: the following functions are local
404 */
405
406#if defined USE_GRAB
407static int memcpy_from_target(struct mytrace *t,
408                              char* dest, long src, size_t n)
409{
410    static int const align = sizeof(long) - 1;
411
412    while(n)
413    {
414        long data;
415        size_t todo = sizeof(long) - (src & align);
416
417        if(n < todo)
418            todo = n;
419
420        data = ptrace(PTRACE_PEEKTEXT, t->pid, src - (src & align), 0);
421        if(errno)
422        {
423            perror("ptrace_peektext");
424            return -1;
425        }
426        memcpy(dest, (char *)&data + (src & align), todo);
427
428        dest += todo;
429        src += todo;
430        n -= todo;
431    }
432
433    return 0;
434}
435
436static int memcpy_into_target(struct mytrace *t,
437                              long dest, char const *src, size_t n)
438{
439    static int const align = sizeof(long) - 1;
440
441    while(n)
442    {
443        long data;
444        size_t todo = sizeof(long) - (dest & align);
445
446        if(n < todo)
447            todo = n;
448        if(todo != sizeof(long))
449        {
450            data = ptrace(PTRACE_PEEKTEXT, t->pid, dest - (dest & align), 0);
451            if(errno)
452            {
453                perror("ptrace_peektext");
454                return -1;
455            }
456        }
457
458        memcpy((char *)&data + (dest & align), src, todo);
459        ptrace(PTRACE_POKETEXT, t->pid, dest - (dest & align), data);
460        if(errno)
461        {
462            perror("ptrace_poketext");
463            return -1;
464        }
465
466        src += todo;
467        dest += todo;
468        n -= todo;
469    }
470
471    return 0;
472}
473
474static long remote_syscall(struct mytrace *t, long call,
475                           long arg1, long arg2, long arg3)
476{
477    /* Method for remote syscall:
478     *  - wait until the traced application exits from a syscall
479     *  - save registers
480     *  - rewind eip/rip to point on the syscall instruction
481     *  - single step: execute syscall instruction
482     *  - retrieve resulting registers
483     *  - restore registers */
484    struct user_regs_struct regs, oldregs;
485    long oinst;
486    int bits;
487    int offset = 2;
488
489    if(call < 0 || call >= (long)(sizeof(syscallnames)/sizeof(*syscallnames)))
490    {
491        fprintf(stderr, "unknown remote syscall %li\n", call);
492        return -1;
493    }
494
495    debug("remote syscall %s(%lu, %lu, %lu)",
496          syscallnames[call], arg1, arg2, arg3);
497
498    print_registers(t->pid);
499
500#if defined __x86_64__
501    bits = 64;
502#else
503    bits = 32;
504#endif
505
506    for(;;)
507    {
508        if(ptrace(PTRACE_GETREGS, t->pid, NULL, &oldregs) < 0)
509        {
510            fprintf(stderr, "PTRACE_GETREGS failed\n");
511            return -1;
512        }
513
514        oinst = ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - 2, 0) & 0xffff;
515        fprintf(stderr, "%lx\n", oinst);
516
517#if defined __x86_64__
518        if(oinst == SYSCALL_AMD64)
519            break;
520        if(oinst == SYSCALL_X86 || oinst == SYSCALL_X86_NEW)
521        {
522            bits = 32;
523            break;
524        }
525#else
526        if(oinst == SYSCALL_X86 || oinst == SYSCALL_X86_NEW)
527            break;
528#endif
529
530        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
531        {
532            perror("ptrace_syscall (1)");
533            return -1;
534        }
535        waitpid(t->pid, NULL, 0);
536        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
537        {
538            perror("ptrace_syscall (2)");
539            return -1;
540        }
541        waitpid(t->pid, NULL, 0);
542    }
543
544    print_registers(t->pid);
545
546    if(oinst == SYSCALL_X86_NEW)
547    {
548        /*  Get back to sysenter */
549        while((ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - offset, 0) & 0xffff) != 0x340f)
550            offset++;
551        oldregs.RBP = oldregs.RSP;
552    }
553
554    regs = oldregs;
555    regs.RIP = regs.RIP - offset;
556#if defined __x86_64__
557    if(bits == 64)
558    {
559        regs.RAX = syscalls64[call];
560        regs.RDI = arg1;
561        regs.RSI = arg2;
562        regs.RDX = arg3;
563    }
564    else
565#endif
566    {
567        regs.RAX = syscalls32[call];
568        regs.RBX = arg1;
569        regs.RCX = arg2;
570        regs.RDX = arg3;
571    }
572
573    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &regs) < 0)
574    {
575        fprintf(stderr, "PTRACE_SETREGS failed\n");
576        return -1;
577    }
578
579    for(;;)
580    {
581        int status;
582
583        print_registers(t->pid);
584
585        if(ptrace(PTRACE_SINGLESTEP, t->pid, NULL, NULL) < 0)
586        {
587            fprintf(stderr, "PTRACE_SINGLESTEP failed\n");
588            return -1;
589        }
590        waitpid(t->pid, &status, 0);
591
592        if(WIFEXITED(status))
593            return 0;
594
595        if(!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP)
596            continue;
597
598        /* Fuck Linux: there is no macro for this */
599        switch((status >> 16) & 0xffff)
600        {
601        case PTRACE_EVENT_FORK:
602            if(ptrace(PTRACE_GETEVENTMSG, t->pid, 0, &t->child) < 0)
603            {
604                fprintf(stderr, "PTRACE_GETEVENTMSG failed\n");
605                return -1;
606            }
607            debug("PTRACE_GETEVENTMSG %d", t->child);
608            continue;
609        case PTRACE_EVENT_EXIT:
610            debug("PTRACE_EVENT_EXIT");
611            /* The process is about to exit, don't do anything else */
612            return 0;
613        }
614
615        break;
616    }
617
618    print_registers(t->pid);
619
620    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
621    {
622        fprintf(stderr, "PTRACE_GETREGS failed\n");
623        return -1;
624    }
625
626    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &oldregs) < 0)
627    {
628        fprintf(stderr, "PTRACE_SETREGS failed\n");
629        return -1;
630    }
631    print_registers(t->pid);
632
633    debug("syscall %s returned %ld", syscallnames[call], regs.RAX);
634
635    if((long)regs.RAX < 0)
636    {
637        errno = -(long)regs.RAX;
638        perror("syscall");
639        return -1;
640    }
641
642    return regs.RAX;
643}
644
645/* For debugging purposes only. Prints register and stack information. */
646#if defined DEBUG
647static void print_registers(pid_t pid)
648{
649    union { long int l; unsigned char data[sizeof(long int)]; } inst;
650    struct user_regs_struct regs;
651    int i;
652
653    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
654    {
655        perror("ptrace_getregs");
656        exit(errno);
657    }
658
659    fprintf(stderr, "  / %s: "FMT"   ", STRINGIFY(RAX), regs.RAX);
660    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RBX), regs.RBX);
661    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RCX), regs.RCX);
662    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RDX), regs.RDX);
663    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RDI), regs.RDI);
664    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RSI), regs.RSI);
665    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RSP), regs.RSP);
666    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RIP), regs.RIP);
667
668    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP - 4, 0);
669    fprintf(stderr, "  | code: ... %02x %02x %02x %02x <---> ",
670            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
671    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP, 0);
672    fprintf(stderr, "%02x %02x %02x %02x ...\n",
673            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
674
675    fprintf(stderr, "  \\ stack: ... ");
676    for(i = -16; i < 24; i += sizeof(long))
677    {
678        inst.l = ptrace(PTRACE_PEEKDATA, pid, regs.RSP + i, 0);
679#if defined __x86_64__
680        fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x ",
681                inst.data[0], inst.data[1], inst.data[2], inst.data[3],
682                inst.data[4], inst.data[5], inst.data[6], inst.data[7]);
683#else
684        fprintf(stderr, "%02x %02x %02x %02x ",
685                inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
686#endif
687        if(i == 0)
688            fprintf(stderr, "[%s] ", STRINGIFY(RSP));
689    }
690    fprintf(stderr, "...\n");
691}
692#endif /* DEBUG */
693
694#endif /* USE_GRAB */
695
696#endif /* ! defined(__APPLE__) */
Note: See TracBrowser for help on using the repository browser.