1 | //===-- Unittests for VDSO ------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "hdr/signal_macros.h" |
10 | #include "hdr/time_macros.h" |
11 | #include "hdr/types/clockid_t.h" |
12 | #include "hdr/types/struct_sigaction.h" |
13 | #include "hdr/types/struct_timespec.h" |
14 | #include "hdr/types/struct_timeval.h" |
15 | #include "hdr/types/time_t.h" |
16 | #include "src/__support/OSUtil/linux/vdso.h" |
17 | #include "src/__support/OSUtil/syscall.h" |
18 | #include "src/__support/macros/properties/architectures.h" |
19 | #include "src/signal/raise.h" |
20 | #include "src/signal/sigaction.h" |
21 | #include "test/UnitTest/ErrnoSetterMatcher.h" |
22 | #include "test/UnitTest/Test.h" |
23 | #include <linux/time_types.h> |
24 | #include <sys/syscall.h> |
25 | |
26 | struct riscv_hwprobe { |
27 | int64_t key; |
28 | uint64_t value; |
29 | }; |
30 | |
31 | namespace LIBC_NAMESPACE_DECL { |
32 | // For x86_64, we explicitly test some traditional vdso symbols are indeed |
33 | // available. |
34 | |
35 | TEST(LlvmLibcOSUtilVDSOTest, GetTimeOfDay) { |
36 | vdso::TypedSymbol<vdso::VDSOSym::GetTimeOfDay> symbol; |
37 | if (!symbol) |
38 | return; |
39 | timeval tv; |
40 | EXPECT_EQ(symbol(&tv, nullptr), 0); |
41 | // hopefully people are not building time machines using our libc. |
42 | EXPECT_GT(tv.tv_sec, static_cast<decltype(tv.tv_sec)>(0)); |
43 | } |
44 | |
45 | TEST(LlvmLibcOSUtilVDSOTest, Time) { |
46 | vdso::TypedSymbol<vdso::VDSOSym::Time> symbol; |
47 | if (!symbol) |
48 | return; |
49 | time_t a, b; |
50 | EXPECT_GT(symbol(&a), static_cast<time_t>(0)); |
51 | EXPECT_GT(symbol(&b), static_cast<time_t>(0)); |
52 | EXPECT_GE(b, a); |
53 | } |
54 | |
55 | TEST(LlvmLibcOSUtilVDSOTest, ClockGetTime) { |
56 | vdso::TypedSymbol<vdso::VDSOSym::ClockGetTime> symbol; |
57 | if (!symbol) |
58 | return; |
59 | timespec a, b; |
60 | EXPECT_EQ(symbol(CLOCK_MONOTONIC, &a), 0); |
61 | EXPECT_EQ(symbol(CLOCK_MONOTONIC, &b), 0); |
62 | if (a.tv_sec == b.tv_sec) { |
63 | EXPECT_LT(a.tv_nsec, b.tv_nsec); |
64 | } else { |
65 | EXPECT_LT(a.tv_sec, b.tv_sec); |
66 | } |
67 | } |
68 | |
69 | TEST(LlvmLibcOSUtilVDSOTest, ClockGetTime64) { |
70 | vdso::TypedSymbol<vdso::VDSOSym::ClockGetTime64> symbol; |
71 | if (!symbol) |
72 | return; |
73 | // See kernel API at |
74 | // https://elixir.bootlin.com/linux/latest/source/tools/testing/selftests/vDSO/vdso_test_correctness.c#L155 |
75 | __kernel_timespec a, b; |
76 | EXPECT_EQ(symbol(CLOCK_MONOTONIC, &a), 0); |
77 | EXPECT_EQ(symbol(CLOCK_MONOTONIC, &b), 0); |
78 | if (a.tv_sec == b.tv_sec) { |
79 | EXPECT_LT(a.tv_nsec, b.tv_nsec); |
80 | } else { |
81 | EXPECT_LT(a.tv_sec, b.tv_sec); |
82 | } |
83 | } |
84 | |
85 | TEST(LlvmLibcOSUtilVDSOTest, ClockGetRes) { |
86 | vdso::TypedSymbol<vdso::VDSOSym::ClockGetRes> symbol; |
87 | if (!symbol) |
88 | return; |
89 | timespec res{}; |
90 | EXPECT_EQ(symbol(CLOCK_MONOTONIC, &res), 0); |
91 | EXPECT_TRUE(res.tv_sec > 0 || res.tv_nsec > 0); |
92 | } |
93 | |
94 | TEST(LlvmLibcOSUtilVDSOTest, GetCpu) { |
95 | // The kernel system call has a third argument, which should be passed as |
96 | // nullptr. |
97 | vdso::TypedSymbol<vdso::VDSOSym::GetCpu> symbol; |
98 | if (!symbol) |
99 | return; |
100 | unsigned cpu = static_cast<unsigned>(-1), node = static_cast<unsigned>(-1); |
101 | EXPECT_EQ(symbol(&cpu, &node, nullptr), 0); |
102 | EXPECT_GE(cpu, 0u); |
103 | EXPECT_GE(node, 0u); |
104 | } |
105 | |
106 | static bool flag = false; |
107 | static void sigprof_handler [[gnu::used]] (int) { flag = true; } |
108 | |
109 | TEST(LlvmLibcOSUtilVDSOTest, RtSigReturn) { |
110 | using namespace testing::ErrnoSetterMatcher; |
111 | // must use struct since there is a function of the same name in the same |
112 | // scope. |
113 | struct sigaction sa {}; |
114 | struct sigaction old_sa {}; |
115 | sa.sa_handler = sigprof_handler; |
116 | sa.sa_flags = SA_RESTORER; |
117 | vdso::TypedSymbol<vdso::VDSOSym::RTSigReturn> symbol; |
118 | if (!symbol) |
119 | return; |
120 | sa.sa_restorer = symbol; |
121 | ASSERT_THAT(LIBC_NAMESPACE::sigaction(SIGPROF, &sa, &old_sa), Succeeds()); |
122 | raise(SIGPROF); |
123 | ASSERT_TRUE(flag); |
124 | flag = false; |
125 | ASSERT_THAT(LIBC_NAMESPACE::sigaction(SIGPROF, &old_sa, nullptr), Succeeds()); |
126 | } |
127 | |
128 | TEST(LlvmLibcOSUtilVDSOTest, FlushICache) { |
129 | vdso::TypedSymbol<vdso::VDSOSym::FlushICache> symbol; |
130 | if (!symbol) |
131 | return; |
132 | char buf[512]; |
133 | // we just check that the flush will not panic the program. |
134 | // the flags part only take 0/1 as up to kernel 6.10, which is used to |
135 | // indicate whether the flush is local to the core or global. |
136 | symbol(buf, buf + sizeof(buf), 0); |
137 | symbol(buf, buf + sizeof(buf), 1); |
138 | } |
139 | |
140 | // https://docs.kernel.org/6.5/riscv/hwprobe.html |
141 | TEST(LlvmLibcOSUtilVDSOTest, RiscvHwProbe) { |
142 | using namespace testing::ErrnoSetterMatcher; |
143 | vdso::TypedSymbol<vdso::VDSOSym::RiscvHwProbe> symbol; |
144 | if (!symbol) |
145 | return; |
146 | // If a key is unknown to the kernel, its key field will be cleared to -1, and |
147 | // its value set to 0. We expect probes.value are all 0. |
148 | // Usermode can supply NULL for cpus and 0 for cpu_count as a shortcut for all |
149 | // online CPUs |
150 | riscv_hwprobe probes[2] = {{-1, 1}, {-1, 1}}; |
151 | ASSERT_THAT(symbol(/*pairs=*/probes, /*count=*/2, /*cpusetsize=*/0, |
152 | /*cpuset=*/nullptr, |
153 | /*flags=*/0), |
154 | Succeeds()); |
155 | for (auto &probe : probes) { |
156 | EXPECT_EQ(probe.key, static_cast<decltype(probe.key)>(-1)); |
157 | EXPECT_EQ(probe.value, static_cast<decltype(probe.value)>(0)); |
158 | } |
159 | } |
160 | |
161 | } // namespace LIBC_NAMESPACE_DECL |
162 | |