Skip to content

Commit db4ccf5

Browse files
Yifan ZhuSchrodingerZhu
authored andcommitted
add typed symbol
1 parent a275cb8 commit db4ccf5

File tree

3 files changed

+129
-63
lines changed

3 files changed

+129
-63
lines changed

libc/src/__support/OSUtil/linux/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ add_object_library(
3737
libc.src.__support.CPP.string_view
3838
libc.src.__support.threads.callonce
3939
libc.src.__support.threads.linux.futex_word_type
40+
libc.hdr.types.struct_timeval
41+
libc.hdr.types.struct_timespec
42+
libc.hdr.types.clockid_t
43+
libc.hdr.types.time_t
4044
libc.src.errno.errno
4145
libc.src.sys.auxv.getauxval
4246
)

libc/src/__support/OSUtil/linux/vdso.h

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,37 @@
77
//===----------------------------------------------------------------------===//
88
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_VDSO_H
99
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_VDSO_H
10+
#include "hdr/types/clock_t.h"
11+
#include "hdr/types/clockid_t.h"
12+
#include "hdr/types/struct_timespec.h"
13+
#include "hdr/types/struct_timeval.h"
14+
#include "hdr/types/time_t.h"
1015
#include "src/__support/CPP/array.h"
1116
#include "src/__support/common.h"
1217
#include "src/__support/macros/attributes.h"
1318
#include "src/__support/macros/properties/architectures.h"
1419
#include "src/__support/threads/callonce.h"
1520

