Home | History | Annotate | Download | only in vDSO
      1 /*
      2  * vdso_test.c: Sample code to test parse_vdso.c on x86
      3  * Copyright (c) 2011-2014 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 -lgcc_s
      9  *      vdso_standalone_test_x86.c parse_vdso.c
     10  * to generate a small binary.  On x86_64, you can omit -lgcc_s
     11  * if you want the binary to be completely standalone.
     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 
     39 /* ...and two syscalls.  This is x86-specific. */
     40 static inline long x86_syscall3(long nr, long a0, long a1, long a2)
     41 {
     42 	long ret;
     43 #ifdef __x86_64__
     44 	asm volatile ("syscall" : "=a" (ret) : "a" (nr),
     45 		      "D" (a0), "S" (a1), "d" (a2) :
     46 		      "cc", "memory", "rcx",
     47 		      "r8", "r9", "r10", "r11" );
     48 #else
     49 	asm volatile ("int $0x80" : "=a" (ret) : "a" (nr),
     50 		      "b" (a0), "c" (a1), "d" (a2) :
     51 		      "cc", "memory" );
     52 #endif
     53 	return ret;
     54 }
     55 
     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 
     61 static inline void linux_exit(int code)
     62 {
     63 	x86_syscall3(__NR_exit, code, 0, 0);
     64 }
     65 
     66 void to_base10(char *lastdig, time_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"
    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
    128 	);
    129