1 | #include <errno.h> |
2 | #include <stdint.h> |
3 | #include <time.h> |
4 | #include "atomic.h" |
5 | #include "musl_features.h" |
6 | #include "syscall.h" |
7 | |
8 | #ifdef VDSO_CGT_SYM |
9 | |
10 | static void *volatile vdso_func; |
11 | |
12 | #ifdef VDSO_CGT32_SYM |
13 | static void *volatile vdso_func_32; |
14 | static int cgt_time32_wrap(clockid_t clk, struct timespec *ts) |
15 | { |
16 | long ts32[2]; |
17 | int (*f)(clockid_t, long[2]) = |
18 | (int (*)(clockid_t, long[2]))vdso_func_32; |
19 | int r = f(clk, ts32); |
20 | if (!r) { |
21 | /* Fallback to syscalls if time32 overflowed. Maybe |
22 | * we lucked out and somehow migrated to a kernel with |
23 | * time64 syscalls available. */ |
24 | if (ts32[0] < 0) { |
25 | a_cas_p(&vdso_func, (void *)cgt_time32_wrap, 0); |
26 | return -ENOSYS; |
27 | } |
28 | ts->tv_sec = ts32[0]; |
29 | ts->tv_nsec = ts32[1]; |
30 | } |
31 | return r; |
32 | } |
33 | #endif |
34 | |
35 | static int cgt_init(clockid_t clk, struct timespec *ts) |
36 | { |
37 | void *p = __vdsosym(VDSO_CGT_VER, VDSO_CGT_SYM); |
38 | #ifdef VDSO_CGT32_SYM |
39 | if (!p) { |
40 | void *q = __vdsosym(VDSO_CGT32_VER, VDSO_CGT32_SYM); |
41 | if (q) { |
42 | a_cas_p(&vdso_func_32, 0, q); |
43 | p = cgt_time32_wrap; |
44 | } |
45 | } |
46 | #endif |
47 | int (*f)(clockid_t, struct timespec *) = |
48 | (int (*)(clockid_t, struct timespec *))p; |
49 | a_cas_p(&vdso_func, (void *)cgt_init, p); |
50 | return f ? f(clk, ts) : -ENOSYS; |
51 | } |
52 | |
53 | static void *volatile vdso_func = (void *)cgt_init; |
54 | |
55 | #endif |
56 | |
57 | int __clock_gettime(clockid_t clk, struct timespec *ts) |
58 | { |
59 | int r; |
60 | |
61 | #ifdef VDSO_CGT_SYM |
62 | int (*f)(clockid_t, struct timespec *) = |
63 | (int (*)(clockid_t, struct timespec *))vdso_func; |
64 | if (f) { |
65 | r = f(clk, ts); |
66 | if (!r) return r; |
67 | if (r == -EINVAL) return __syscall_ret(r); |
68 | /* Fall through on errors other than EINVAL. Some buggy |
69 | * vdso implementations return ENOSYS for clocks they |
70 | * can't handle, rather than making the syscall. This |
71 | * also handles the case where cgt_init fails to find |
72 | * a vdso function to use. */ |
73 | } |
74 | #endif |
75 | |
76 | #ifdef SYS_clock_gettime64 |
77 | r = -ENOSYS; |
78 | if (sizeof(time_t) > 4) |
79 | r = __syscall(SYS_clock_gettime64, clk, ts); |
80 | if (SYS_clock_gettime == SYS_clock_gettime64 || r!=-ENOSYS) |
81 | return __syscall_ret(r); |
82 | long ts32[2]; |
83 | r = __syscall(SYS_clock_gettime, clk, ts32); |
84 | if (r==-ENOSYS && clk==CLOCK_REALTIME) { |
85 | r = __syscall(SYS_gettimeofday, ts32, 0); |
86 | ts32[1] *= 1000; |
87 | } |
88 | if (!r) { |
89 | ts->tv_sec = ts32[0]; |
90 | ts->tv_nsec = ts32[1]; |
91 | return r; |
92 | } |
93 | return __syscall_ret(r); |
94 | #else |
95 | r = __syscall(SYS_clock_gettime, clk, ts); |
96 | if (r == -ENOSYS) { |
97 | if (clk == CLOCK_REALTIME) { |
98 | __syscall(SYS_gettimeofday, ts, 0); |
99 | ts->tv_nsec = (int)ts->tv_nsec * 1000; |
100 | return 0; |
101 | } |
102 | r = -EINVAL; |
103 | } |
104 | return __syscall_ret(r); |
105 | #endif |
106 | } |
107 | |
108 | weak_alias(__clock_gettime, clock_gettime); |
109 | |