1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * vdso_test.c: Sample code to test parse_vdso.c on x86 |
4 | * Copyright (c) 2011-2014 Andy Lutomirski |
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 | #include "parse_vdso.h" |
20 | |
21 | /* We need a libc functions... */ |
22 | int strcmp(const char *a, const char *b) |
23 | { |
24 | /* This implementation is buggy: it never returns -1. */ |
25 | while (*a || *b) { |
26 | if (*a != *b) |
27 | return 1; |
28 | if (*a == 0 || *b == 0) |
29 | return 1; |
30 | a++; |
31 | b++; |
32 | } |
33 | |
34 | return 0; |
35 | } |
36 | |
37 | /* ...and two syscalls. This is x86-specific. */ |
38 | static inline long x86_syscall3(long nr, long a0, long a1, long a2) |
39 | { |
40 | long ret; |
41 | #ifdef __x86_64__ |
42 | asm volatile ("syscall" : "=a" (ret) : "a" (nr), |
43 | "D" (a0), "S" (a1), "d" (a2) : |
44 | "cc" , "memory" , "rcx" , |
45 | "r8" , "r9" , "r10" , "r11" ); |
46 | #else |
47 | asm volatile ("int $0x80" : "=a" (ret) : "a" (nr), |
48 | "b" (a0), "c" (a1), "d" (a2) : |
49 | "cc" , "memory" ); |
50 | #endif |
51 | return ret; |
52 | } |
53 | |
54 | static inline long linux_write(int fd, const void *data, size_t len) |
55 | { |
56 | return x86_syscall3(nr: __NR_write, a0: fd, a1: (long)data, a2: (long)len); |
57 | } |
58 | |
59 | static inline void linux_exit(int code) |
60 | { |
61 | x86_syscall3(nr: __NR_exit, a0: code, a1: 0, a2: 0); |
62 | } |
63 | |
64 | void to_base10(char *lastdig, time_t n) |
65 | { |
66 | while (n) { |
67 | *lastdig = (n % 10) + '0'; |
68 | n /= 10; |
69 | lastdig--; |
70 | } |
71 | } |
72 | |
73 | __attribute__((externally_visible)) void c_main(void **stack) |
74 | { |
75 | /* Parse the stack */ |
76 | long argc = (long)*stack; |
77 | stack += argc + 2; |
78 | |
79 | /* Now we're pointing at the environment. Skip it. */ |
80 | while(*stack) |
81 | stack++; |
82 | stack++; |
83 | |
84 | /* Now we're pointing at auxv. Initialize the vDSO parser. */ |
85 | vdso_init_from_auxv(auxv: (void *)stack); |
86 | |
87 | /* Find gettimeofday. */ |
88 | typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); |
89 | gtod_t gtod = (gtod_t)vdso_sym(version: "LINUX_2.6" , name: "__vdso_gettimeofday" ); |
90 | |
91 | if (!gtod) |
92 | linux_exit(code: 1); |
93 | |
94 | struct timeval tv; |
95 | long ret = gtod(&tv, 0); |
96 | |
97 | if (ret == 0) { |
98 | char buf[] = "The time is .000000\n" ; |
99 | to_base10(buf + 31, tv.tv_sec); |
100 | to_base10(buf + 38, tv.tv_usec); |
101 | linux_write(1, buf, sizeof(buf) - 1); |
102 | } else { |
103 | linux_exit(code: ret); |
104 | } |
105 | |
106 | linux_exit(code: 0); |
107 | } |
108 | |
109 | /* |
110 | * This is the real entry point. It passes the initial stack into |
111 | * the C entry point. |
112 | */ |
113 | asm ( |
114 | ".text\n" |
115 | ".global _start\n" |
116 | ".type _start,@function\n" |
117 | "_start:\n\t" |
118 | #ifdef __x86_64__ |
119 | "mov %rsp,%rdi\n\t" |
120 | "jmp c_main" |
121 | #else |
122 | "push %esp\n\t" |
123 | "call c_main\n\t" |
124 | "int $3" |
125 | #endif |
126 | ); |
127 | |