Blame view

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