linux/Documentation/vDSO/vdso_test.c
<<
>>
Prefs
   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
  18extern void *vdso_sym(const char *version, const char *name);
  19extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
  20extern void vdso_init_from_auxv(void *auxv);
  21
  22/* We need a libc functions... */
  23int 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. */
  39static 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
  50static inline void linux_exit(int code)
  51{
  52        asm volatile ("syscall" : : "a" (__NR_exit), "D" (code));
  53}
  54
  55void 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 */
 104asm (
 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        );
 112