21+
// NOLINTBEGIN(llvmlibc-implementation-in-namespace)
22+
// TODO: some of the following can be defined via proxy headers.
23+
struct __kernel_timespec;
24+
struct timezone;
25+
struct riscv_hwprobe;
26+
struct getcpu_cache;
27+
struct cpu_set_t;
28+
// NOLINTEND(llvmlibc-implementation-in-namespace)
29+
1630
namespace LIBC_NAMESPACE {
1731
namespace vdso {
32+
33+
#ifdef __clang__
34+
__extension__ template <typename T> using NullablePtr = T *_Nullable;
35+
__extension__ template <typename T> using NonNullPtr = T *_Nonnull;
36+
#else
37+
template <typename T> using NullablePtr = T *;
38+
template <typename T> using NonNullPtr = T *;
39+
#endif
40+
1841
enum class VDSOSym {
1942
ClockGetTime,
2043
ClockGetTime64,
@@ -44,6 +67,52 @@ enum class VDSOSym {
4467

4568
namespace LIBC_NAMESPACE {
4669
namespace vdso {
70+
71+
template <VDSOSym sym> struct VDSOTypeDispatch {
72+
using Type = void *;
73+
};
74+
75+
template <> struct VDSOTypeDispatch<VDSOSym::ClockGetTime> {
76+
using Type = int (*)(clockid_t, NonNullPtr<timespec>);
77+
};
78+
79+
template <> struct VDSOTypeDispatch<VDSOSym::ClockGetTime64> {
80+
using Type = int (*)(clockid_t, NonNullPtr<__kernel_timespec>);
81+
};
82+
83+
template <> struct VDSOTypeDispatch<VDSOSym::GetTimeOfDay> {
84+
using Type = int (*)(NonNullPtr<timeval> __restrict,
85+
NullablePtr<timezone> __restrict);
86+
};
87+
88+
template <> struct VDSOTypeDispatch<VDSOSym::GetCpu> {
89+
using Type = int (*)(NullablePtr<unsigned>, NullablePtr<unsigned>,
90+
NullablePtr<getcpu_cache>);
91+
};
92+
93+
template <> struct VDSOTypeDispatch<VDSOSym::Time> {
94+
using Type = time_t (*)(NullablePtr<time_t>);
95+
};
96+
97+
template <> struct VDSOTypeDispatch<VDSOSym::ClockGetRes> {
98+
using Type = int (*)(clockid_t, NullablePtr<timespec>);
99+
};
100+
101+
template <> struct VDSOTypeDispatch<VDSOSym::RTSigReturn> {
102+
using Type = void (*)(void);
103+
};
104+
105+
template <> struct VDSOTypeDispatch<VDSOSym::FlushICache> {
106+
using Type = void (*)(NullablePtr<void>, NullablePtr<void>, unsigned int);
107+
};
108+
109+
template <> struct VDSOTypeDispatch<VDSOSym::RiscvHwProbe> {
110+
using Type = int (*)(NullablePtr<riscv_hwprobe> __restrict, size_t, size_t,
111+
NullablePtr<cpu_set_t> __restrict, unsigned);
112+
};
113+
114+
template <VDSOSym sym> using VDSOSymType = typename VDSOTypeDispatch<sym>::Type;
115+
47116
class Symbol {
48117
VDSOSym sym;
49118

@@ -71,14 +140,23 @@ class Symbol {
71140
static VDSOArray global_cache;
72141
static void initialize_vdso_global_cache();
73142

74-
public:
75143
template <typename T> LIBC_INLINE T get() {
76144
if (name().empty() || !is_valid())
77145
return nullptr;
78146

79147
callonce(&once_flag, Symbol::initialize_vdso_global_cache);
80148
return cpp::bit_cast<T>(global_cache[*this]);
81149
}
150+
template <VDSOSym sym> friend struct TypedSymbol;
151+
};
152+
153+
template <VDSOSym sym> struct TypedSymbol {
154+
LIBC_INLINE constexpr operator VDSOSymType<sym>() const {
155+
return Symbol{sym}.get<VDSOSymType<sym>>();
156+
}
157+
template <typename... Args> LIBC_INLINE auto operator()(Args &&...args) {
158+
return this->operator VDSOSymType<sym>()(cpp::forward<Args>(args)...);
159+
}
82160
};
83161

84162
} // namespace vdso

libc/test/src/__support/OSUtil/linux/vdso_test.cpp

Lines changed: 46 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -19,60 +19,60 @@
1919
#include "src/signal/raise.h"
2020
#include "src/signal/sigaction.h"
2121
#include "test/UnitTest/ErrnoSetterMatcher.h"
22-
#include "test/UnitTest/LibcTest.h" vdso_test
22+
#include "test/UnitTest/LibcTest.h"
2323
#include "test/UnitTest/Test.h"
2424
#include <linux/time_types.h>
2525
#include <sys/syscall.h>
2626

27+
struct riscv_hwprobe {
28+
int64_t key;
29+
uint64_t value;
30+
};
31+
2732
namespace LIBC_NAMESPACE {
2833
// For x86_64, we explicitly test some traditional vdso symbols are indeed
2934
// available.
3035

3136
TEST(LlvmLibcOSUtilVDSOTest, GetTimeOfDay) {
32-
using FuncTy = int (*)(timeval *, struct timezone *);
33-
vdso::Symbol symbol{vdso::VDSOSym::GetTimeOfDay};
34-
auto func = symbol.get<FuncTy>();
37+
vdso::TypedSymbol<vdso::VDSOSym::GetTimeOfDay> symbol;
38+
3539
#ifdef LIBC_TARGET_ARCH_IS_X86
36-
ASSERT_NE(func, static_cast<FuncTy>(nullptr));
40+
ASSERT_TRUE(symbol != nullptr);
3741
#else
38-
if (func == nullptr)
42+
if (!symbol)
3943
return;
4044
#endif
4145
timeval tv;
42-
EXPECT_EQ(func(&tv, nullptr), 0);
46+
EXPECT_EQ(symbol(&tv, nullptr), 0);
4347
// hopefully people are not building time machines using our libc.
4448
EXPECT_GT(tv.tv_sec, static_cast<decltype(tv.tv_sec)>(0));
4549
}
4650

4751
TEST(LlvmLibcOSUtilVDSOTest, Time) {
48-
using FuncTy = time_t (*)(time_t *);
49-
vdso::Symbol symbol{vdso::VDSOSym::Time};
50-
auto func = symbol.get<FuncTy>();
52+
vdso::TypedSymbol<vdso::VDSOSym::Time> symbol;
5153
#ifdef LIBC_TARGET_ARCH_IS_X86
52-
ASSERT_NE(func, static_cast<FuncTy>(nullptr));
54+
ASSERT_TRUE(symbol != nullptr);
5355
#else
54-
if (func == nullptr)
56+
if (!symbol)
5557
return;
5658
#endif
5759
time_t a, b;
58-
EXPECT_GT(func(&a), static_cast<time_t>(0));
59-
EXPECT_GT(func(&b), static_cast<time_t>(0));
60+
EXPECT_GT(symbol(&a), static_cast<time_t>(0));
61+
EXPECT_GT(symbol(&b), static_cast<time_t>(0));
6062
EXPECT_GE(b, a);
6163
}
6264

6365
TEST(LlvmLibcOSUtilVDSOTest, ClockGetTime) {
64-
using FuncTy = int (*)(clockid_t, timespec *);
65-
vdso::Symbol symbol{vdso::VDSOSym::ClockGetTime};
66-
auto func = symbol.get<FuncTy>();
66+
vdso::TypedSymbol<vdso::VDSOSym::ClockGetTime> symbol;
6767
#ifdef LIBC_TARGET_ARCH_IS_X86
68-
ASSERT_NE(func, static_cast<FuncTy>(nullptr));
68+
ASSERT_TRUE(symbol != nullptr);
6969
#else
70-
if (func == nullptr)
70+
if (!symbol)
7171
return;
7272
#endif
7373
timespec a, b;
74-
EXPECT_EQ(func(CLOCK_MONOTONIC, &a), 0);
75-
EXPECT_EQ(func(CLOCK_MONOTONIC, &b), 0);
74+
EXPECT_EQ(symbol(CLOCK_MONOTONIC, &a), 0);
75+
EXPECT_EQ(symbol(CLOCK_MONOTONIC, &b), 0);
7676
if (a.tv_sec == b.tv_sec) {
7777
EXPECT_LT(a.tv_nsec, b.tv_nsec);
7878
} else {
@@ -81,16 +81,14 @@ TEST(LlvmLibcOSUtilVDSOTest, ClockGetTime) {
8181
}
8282

8383
TEST(LlvmLibcOSUtilVDSOTest, ClockGetTime64) {
84-
using FuncTy = int (*)(clockid_t, __kernel_timespec *);
85-
vdso::Symbol symbol{vdso::VDSOSym::ClockGetTime64};
86-
auto func = symbol.get<FuncTy>();
87-
if (func == nullptr)
84+
vdso::TypedSymbol<vdso::VDSOSym::ClockGetTime64> symbol;
85+
if (!symbol)
8886
return;
8987
// See kernel API at
9088
// https://elixir.bootlin.com/linux/latest/source/tools/testing/selftests/vDSO/vdso_test_correctness.c#L155
9189
__kernel_timespec a, b;
92-
EXPECT_EQ(func(CLOCK_MONOTONIC, &a), 0);
93-
EXPECT_EQ(func(CLOCK_MONOTONIC, &b), 0);
90+
EXPECT_EQ(symbol(CLOCK_MONOTONIC, &a), 0);
91+
EXPECT_EQ(symbol(CLOCK_MONOTONIC, &b), 0);
9492
if (a.tv_sec == b.tv_sec) {
9593
EXPECT_LT(a.tv_nsec, b.tv_nsec);
9694
} else {
@@ -99,28 +97,24 @@ TEST(LlvmLibcOSUtilVDSOTest, ClockGetTime64) {
9997
}
10098

10199
TEST(LlvmLibcOSUtilVDSOTest, ClockGetRes) {
102-
using FuncTy = int (*)(clockid_t, timespec *);
103-
vdso::Symbol symbol{vdso::VDSOSym::ClockGetRes};
104-
auto func = symbol.get<FuncTy>();
105-
if (func == nullptr)
100+
vdso::TypedSymbol<vdso::VDSOSym::ClockGetRes> symbol;
101+
if (!symbol)
106102
return;
107103
timespec res{};
108-
EXPECT_EQ(func(CLOCK_MONOTONIC, &res), 0);
104+
EXPECT_EQ(symbol(CLOCK_MONOTONIC, &res), 0);
109105
EXPECT_TRUE(res.tv_sec > 0 || res.tv_nsec > 0);
110106
}
111107

112108
TEST(LlvmLibcOSUtilVDSOTest, GetCpu) {
113109
// The kernel system call has a third argument, which should be passed as
114110
// nullptr.
115-
using FuncTy = int (*)(int *, int *, void *);
116-
vdso::Symbol symbol{vdso::VDSOSym::GetCpu};
117-
auto func = symbol.get<FuncTy>();
118-
if (func == nullptr)
111+
vdso::TypedSymbol<vdso::VDSOSym::GetCpu> symbol;
112+
if (!symbol)
119113
return;
120-
int cpu = -1, node = -1;
121-
EXPECT_EQ(func(&cpu, &node, nullptr), 0);
122-
EXPECT_GE(cpu, 0);
123-
EXPECT_GE(node, 0);
114+
unsigned cpu = static_cast<unsigned>(-1), node = static_cast<unsigned>(-1);
115+
EXPECT_EQ(symbol(&cpu, &node, nullptr), 0);
116+
EXPECT_GE(cpu, 0u);
117+
EXPECT_GE(node, 0u);
124118
}
125119

126120
static bool flag = false;
@@ -134,11 +128,10 @@ TEST(LlvmLibcOSUtilVDSOTest, RtSigReturn) {
134128
struct sigaction old_sa {};
135129
sa.sa_handler = sigprof_handler;
136130
sa.sa_flags = SA_RESTORER;
137-
vdso::Symbol symbol{vdso::VDSOSym::RTSigReturn};
138-
auto func = symbol.get<decltype(sa.sa_restorer)>();
139-
if (func == nullptr)
131+
vdso::TypedSymbol<vdso::VDSOSym::RTSigReturn> symbol;
132+
if (!symbol)
140133
return;
141-
sa.sa_restorer = func;
134+
sa.sa_restorer = symbol;
142135
ASSERT_THAT(LIBC_NAMESPACE::sigaction(SIGPROF, &sa, &old_sa), Succeeds());
143136
raise(SIGPROF);
144137
ASSERT_TRUE(flag);
@@ -147,40 +140,31 @@ TEST(LlvmLibcOSUtilVDSOTest, RtSigReturn) {
147140
}
148141

149142
TEST(LlvmLibcOSUtilVDSOTest, FlushICache) {
150-
using FuncTy = void (*)(void *, void *, unsigned long);
151-
vdso::Symbol symbol{vdso::VDSOSym::FlushICache};
152-
auto func = symbol.get<FuncTy>();
153-
if (func == nullptr)
143+
vdso::TypedSymbol<vdso::VDSOSym::FlushICache> symbol;
144+
if (!symbol)
154145
return;
155146
char buf[512];
156147
// we just check that the flush will not panic the program.
157148
// the flags part only take 0/1 as up to kernel 6.10, which is used to
158149
// indicate whether the flush is local to the core or global.
159-
func(buf, buf + sizeof(buf), 0);
160-
func(buf, buf + sizeof(buf), 1);
150+
symbol(buf, buf + sizeof(buf), 0);
151+
symbol(buf, buf + sizeof(buf), 1);
161152
}
162153

163154
// https://docs.kernel.org/6.5/riscv/hwprobe.html
164155
TEST(LlvmLibcOSUtilVDSOTest, RiscvHwProbe) {
165156
using namespace testing::ErrnoSetterMatcher;
166-
struct riscv_hwprobe {
167-
int64_t key;
168-
uint64_t value;
169-
};
170-
using FuncTy =
171-
long (*)(riscv_hwprobe *, size_t, size_t, struct cpu_set_t *, unsigned);
172-
vdso::Symbol symbol{vdso::VDSOSym::RiscvHwProbe};
173-
auto func = symbol.get<FuncTy>();
174-
if (func == nullptr)
157+
vdso::TypedSymbol<vdso::VDSOSym::RiscvHwProbe> symbol;
158+
if (!symbol)
175159
return;
176160
// If a key is unknown to the kernel, its key field will be cleared to -1, and
177161
// its value set to 0. We expect probes.value are all 0.
178162
// Usermode can supply NULL for cpus and 0 for cpu_count as a shortcut for all
179163
// online CPUs
180164
riscv_hwprobe probes[2] = {{-1, 1}, {-1, 1}};
181-
ASSERT_THAT(func(/*pairs=*/probes, /*count=*/2, /*cpusetsize=*/0,
182-
/*cpuset=*/nullptr,
183-
/*flags=*/0),
165+
ASSERT_THAT(symbol(/*pairs=*/probes, /*count=*/2, /*cpusetsize=*/0,
166+
/*cpuset=*/nullptr,
167+
/*flags=*/0),
184168
Succeeds());
185169
for (auto &probe : probes) {
186170
EXPECT_EQ(probe.key, static_cast<decltype(probe.key)>(-1));

0 commit comments

Comments
 (0)