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