Blame view

kernel/linux-rt-4.4.41/Documentation/vDSO/vdso_standalone_test_x86.c 2.79 KB
5113f6f70   김현기   kernel add
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  /*
   * vdso_test.c: Sample code to test parse_vdso.c on x86
   * Copyright (c) 2011-2014 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 -lgcc_s
   *      vdso_standalone_test_x86.c parse_vdso.c
   * to generate a small binary.  On x86_64, you can omit -lgcc_s
   * if you want the binary to be completely standalone.
   */
  
  #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-specific. */
  static inline long x86_syscall3(long nr, long a0, long a1, long a2)
  {
  	long ret;
  #ifdef __x86_64__
  	asm volatile ("syscall" : "=a" (ret) : "a" (nr),
  		      "D" (a0), "S" (a1), "d" (a2) :
  		      "cc", "memory", "rcx",
  		      "r8", "r9", "r10", "r11" );
  #else
  	asm volatile ("int $0x80" : "=a" (ret) : "a" (nr),
  		      "b" (a0), "c" (a1), "d" (a2) :
  		      "cc", "memory" );
  #endif
  	return ret;
  }
  
  static inline long linux_write(int fd, const void *data, size_t len)
  {
  	return x86_syscall3(__NR_write, fd, (long)data, (long)len);
  }
  
  static inline void linux_exit(int code)
  {
  	x86_syscall3(__NR_exit, code, 0, 0);
  }
  
  void to_base10(char *lastdig, time_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"
  #ifdef __x86_64__
  	"mov %rsp,%rdi
  \t"
  	"jmp c_main"
  #else
  	"push %esp
  \t"
  	"call c_main
  \t"
  	"int $3"
  #endif
  	);