1 | //! Implement syscalls using the vDSO. |
2 | //! |
3 | //! <https://man7.org/linux/man-pages/man7/vdso.7.html> |
4 | //! |
5 | //! # Safety |
6 | //! |
7 | //! Similar to syscalls.rs, this file performs raw system calls, and sometimes |
8 | //! passes them uninitialized memory buffers. This file also calls vDSO |
9 | //! functions. |
10 | #![allow (unsafe_code)] |
11 | #![allow (clippy::missing_transmute_annotations)] |
12 | |
13 | #[cfg (target_arch = "x86" )] |
14 | use super::reg::{ArgReg, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0}; |
15 | use super::vdso; |
16 | #[cfg (target_arch = "x86" )] |
17 | use core::arch::global_asm; |
18 | #[cfg (feature = "process" )] |
19 | #[cfg (any( |
20 | target_arch = "x86_64" , |
21 | target_arch = "x86" , |
22 | target_arch = "riscv64" , |
23 | target_arch = "powerpc64" |
24 | ))] |
25 | use core::ffi::c_void; |
26 | use core::mem::transmute; |
27 | use core::ptr::null_mut; |
28 | use core::sync::atomic::AtomicPtr; |
29 | use core::sync::atomic::Ordering::Relaxed; |
30 | #[cfg (target_pointer_width = "32" )] |
31 | #[cfg (feature = "time" )] |
32 | use linux_raw_sys::general::timespec as __kernel_old_timespec; |
33 | #[cfg (any( |
34 | all( |
35 | feature = "process" , |
36 | any( |
37 | target_arch = "x86_64" , |
38 | target_arch = "x86" , |
39 | target_arch = "riscv64" , |
40 | target_arch = "powerpc64" |
41 | ) |
42 | ), |
43 | feature = "time" |
44 | ))] |
45 | use {super::c, super::conv::ret, core::mem::MaybeUninit}; |
46 | #[cfg (feature = "time" )] |
47 | use { |
48 | super::conv::c_int, |
49 | crate::clockid::{ClockId, DynamicClockId}, |
50 | crate::io, |
51 | crate::timespec::Timespec, |
52 | linux_raw_sys::general::{__kernel_clockid_t, __kernel_timespec}, |
53 | }; |
54 | |
55 | #[cfg (feature = "time" )] |
56 | #[inline ] |
57 | pub(crate) fn clock_gettime(which_clock: ClockId) -> __kernel_timespec { |
58 | // SAFETY: `CLOCK_GETTIME` contains either null or the address of a |
59 | // function with an ABI like libc `clock_gettime`, and calling it has the |
60 | // side effect of writing to the result buffer, and no others. |
61 | unsafe { |
62 | let mut result: MaybeUninit<__kernel_timespec> = MaybeUninit::<__kernel_timespec>::uninit(); |
63 | let callee: unsafe fn(i32, *mut __kernel_timespec) -> … = match transmute(src:CLOCK_GETTIME.load(order:Relaxed)) { |
64 | Some(callee: unsafe fn(i32, *mut __kernel_timespec) -> …) => callee, |
65 | None => init_clock_gettime(), |
66 | }; |
67 | let r0: i32 = callee(which_clock as c::c_int, result.as_mut_ptr()); |
68 | // The `ClockId` enum only contains clocks which never fail. It may be |
69 | // tempting to change this to `debug_assert_eq`, however they can still |
70 | // fail on uncommon kernel configs, so we leave this in place to ensure |
71 | // that we don't execute undefined behavior if they ever do fail. |
72 | assert_eq!(r0, 0); |
73 | result.assume_init() |
74 | } |
75 | } |
76 | |
77 | #[cfg (feature = "time" )] |
78 | #[inline ] |
79 | pub(crate) fn clock_gettime_dynamic(which_clock: DynamicClockId<'_>) -> io::Result<Timespec> { |
80 | let id = match which_clock { |
81 | DynamicClockId::Known(id) => id as __kernel_clockid_t, |
82 | |
83 | DynamicClockId::Dynamic(fd) => { |
84 | // See `FD_TO_CLOCKID` in Linux's `clock_gettime` documentation. |
85 | use crate::backend::fd::AsRawFd; |
86 | const CLOCKFD: i32 = 3; |
87 | ((!fd.as_raw_fd() << 3) | CLOCKFD) as __kernel_clockid_t |
88 | } |
89 | |
90 | DynamicClockId::RealtimeAlarm => c::CLOCK_REALTIME_ALARM as __kernel_clockid_t, |
91 | DynamicClockId::Tai => c::CLOCK_TAI as __kernel_clockid_t, |
92 | DynamicClockId::Boottime => c::CLOCK_BOOTTIME as __kernel_clockid_t, |
93 | DynamicClockId::BoottimeAlarm => c::CLOCK_BOOTTIME_ALARM as __kernel_clockid_t, |
94 | }; |
95 | |
96 | // SAFETY: `CLOCK_GETTIME` contains either null or the address of a |
97 | // function with an ABI like libc `clock_gettime`, and calling it has the |
98 | // side effect of writing to the result buffer, and no others. |
99 | unsafe { |
100 | const EINVAL: c::c_int = -(c::EINVAL as c::c_int); |
101 | let mut timespec = MaybeUninit::<Timespec>::uninit(); |
102 | let callee = match transmute(CLOCK_GETTIME.load(Relaxed)) { |
103 | Some(callee) => callee, |
104 | None => init_clock_gettime(), |
105 | }; |
106 | match callee(id, timespec.as_mut_ptr()) { |
107 | 0 => (), |
108 | EINVAL => return Err(io::Errno::INVAL), |
109 | _ => _rustix_clock_gettime_via_syscall(id, timespec.as_mut_ptr())?, |
110 | } |
111 | Ok(timespec.assume_init()) |
112 | } |
113 | } |
114 | |
115 | #[cfg (feature = "process" )] |
116 | #[cfg (any( |
117 | target_arch = "x86_64" , |
118 | target_arch = "x86" , |
119 | target_arch = "riscv64" , |
120 | target_arch = "powerpc64" |
121 | ))] |
122 | #[inline ] |
123 | pub(crate) fn sched_getcpu() -> usize { |
124 | // SAFETY: `GETCPU` contains either null or the address of a function with |
125 | // an ABI like libc `getcpu`, and calling it has the side effect of writing |
126 | // to the result buffers, and no others. |
127 | unsafe { |
128 | let mut cpu: MaybeUninit = MaybeUninit::<u32>::uninit(); |
129 | let callee: unsafe fn(*mut u32, *mut …, …) -> … = match transmute(src:GETCPU.load(order:Relaxed)) { |
130 | Some(callee: unsafe fn(*mut u32, *mut …, …) -> …) => callee, |
131 | None => init_getcpu(), |
132 | }; |
133 | let r0: i32 = callee(cpu.as_mut_ptr(), null_mut(), null_mut()); |
134 | debug_assert_eq!(r0, 0); |
135 | cpu.assume_init() as usize |
136 | } |
137 | } |
138 | |
139 | #[cfg (target_arch = "x86" )] |
140 | pub(super) mod x86_via_vdso { |
141 | use super::{transmute, ArgReg, Relaxed, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0}; |
142 | use crate::backend::arch::asm; |
143 | |
144 | #[inline ] |
145 | pub(in crate::backend) unsafe fn syscall0(nr: SyscallNumber<'_>) -> RetReg<R0> { |
146 | let callee = match transmute(super::SYSCALL.load(Relaxed)) { |
147 | Some(callee) => callee, |
148 | None => super::init_syscall(), |
149 | }; |
150 | asm::indirect_syscall0(callee, nr) |
151 | } |
152 | |
153 | #[inline ] |
154 | pub(in crate::backend) unsafe fn syscall1<'a>( |
155 | nr: SyscallNumber<'a>, |
156 | a0: ArgReg<'a, A0>, |
157 | ) -> RetReg<R0> { |
158 | let callee = match transmute(super::SYSCALL.load(Relaxed)) { |
159 | Some(callee) => callee, |
160 | None => super::init_syscall(), |
161 | }; |
162 | asm::indirect_syscall1(callee, nr, a0) |
163 | } |
164 | |
165 | #[inline ] |
166 | pub(in crate::backend) unsafe fn syscall1_noreturn<'a>( |
167 | nr: SyscallNumber<'a>, |
168 | a0: ArgReg<'a, A0>, |
169 | ) -> ! { |
170 | let callee = match transmute(super::SYSCALL.load(Relaxed)) { |
171 | Some(callee) => callee, |
172 | None => super::init_syscall(), |
173 | }; |
174 | asm::indirect_syscall1_noreturn(callee, nr, a0) |
175 | } |
176 | |
177 | #[inline ] |
178 | pub(in crate::backend) unsafe fn syscall2<'a>( |
179 | nr: SyscallNumber<'a>, |
180 | a0: ArgReg<'a, A0>, |
181 | a1: ArgReg<'a, A1>, |
182 | ) -> RetReg<R0> { |
183 | let callee = match transmute(super::SYSCALL.load(Relaxed)) { |
184 | Some(callee) => callee, |
185 | None => super::init_syscall(), |
186 | }; |
187 | asm::indirect_syscall2(callee, nr, a0, a1) |
188 | } |
189 | |
190 | #[inline ] |
191 | pub(in crate::backend) unsafe fn syscall3<'a>( |
192 | nr: SyscallNumber<'a>, |
193 | a0: ArgReg<'a, A0>, |
194 | a1: ArgReg<'a, A1>, |
195 | a2: ArgReg<'a, A2>, |
196 | ) -> RetReg<R0> { |
197 | let callee = match transmute(super::SYSCALL.load(Relaxed)) { |
198 | Some(callee) => callee, |
199 | None => super::init_syscall(), |
200 | }; |
201 | asm::indirect_syscall3(callee, nr, a0, a1, a2) |
202 | } |
203 | |
204 | #[inline ] |
205 | pub(in crate::backend) unsafe fn syscall4<'a>( |
206 | nr: SyscallNumber<'a>, |
207 | a0: ArgReg<'a, A0>, |
208 | a1: ArgReg<'a, A1>, |
209 | a2: ArgReg<'a, A2>, |
210 | a3: ArgReg<'a, A3>, |
211 | ) -> RetReg<R0> { |
212 | let callee = match transmute(super::SYSCALL.load(Relaxed)) { |
213 | Some(callee) => callee, |
214 | None => super::init_syscall(), |
215 | }; |
216 | asm::indirect_syscall4(callee, nr, a0, a1, a2, a3) |
217 | } |
218 | |
219 | #[inline ] |
220 | pub(in crate::backend) unsafe fn syscall5<'a>( |
221 | nr: SyscallNumber<'a>, |
222 | a0: ArgReg<'a, A0>, |
223 | a1: ArgReg<'a, A1>, |
224 | a2: ArgReg<'a, A2>, |
225 | a3: ArgReg<'a, A3>, |
226 | a4: ArgReg<'a, A4>, |
227 | ) -> RetReg<R0> { |
228 | let callee = match transmute(super::SYSCALL.load(Relaxed)) { |
229 | Some(callee) => callee, |
230 | None => super::init_syscall(), |
231 | }; |
232 | asm::indirect_syscall5(callee, nr, a0, a1, a2, a3, a4) |
233 | } |
234 | |
235 | #[inline ] |
236 | pub(in crate::backend) unsafe fn syscall6<'a>( |
237 | nr: SyscallNumber<'a>, |
238 | a0: ArgReg<'a, A0>, |
239 | a1: ArgReg<'a, A1>, |
240 | a2: ArgReg<'a, A2>, |
241 | a3: ArgReg<'a, A3>, |
242 | a4: ArgReg<'a, A4>, |
243 | a5: ArgReg<'a, A5>, |
244 | ) -> RetReg<R0> { |
245 | let callee = match transmute(super::SYSCALL.load(Relaxed)) { |
246 | Some(callee) => callee, |
247 | None => super::init_syscall(), |
248 | }; |
249 | asm::indirect_syscall6(callee, nr, a0, a1, a2, a3, a4, a5) |
250 | } |
251 | |
252 | // With the indirect call, it isn't meaningful to do a separate |
253 | // `_readonly` optimization. |
254 | #[allow (unused_imports)] |
255 | pub(in crate::backend) use { |
256 | syscall0 as syscall0_readonly, syscall1 as syscall1_readonly, |
257 | syscall2 as syscall2_readonly, syscall3 as syscall3_readonly, |
258 | syscall4 as syscall4_readonly, syscall5 as syscall5_readonly, |
259 | syscall6 as syscall6_readonly, |
260 | }; |
261 | } |
262 | |
263 | #[cfg (feature = "time" )] |
264 | type ClockGettimeType = unsafe extern "C" fn(c::c_int, *mut Timespec) -> c::c_int; |
265 | |
266 | #[cfg (feature = "process" )] |
267 | #[cfg (any( |
268 | target_arch = "x86_64" , |
269 | target_arch = "x86" , |
270 | target_arch = "riscv64" , |
271 | target_arch = "powerpc64" |
272 | ))] |
273 | type GetcpuType = unsafe extern "C" fn(*mut u32, *mut u32, *mut c_void) -> c::c_int; |
274 | |
275 | /// The underlying syscall functions are only called from asm, using the |
276 | /// special syscall calling convention to pass arguments and return values, |
277 | /// which the signature here doesn't reflect. |
278 | #[cfg (target_arch = "x86" )] |
279 | pub(super) type SyscallType = unsafe extern "C" fn(); |
280 | |
281 | /// Initialize `CLOCK_GETTIME` and return its value. |
282 | #[cfg (feature = "time" )] |
283 | #[cold ] |
284 | fn init_clock_gettime() -> ClockGettimeType { |
285 | init(); |
286 | // SAFETY: Load the function address from static storage that we just |
287 | // initialized. |
288 | unsafe { transmute(src:CLOCK_GETTIME.load(order:Relaxed)) } |
289 | } |
290 | |
291 | /// Initialize `GETCPU` and return its value. |
292 | #[cfg (feature = "process" )] |
293 | #[cfg (any( |
294 | target_arch = "x86_64" , |
295 | target_arch = "x86" , |
296 | target_arch = "riscv64" , |
297 | target_arch = "powerpc64" |
298 | ))] |
299 | #[cold ] |
300 | fn init_getcpu() -> GetcpuType { |
301 | init(); |
302 | // SAFETY: Load the function address from static storage that we just |
303 | // initialized. |
304 | unsafe { transmute(src:GETCPU.load(order:Relaxed)) } |
305 | } |
306 | |
307 | /// Initialize `SYSCALL` and return its value. |
308 | #[cfg (target_arch = "x86" )] |
309 | #[cold ] |
310 | fn init_syscall() -> SyscallType { |
311 | init(); |
312 | // SAFETY: Load the function address from static storage that we just |
313 | // initialized. |
314 | unsafe { transmute(SYSCALL.load(Relaxed)) } |
315 | } |
316 | |
317 | /// `AtomicPtr` can't hold a `fn` pointer, so we use a `*` pointer to this |
318 | /// placeholder type, and cast it as needed. |
319 | struct Function; |
320 | #[cfg (feature = "time" )] |
321 | static CLOCK_GETTIME: AtomicPtr<Function> = AtomicPtr::new(null_mut()); |
322 | #[cfg (feature = "process" )] |
323 | #[cfg (any( |
324 | target_arch = "x86_64" , |
325 | target_arch = "x86" , |
326 | target_arch = "riscv64" , |
327 | target_arch = "powerpc64" |
328 | ))] |
329 | static GETCPU: AtomicPtr<Function> = AtomicPtr::new(null_mut()); |
330 | #[cfg (target_arch = "x86" )] |
331 | static SYSCALL: AtomicPtr<Function> = AtomicPtr::new(null_mut()); |
332 | |
333 | #[cfg (feature = "time" )] |
334 | unsafe extern "C" fn rustix_clock_gettime_via_syscall( |
335 | clockid: c::c_int, |
336 | res: *mut Timespec, |
337 | ) -> c::c_int { |
338 | match _rustix_clock_gettime_via_syscall(clockid, res) { |
339 | Ok(()) => 0, |
340 | Err(err: Errno) => err.raw_os_error().wrapping_neg(), |
341 | } |
342 | } |
343 | |
344 | #[cfg (feature = "time" )] |
345 | #[cfg (target_pointer_width = "32" )] |
346 | unsafe fn _rustix_clock_gettime_via_syscall( |
347 | clockid: c::c_int, |
348 | res: *mut Timespec, |
349 | ) -> io::Result<()> { |
350 | let r0 = syscall!(__NR_clock_gettime64, c_int(clockid), res); |
351 | match ret(r0) { |
352 | Err(io::Errno::NOSYS) => _rustix_clock_gettime_via_syscall_old(clockid, res), |
353 | otherwise => otherwise, |
354 | } |
355 | } |
356 | |
357 | #[cfg (feature = "time" )] |
358 | #[cfg (target_pointer_width = "32" )] |
359 | unsafe fn _rustix_clock_gettime_via_syscall_old( |
360 | clockid: c::c_int, |
361 | res: *mut Timespec, |
362 | ) -> io::Result<()> { |
363 | // Ordinarily `rustix` doesn't like to emulate system calls, but in the |
364 | // case of time APIs, it's specific to Linux, specific to 32-bit |
365 | // architectures *and* specific to old kernel versions, and it's not that |
366 | // hard to fix up here, so that no other code needs to worry about this. |
367 | let mut old_result = MaybeUninit::<__kernel_old_timespec>::uninit(); |
368 | let r0 = syscall!(__NR_clock_gettime, c_int(clockid), &mut old_result); |
369 | match ret(r0) { |
370 | Ok(()) => { |
371 | let old_result = old_result.assume_init(); |
372 | *res = Timespec { |
373 | tv_sec: old_result.tv_sec.into(), |
374 | tv_nsec: old_result.tv_nsec.into(), |
375 | }; |
376 | Ok(()) |
377 | } |
378 | otherwise => otherwise, |
379 | } |
380 | } |
381 | |
382 | #[cfg (feature = "time" )] |
383 | #[cfg (target_pointer_width = "64" )] |
384 | unsafe fn _rustix_clock_gettime_via_syscall( |
385 | clockid: c::c_int, |
386 | res: *mut Timespec, |
387 | ) -> io::Result<()> { |
388 | ret(raw:syscall!(__NR_clock_gettime, c_int(clockid), res)) |
389 | } |
390 | |
391 | #[cfg (feature = "process" )] |
392 | #[cfg (any( |
393 | target_arch = "x86_64" , |
394 | target_arch = "x86" , |
395 | target_arch = "riscv64" , |
396 | target_arch = "powerpc64" |
397 | ))] |
398 | unsafe extern "C" fn rustix_getcpu_via_syscall( |
399 | cpu: *mut u32, |
400 | node: *mut u32, |
401 | unused: *mut c_void, |
402 | ) -> c::c_int { |
403 | match ret(raw:syscall!(__NR_getcpu, cpu, node, unused)) { |
404 | Ok(()) => 0, |
405 | Err(err: Errno) => err.raw_os_error().wrapping_neg(), |
406 | } |
407 | } |
408 | |
409 | #[cfg (target_arch = "x86" )] |
410 | extern "C" { |
411 | /// A symbol pointing to an `int 0x80` instruction. This “function” is only |
412 | /// called from assembly, and only with the x86 syscall calling convention, |
413 | /// so its signature here is not its true signature. |
414 | /// |
415 | /// This extern block and the `global_asm!` below can be replaced with |
416 | /// `#[naked]` if it's stabilized. |
417 | fn rustix_int_0x80(); |
418 | } |
419 | |
420 | #[cfg (target_arch = "x86" )] |
421 | global_asm!( |
422 | r#" |
423 | .section .text.rustix_int_0x80,"ax",@progbits |
424 | .p2align 4 |
425 | .weak rustix_int_0x80 |
426 | .hidden rustix_int_0x80 |
427 | .type rustix_int_0x80, @function |
428 | rustix_int_0x80: |
429 | .cfi_startproc |
430 | int 0x80 |
431 | ret |
432 | .cfi_endproc |
433 | .size rustix_int_0x80, .-rustix_int_0x80 |
434 | "# |
435 | ); |
436 | |
437 | fn minimal_init() { |
438 | // Store default function addresses in static storage so that if we |
439 | // end up making any system calls while we read the vDSO, they'll work. If |
440 | // the memory happens to already be initialized, this is redundant, but not |
441 | // harmful. |
442 | #[cfg (feature = "time" )] |
443 | { |
444 | CLOCK_GETTIME |
445 | .compare_exchange( |
446 | null_mut(), |
447 | rustix_clock_gettime_via_syscall as *mut Function, |
448 | Relaxed, |
449 | Relaxed, |
450 | ) |
451 | .ok(); |
452 | } |
453 | |
454 | #[cfg (feature = "process" )] |
455 | #[cfg (any( |
456 | target_arch = "x86_64" , |
457 | target_arch = "x86" , |
458 | target_arch = "riscv64" , |
459 | target_arch = "powerpc64" |
460 | ))] |
461 | { |
462 | GETCPU |
463 | .compare_exchange( |
464 | null_mut(), |
465 | rustix_getcpu_via_syscall as *mut Function, |
466 | Relaxed, |
467 | Relaxed, |
468 | ) |
469 | .ok(); |
470 | } |
471 | |
472 | #[cfg (target_arch = "x86" )] |
473 | { |
474 | SYSCALL |
475 | .compare_exchange( |
476 | null_mut(), |
477 | rustix_int_0x80 as *mut Function, |
478 | Relaxed, |
479 | Relaxed, |
480 | ) |
481 | .ok(); |
482 | } |
483 | } |
484 | |
485 | fn init() { |
486 | minimal_init(); |
487 | |
488 | if let Some(vdso) = vdso::Vdso::new() { |
489 | #[cfg (feature = "time" )] |
490 | { |
491 | // Look up the platform-specific `clock_gettime` symbol as |
492 | // documented [here], except on 32-bit platforms where we look up |
493 | // the `64`-suffixed variant and fail if we don't find it. |
494 | // |
495 | // [here]: https://man7.org/linux/man-pages/man7/vdso.7.html |
496 | #[cfg (target_arch = "x86_64" )] |
497 | let ptr = vdso.sym(cstr!("LINUX_2.6" ), cstr!("__vdso_clock_gettime" )); |
498 | #[cfg (target_arch = "arm" )] |
499 | let ptr = vdso.sym(cstr!("LINUX_2.6" ), cstr!("__vdso_clock_gettime64" )); |
500 | #[cfg (target_arch = "aarch64" )] |
501 | let ptr = vdso.sym(cstr!("LINUX_2.6.39" ), cstr!("__kernel_clock_gettime" )); |
502 | #[cfg (target_arch = "x86" )] |
503 | let ptr = vdso.sym(cstr!("LINUX_2.6" ), cstr!("__vdso_clock_gettime64" )); |
504 | #[cfg (target_arch = "riscv64" )] |
505 | let ptr = vdso.sym(cstr!("LINUX_4.15" ), cstr!("__vdso_clock_gettime" )); |
506 | #[cfg (target_arch = "powerpc64" )] |
507 | let ptr = vdso.sym(cstr!("LINUX_2.6.15" ), cstr!("__kernel_clock_gettime" )); |
508 | #[cfg (target_arch = "s390x" )] |
509 | let ptr = vdso.sym(cstr!("LINUX_2.6.29" ), cstr!("__kernel_clock_gettime" )); |
510 | #[cfg (any(target_arch = "mips" , target_arch = "mips32r6" ))] |
511 | let ptr = vdso.sym(cstr!("LINUX_2.6" ), cstr!("__vdso_clock_gettime64" )); |
512 | #[cfg (any(target_arch = "mips64" , target_arch = "mips64r6" ))] |
513 | let ptr = vdso.sym(cstr!("LINUX_2.6" ), cstr!("__vdso_clock_gettime" )); |
514 | |
515 | // On all 64-bit platforms, the 64-bit `clock_gettime` symbols are |
516 | // always available. |
517 | #[cfg (target_pointer_width = "64" )] |
518 | let ok = true; |
519 | |
520 | // On some 32-bit platforms, the 64-bit `clock_gettime` symbols are |
521 | // not available on older kernel versions. |
522 | #[cfg (any( |
523 | target_arch = "arm" , |
524 | target_arch = "mips" , |
525 | target_arch = "mips32r6" , |
526 | target_arch = "x86" |
527 | ))] |
528 | let ok = !ptr.is_null(); |
529 | |
530 | if ok { |
531 | assert!(!ptr.is_null()); |
532 | |
533 | // Store the computed function addresses in static storage so |
534 | // that we don't need to compute them again (but if we do, it |
535 | // doesn't hurt anything). |
536 | CLOCK_GETTIME.store(ptr.cast(), Relaxed); |
537 | } |
538 | } |
539 | |
540 | #[cfg (feature = "process" )] |
541 | #[cfg (any( |
542 | target_arch = "x86_64" , |
543 | target_arch = "x86" , |
544 | target_arch = "riscv64" , |
545 | target_arch = "powerpc64" |
546 | ))] |
547 | { |
548 | // Look up the platform-specific `getcpu` symbol as documented |
549 | // [here]. |
550 | // |
551 | // [here]: https://man7.org/linux/man-pages/man7/vdso.7.html |
552 | #[cfg (target_arch = "x86_64" )] |
553 | let ptr = vdso.sym(cstr!("LINUX_2.6" ), cstr!("__vdso_getcpu" )); |
554 | #[cfg (target_arch = "x86" )] |
555 | let ptr = vdso.sym(cstr!("LINUX_2.6" ), cstr!("__vdso_getcpu" )); |
556 | #[cfg (target_arch = "riscv64" )] |
557 | let ptr = vdso.sym(cstr!("LINUX_4.15" ), cstr!("__vdso_getcpu" )); |
558 | #[cfg (target_arch = "powerpc64" )] |
559 | let ptr = vdso.sym(cstr!("LINUX_2.6.15" ), cstr!("__kernel_getcpu" )); |
560 | |
561 | #[cfg (any( |
562 | target_arch = "x86_64" , |
563 | target_arch = "riscv64" , |
564 | target_arch = "powerpc64" |
565 | ))] |
566 | let ok = true; |
567 | |
568 | // On 32-bit x86, the symbol doesn't appear present sometimes. |
569 | #[cfg (target_arch = "x86" )] |
570 | let ok = !ptr.is_null(); |
571 | |
572 | #[cfg (any( |
573 | target_arch = "aarch64" , |
574 | target_arch = "arm" , |
575 | target_arch = "mips" , |
576 | target_arch = "mips32r6" , |
577 | target_arch = "mips64" , |
578 | target_arch = "mips64r6" , |
579 | target_arch = "s390x" , |
580 | ))] |
581 | let ok = false; |
582 | |
583 | if ok { |
584 | assert!(!ptr.is_null()); |
585 | |
586 | // Store the computed function addresses in static storage so |
587 | // that we don't need to compute them again (but if we do, it |
588 | // doesn't hurt anything). |
589 | GETCPU.store(ptr.cast(), Relaxed); |
590 | } |
591 | } |
592 | |
593 | // On x86, also look up the vsyscall entry point. |
594 | #[cfg (target_arch = "x86" )] |
595 | { |
596 | let ptr = vdso.sym(cstr!("LINUX_2.5" ), cstr!("__kernel_vsyscall" )); |
597 | assert!(!ptr.is_null()); |
598 | |
599 | // As above, store the computed function addresses in |
600 | // static storage. |
601 | SYSCALL.store(ptr.cast(), Relaxed); |
602 | } |
603 | } |
604 | } |
605 | |