| Andy Lutomirski | 98eedc3 | 2011-07-13 09:24:16 -0400 | [diff] [blame] | 1 | /* | 
|  | 2 | * vdso_test.c: Sample code to test parse_vdso.c on x86_64 | 
|  | 3 | * Copyright (c) 2011 Andy Lutomirski | 
|  | 4 | * Subject to the GNU General Public License, version 2 | 
|  | 5 | * | 
|  | 6 | * You can amuse yourself by compiling with: | 
|  | 7 | * gcc -std=gnu99 -nostdlib | 
|  | 8 | *     -Os -fno-asynchronous-unwind-tables -flto | 
|  | 9 | *      vdso_test.c parse_vdso.c -o vdso_test | 
|  | 10 | * to generate a small binary with no dependencies at all. | 
|  | 11 | */ | 
|  | 12 |  | 
|  | 13 | #include <sys/syscall.h> | 
|  | 14 | #include <sys/time.h> | 
|  | 15 | #include <unistd.h> | 
|  | 16 | #include <stdint.h> | 
|  | 17 |  | 
|  | 18 | extern void *vdso_sym(const char *version, const char *name); | 
|  | 19 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | 
|  | 20 | extern void vdso_init_from_auxv(void *auxv); | 
|  | 21 |  | 
|  | 22 | /* We need a libc functions... */ | 
|  | 23 | int strcmp(const char *a, const char *b) | 
|  | 24 | { | 
|  | 25 | /* This implementation is buggy: it never returns -1. */ | 
|  | 26 | while (*a || *b) { | 
|  | 27 | if (*a != *b) | 
|  | 28 | return 1; | 
|  | 29 | if (*a == 0 || *b == 0) | 
|  | 30 | return 1; | 
|  | 31 | a++; | 
|  | 32 | b++; | 
|  | 33 | } | 
|  | 34 |  | 
|  | 35 | return 0; | 
|  | 36 | } | 
|  | 37 |  | 
|  | 38 | /* ...and two syscalls.  This is x86_64-specific. */ | 
|  | 39 | static inline long linux_write(int fd, const void *data, size_t len) | 
|  | 40 | { | 
|  | 41 |  | 
|  | 42 | long ret; | 
|  | 43 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_write), | 
|  | 44 | "D" (fd), "S" (data), "d" (len) : | 
|  | 45 | "cc", "memory", "rcx", | 
|  | 46 | "r8", "r9", "r10", "r11" ); | 
|  | 47 | return ret; | 
|  | 48 | } | 
|  | 49 |  | 
|  | 50 | static inline void linux_exit(int code) | 
|  | 51 | { | 
|  | 52 | asm volatile ("syscall" : : "a" (__NR_exit), "D" (code)); | 
|  | 53 | } | 
|  | 54 |  | 
|  | 55 | void to_base10(char *lastdig, uint64_t n) | 
|  | 56 | { | 
|  | 57 | while (n) { | 
|  | 58 | *lastdig = (n % 10) + '0'; | 
|  | 59 | n /= 10; | 
|  | 60 | lastdig--; | 
|  | 61 | } | 
|  | 62 | } | 
|  | 63 |  | 
|  | 64 | __attribute__((externally_visible)) void c_main(void **stack) | 
|  | 65 | { | 
|  | 66 | /* Parse the stack */ | 
|  | 67 | long argc = (long)*stack; | 
|  | 68 | stack += argc + 2; | 
|  | 69 |  | 
|  | 70 | /* Now we're pointing at the environment.  Skip it. */ | 
|  | 71 | while(*stack) | 
|  | 72 | stack++; | 
|  | 73 | stack++; | 
|  | 74 |  | 
|  | 75 | /* Now we're pointing at auxv.  Initialize the vDSO parser. */ | 
|  | 76 | vdso_init_from_auxv((void *)stack); | 
|  | 77 |  | 
|  | 78 | /* Find gettimeofday. */ | 
|  | 79 | typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); | 
|  | 80 | gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | 
|  | 81 |  | 
|  | 82 | if (!gtod) | 
|  | 83 | linux_exit(1); | 
|  | 84 |  | 
|  | 85 | struct timeval tv; | 
|  | 86 | long ret = gtod(&tv, 0); | 
|  | 87 |  | 
|  | 88 | if (ret == 0) { | 
|  | 89 | char buf[] = "The time is                     .000000\n"; | 
|  | 90 | to_base10(buf + 31, tv.tv_sec); | 
|  | 91 | to_base10(buf + 38, tv.tv_usec); | 
|  | 92 | linux_write(1, buf, sizeof(buf) - 1); | 
|  | 93 | } else { | 
|  | 94 | linux_exit(ret); | 
|  | 95 | } | 
|  | 96 |  | 
|  | 97 | linux_exit(0); | 
|  | 98 | } | 
|  | 99 |  | 
|  | 100 | /* | 
|  | 101 | * This is the real entry point.  It passes the initial stack into | 
|  | 102 | * the C entry point. | 
|  | 103 | */ | 
|  | 104 | asm ( | 
|  | 105 | ".text\n" | 
|  | 106 | ".global _start\n" | 
|  | 107 | ".type _start,@function\n" | 
|  | 108 | "_start:\n\t" | 
|  | 109 | "mov %rsp,%rdi\n\t" | 
|  | 110 | "jmp c_main" | 
|  | 111 | ); |