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