1#![no_std]
6
7#[cfg_attr(
8 all(
9 any(target_os = "linux", target_os = "android"),
10 target_arch = "aarch64",
11 target_pointer_width = "64"
12 ),
13 path = "arch/aarch64.rs"
14)]
15#[cfg_attr(
16 all(
17 any(target_os = "linux", target_os = "android"),
18 target_arch = "arm",
19 target_pointer_width = "32"
20 ),
21 path = "arch/arm.rs"
22)]
23#[cfg_attr(
24 all(
25 target_os = "linux",
26 target_arch = "loongarch64",
27 target_endian = "little",
28 target_pointer_width = "64"
29 ),
30 path = "arch/loongarch64.rs"
31)]
32#[cfg_attr(
33 all(
34 target_os = "linux",
35 any(all(target_arch = "mips", target_pointer_width = "32"),)
36 ),
37 path = "arch/mips.rs"
38)]
39#[cfg_attr(
40 all(target_os = "linux", target_arch = "mips64"),
41 path = "arch/mips64.rs"
42)]
43#[cfg_attr(
44 all(
45 target_os = "linux",
46 target_arch = "powerpc",
47 target_endian = "big",
48 target_pointer_width = "32"
49 ),
50 path = "arch/powerpc.rs"
51)]
52#[cfg_attr(
53 all(
54 target_os = "linux",
55 target_arch = "powerpc64",
56 target_pointer_width = "64"
57 ),
58 path = "arch/powerpc64.rs"
59)]
60#[cfg_attr(
61 all(
62 target_os = "linux",
63 all(
64 any(
65 all(target_arch = "riscv32", target_pointer_width = "32"),
66 all(target_arch = "riscv64", target_pointer_width = "64")
67 ),
68 target_endian = "little"
69 )
70 ),
71 path = "arch/riscv.rs"
72)]
73#[cfg_attr(
74 all(
75 target_os = "linux",
76 target_arch = "s390x",
77 target_endian = "big",
78 target_pointer_width = "64"
79 ),
80 path = "arch/s390x.rs"
81)]
82#[cfg_attr(
83 all(
84 target_os = "linux",
85 target_arch = "x86_64",
86 target_endian = "little",
87 target_pointer_width = "32"
88 ),
89 path = "arch/x32.rs"
90)]
91#[cfg_attr(
92 all(
93 any(target_os = "linux", target_os = "android"),
94 target_arch = "x86",
95 target_endian = "little",
96 target_pointer_width = "32"
97 ),
98 path = "arch/x86.rs"
99)]
100#[cfg_attr(
101 all(
102 any(target_os = "linux", target_os = "android"),
103 target_arch = "x86_64",
104 target_endian = "little",
105 target_pointer_width = "64"
106 ),
107 path = "arch/x86_64.rs"
108)]
109mod arch;
110
111mod elf;
112pub(crate) mod util;
113
114pub use arch::Vdso;
115
116use core::{marker::PhantomData, ptr};
117
118pub(crate) struct VdsoReader<'a> {
119 header: &'a VdsoHeader,
120 versyms: *const u16,
121 verdefs: *const elf::Verdef,
122 strings: *const u8,
123 syms: &'a [elf::Sym],
124}
125
126impl<'a> VdsoReader<'a> {
127 pub unsafe fn from_ptr(ptr: *const core::ffi::c_void) -> Option<Self> {
128 Self::from_header(VdsoHeader::from_ptr(ptr)?)
129 }
130
131 unsafe fn from_header(header: &'a VdsoHeader) -> Option<VdsoReader> {
132 let mut versyms: *const u16 = ptr::null();
133 let mut verdefs: *const elf::Verdef = ptr::null();
134 let mut strings: *const u8 = ptr::null();
135 let mut syms: Option<&[elf::Sym]> = None;
136 let mut filled = 0u8;
137
138 for sh in header.shs() {
139 match sh.sh_type {
140 elf::SHT_GNU_VERSYM => {
141 versyms = header.offset(sh.sh_offset);
142 filled |= 1 << 0;
143 if filled == 15 {
144 break;
145 }
146 }
147 elf::SHT_GNU_VERDEF => {
148 verdefs = header.offset(sh.sh_offset);
149 filled |= 1 << 1;
150 if filled == 15 {
151 break;
152 }
153 }
154 elf::SHT_STRTAB => {
155 strings = header.offset(sh.sh_offset);
156 filled |= 1 << 2;
157 if filled == 15 {
158 break;
159 }
160 }
161 elf::SHT_DYNSYM => {
162 syms = Some(header.slice(sh.sh_offset, sh.sh_size));
163 filled |= 1 << 3;
164 if filled == 15 {
165 break;
166 }
167 }
168 _ => (),
169 }
170 }
171
172 if versyms.is_null() {
173 verdefs = ptr::null();
174 } else if verdefs.is_null() {
175 versyms = ptr::null();
176 }
177 let syms = syms?;
178 if strings.is_null() {
179 return None;
180 }
181
182 Some(Self {
183 header,
184 versyms,
185 verdefs,
186 strings,
187 syms,
188 })
189 }
190
191 pub fn versions(&self) -> VersionIter {
192 VersionIter {
193 verdefs: self.verdefs,
194 reader: self,
195 }
196 }
197
198 pub fn symbols(&self) -> SymbolIter {
199 SymbolIter {
200 versyms: self.versyms,
201 iter: self.syms.iter(),
202 reader: self,
203 }
204 }
205}
206
207pub(crate) struct Version<'a> {
208 hash: u32,
209 id: u16,
210 name: *const u8,
211 _life: PhantomData<&'a ()>,
212}
213
214impl<'a> Version<'a> {
215 #[inline]
216 pub const fn hash(&self) -> u32 {
217 self.hash
218 }
219
220 #[inline]
221 pub const fn id(&self) -> u16 {
222 self.id
223 }
224
225 #[inline]
226 pub fn name(&self) -> *const u8 {
227 self.name
228 }
229}
230
231pub(crate) struct VersionIter<'a> {
232 verdefs: *const elf::Verdef,
233 reader: &'a VdsoReader<'a>,
234}
235
236impl<'a> Iterator for VersionIter<'a> {
237 type Item = Version<'a>;
238
239 fn next(&mut self) -> Option<Self::Item> {
240 loop {
241 unsafe {
242 if self.verdefs.is_null() {
243 return None;
244 }
245
246 let verdef = &*self.verdefs;
247 if verdef.vd_next == 0 {
248 self.verdefs = ptr::null();
249 } else {
250 self.verdefs = self
251 .verdefs
252 .cast::<u8>()
253 .add(verdef.vd_next as usize)
254 .cast();
255 }
256
257 if verdef.vd_version == 1 && verdef.vd_flags & 1 == 0 {
258 let aux = &*(verdef as *const elf::Verdef)
259 .cast::<u8>()
260 .add(verdef.vd_aux as usize)
261 .cast::<elf::Verdaux>();
262 let name = self.reader.strings.add(aux.vda_name as usize);
263
264 return Some(Version {
265 hash: verdef.vd_hash,
266 id: verdef.vd_ndx,
267 name,
268 _life: PhantomData,
269 });
270 }
271 }
272 }
273 }
274}
275
276pub(crate) struct Symbol<'a> {
277 name: *const u8,
278 ptr: *const core::ffi::c_void,
279 vid: Option<u16>,
280 _life: PhantomData<&'a ()>,
281}
282
283impl<'a> Symbol<'a> {
284 #[inline]
285 pub fn name(&self) -> *const u8 {
286 self.name
287 }
288
289 #[inline]
290 pub fn ptr(&self) -> *const core::ffi::c_void {
291 self.ptr
292 }
293
294 #[inline]
295 pub fn version_id(&self) -> Option<u16> {
296 self.vid
297 }
298}
299
300pub(crate) struct SymbolIter<'a> {
301 versyms: *const u16,
302 iter: core::slice::Iter<'a, elf::Sym>,
303 reader: &'a VdsoReader<'a>,
304}
305
306impl<'a> Iterator for SymbolIter<'a> {
307 type Item = Symbol<'a>;
308
309 fn next(&mut self) -> Option<Self::Item> {
310 loop {
311 unsafe {
312 let sym = self.iter.next()?;
313 let vid = if !self.versyms.is_null() {
314 let res = *self.versyms;
315 self.versyms = self.versyms.add(1);
316 Some(res)
317 } else {
318 None
319 };
320
321 let typ = elf::st_type(sym.st_info);
322 let bind = elf::st_bind(sym.st_info);
323
324 if (bind == elf::STB_GLOBAL || bind == elf::STB_WEAK)
325 && (typ == elf::STT_FUNC || typ == elf::STT_NOTYPE)
326 && elf::st_visibility(sym.st_other) == elf::STV_DEFAULT
327 {
328 let name = self.reader.strings.add(sym.st_name as usize);
329
330 return Some(Symbol {
331 name,
332 ptr: self.reader.header.offset(sym.st_value),
333 vid,
334 _life: PhantomData,
335 });
336 }
337 }
338 }
339 }
340}
341
342#[repr(transparent)]
343struct VdsoHeader(elf::Header);
344
345impl VdsoHeader {
346 pub(crate) unsafe fn from_ptr<'a>(ptr: *const core::ffi::c_void) -> Option<&'a Self> {
347 let head = &*(ptr as *const Self);
348
349 if head.0.e_ident[..elf::ELFMAG.len()] != elf::ELFMAG[..] {
351 return None;
352 }
353
354 if head.0.e_ident[elf::EI_CLASS] != elf::ELFCLASS {
356 return None;
357 }
358
359 match head.0.e_ident[elf::EI_OSABI] {
361 elf::ELFOSABI_SYSV | elf::ELFOSABI_LINUX => (),
362 _ => return None,
363 }
364
365 if head.0.e_ident[elf::EI_ABIVERSION] != 0 {
367 return None;
368 }
369
370 if head.0.e_type != elf::ET_DYN {
372 return None;
373 }
374
375 if head.0.e_ident[elf::EI_VERSION] != elf::EV_CURRENT {
377 return None;
378 }
379
380 if head.0.e_ehsize as usize != core::mem::size_of::<elf::Header>()
382 || head.0.e_phentsize as usize != core::mem::size_of::<elf::ProgramHeader>()
383 {
384 return None;
385 }
386
387 if head.0.e_phnum == 0xffff {
388 return None;
389 }
390
391 if (head.0.e_phoff as usize) < core::mem::size_of::<elf::Header>() {
392 return None;
393 }
394
395 if head.0.e_ident[elf::EI_DATA] != elf::ELFDATA {
397 return None;
398 }
399
400 if head.0.e_machine != elf::EM_CURRENT {
402 return None;
403 }
404
405 Some(head)
406 }
407
408 pub(crate) unsafe fn offset<T, O>(&self, off: O) -> *const T
409 where
410 O: Into<elf::Word>,
411 {
412 (self as *const Self)
413 .cast::<u8>()
414 .add(off.into() as usize)
415 .cast::<T>()
416 }
417
418 pub(crate) unsafe fn slice<'a, T, T1, T2>(&'a self, off: T1, len: T2) -> &[T]
419 where
420 T1: Into<elf::Word> + 'a,
421 T2: Into<elf::Word> + 'a,
422 {
423 core::slice::from_raw_parts::<u8>(self.offset(off), len.into() as usize)
424 .align_to()
425 .1
426 }
427
428 pub(crate) unsafe fn shs(&self) -> &[elf::SectionHeader] {
429 self.slice(self.0.e_shoff, self.0.e_shentsize * self.0.e_shnum)
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 extern crate std;
436
437 #[cfg(all(
438 any(target_os = "linux", target_os = "android"),
439 target_arch = "x86_64",
440 target_endian = "little",
441 target_pointer_width = "64"
442 ))]
443 const FAKE: &str = "fake/x86_64.so";
444 #[cfg(all(
445 any(target_os = "linux", target_os = "android"),
446 target_arch = "x86_64",
447 target_endian = "little",
448 target_pointer_width = "32"
449 ))]
450 const FAKE: &str = "fake/x32.so";
451 #[cfg(all(
452 any(target_os = "linux", target_os = "android"),
453 target_arch = "x86",
454 target_endian = "little",
455 target_pointer_width = "32"
456 ))]
457 const FAKE: &str = "fake/x86.so";
458 #[cfg(all(
459 any(target_os = "linux", target_os = "android"),
460 target_arch = "aarch64",
461 target_pointer_width = "64"
462 ))]
463 const FAKE: &str = "fake/aarch64.so";
464 #[cfg(all(
465 any(target_os = "linux", target_os = "android"),
466 target_arch = "arm",
467 target_pointer_width = "32"
468 ))]
469 const FAKE: &str = "fake/arm.so";
470 #[cfg(all(
471 any(target_os = "linux", target_os = "android"),
472 target_arch = "loongarch64",
473 target_endian = "little",
474 target_pointer_width = "64"
475 ))]
476 const FAKE: &str = "fake/loongarch64.so";
477 #[cfg(all(
478 any(target_os = "linux", target_os = "android"),
479 target_arch = "mips",
480 target_endian = "big",
481 target_pointer_width = "32"
482 ))]
483 const FAKE: &str = "fake/mips.so";
484 #[cfg(all(
485 any(target_os = "linux", target_os = "android"),
486 target_arch = "mips",
487 target_endian = "little",
488 target_pointer_width = "32"
489 ))]
490 const FAKE: &str = "fake/mipsel.so";
491 #[cfg(all(
492 any(target_os = "linux", target_os = "android"),
493 target_arch = "mips64",
494 target_endian = "big",
495 target_pointer_width = "64"
496 ))]
497 const FAKE: &str = "fake/mips64.so";
498 #[cfg(all(
499 any(target_os = "linux", target_os = "android"),
500 target_arch = "mips64",
501 target_endian = "little",
502 target_pointer_width = "64"
503 ))]
504 const FAKE: &str = "fake/mips64el.so";
505 #[cfg(all(
506 any(target_os = "linux", target_os = "android"),
507 target_arch = "powerpc",
508 target_endian = "big",
509 target_pointer_width = "32"
510 ))]
511 const FAKE: &str = "fake/powerpc.so";
512 #[cfg(all(
513 any(target_os = "linux", target_os = "android"),
514 target_arch = "powerpc64",
515 target_endian = "big",
516 target_pointer_width = "64"
517 ))]
518 const FAKE: &str = "fake/powerpc64.so";
519 #[cfg(all(
520 any(target_os = "linux", target_os = "android"),
521 target_arch = "powerpc64",
522 target_endian = "little",
523 target_pointer_width = "64"
524 ))]
525 const FAKE: &str = "fake/powerpc64le.so";
526 #[cfg(all(
527 any(target_os = "linux", target_os = "android"),
528 target_arch = "riscv32",
529 target_endian = "little",
530 target_pointer_width = "32"
531 ))]
532 const FAKE: &str = "fake/riscv32.so";
533 #[cfg(all(
534 any(target_os = "linux", target_os = "android"),
535 target_arch = "riscv64",
536 target_endian = "little",
537 target_pointer_width = "64"
538 ))]
539 const FAKE: &str = "fake/riscv64.so";
540 #[cfg(all(
541 any(target_os = "linux", target_os = "android"),
542 target_arch = "s390x",
543 target_endian = "big",
544 target_pointer_width = "64"
545 ))]
546 const FAKE: &str = "fake/s390x.so";
547
548 #[cfg(all(
549 any(target_os = "linux", target_os = "android"),
550 target_arch = "x86_64",
551 target_endian = "little",
552 any(target_pointer_width = "64", target_pointer_width = "32")
553 ))]
554 #[test]
555 fn parse() {
556 let data = std::fs::read(FAKE).unwrap();
557 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
558 assert!(vdso.is_some());
559 let vdso = vdso.unwrap();
560 assert!(!vdso.clock_gettime.is_null());
561 assert!(!vdso.getcpu.is_null());
562 assert!(!vdso.gettimeofday.is_null());
563 assert!(!vdso.time.is_null());
564 }
565
566 #[cfg(all(
567 any(target_os = "linux", target_os = "android"),
568 target_arch = "x86",
569 target_endian = "little",
570 target_pointer_width = "32"
571 ))]
572 #[test]
573 fn parse() {
574 let data = std::fs::read(FAKE).unwrap();
575 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
576 assert!(vdso.is_some());
577 let vdso = vdso.unwrap();
578 assert!(!vdso.sigreturn.is_null());
579 assert!(!vdso.rt_sigreturn.is_null());
580 assert!(!vdso.vsyscall.is_null());
581 assert!(!vdso.clock_gettime.is_null());
582 assert!(!vdso.gettimeofday.is_null());
583 assert!(!vdso.time.is_null());
584 }
585
586 #[cfg(all(
587 any(target_os = "linux", target_os = "android"),
588 target_arch = "aarch64",
589 target_pointer_width = "64"
590 ))]
591 #[test]
592 fn parse() {
593 let data = std::fs::read(FAKE).unwrap();
594 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
595 assert!(vdso.is_some());
596 let vdso = vdso.unwrap();
597 assert!(!vdso.rt_sigreturn.is_null());
598 assert!(!vdso.gettimeofday.is_null());
599 assert!(!vdso.clock_gettime.is_null());
600 assert!(!vdso.clock_getres.is_null());
601 }
602
603 #[cfg(all(
604 any(target_os = "linux", target_os = "android"),
605 target_arch = "arm",
606 target_pointer_width = "32"
607 ))]
608 #[test]
609 fn parse() {
610 let data = std::fs::read(FAKE).unwrap();
611 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
612 assert!(vdso.is_some());
613 let vdso = vdso.unwrap();
614 assert!(!vdso.gettimeofday.is_null());
615 assert!(!vdso.clock_gettime.is_null());
616 }
617
618 #[cfg(all(
619 any(target_os = "linux", target_os = "android"),
620 target_arch = "loongarch64",
621 target_endian = "little",
622 target_pointer_width = "64"
623 ))]
624 #[test]
625 fn parse() {
626 let data = std::fs::read(FAKE).unwrap();
627 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
628 assert!(vdso.is_some());
629 let vdso = vdso.unwrap();
630 assert!(!vdso.getcpu.is_null());
631 assert!(!vdso.clock_getres.is_null());
632 assert!(!vdso.clock_gettime.is_null());
633 assert!(!vdso.gettimeofday.is_null());
634 assert!(!vdso.rt_sigreturn.is_null());
635 }
636
637 #[cfg(all(
638 any(target_os = "linux", target_os = "android"),
639 any(
640 all(target_arch = "mips", target_pointer_width = "32"),
641 target_arch = "mips64"
642 )
643 ))]
644 #[test]
645 fn parse() {
646 let data = std::fs::read(FAKE).unwrap();
647 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
648 assert!(vdso.is_some());
649 let vdso = vdso.unwrap();
650 assert!(!vdso.gettimeofday.is_null());
651 assert!(!vdso.clock_gettime.is_null());
652 }
653
654 #[cfg(all(
655 any(target_os = "linux", target_os = "android"),
656 target_arch = "powerpc",
657 target_endian = "big",
658 target_pointer_width = "32"
659 ))]
660 #[test]
661 fn parse() {
662 let data = std::fs::read(FAKE).unwrap();
663 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
664 assert!(vdso.is_some());
665 let vdso = vdso.unwrap();
666 assert!(!vdso.clock_getres.is_null());
667 assert!(!vdso.clock_gettime.is_null());
668 assert!(!vdso.clock_gettime64.is_null());
669 assert!(!vdso.datapage_offset.is_null());
670 assert!(!vdso.get_syscall_map.is_null());
671 assert!(!vdso.get_tbfreq.is_null());
672 assert!(!vdso.gettimeofday.is_null());
673 assert!(!vdso.sigtramp_rt32.is_null());
674 assert!(!vdso.sigtramp32.is_null());
675 assert!(!vdso.sync_dicache.is_null());
676 assert!(!vdso.sync_dicache_p5.is_null());
677 }
678
679 #[cfg(all(
680 any(target_os = "linux", target_os = "android"),
681 target_arch = "powerpc64",
682 target_pointer_width = "64"
683 ))]
684 #[test]
685 fn parse() {
686 let data = std::fs::read(FAKE).unwrap();
687 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
688 assert!(vdso.is_some());
689 let vdso = vdso.unwrap();
690 assert!(!vdso.clock_getres.is_null());
691 assert!(!vdso.clock_gettime.is_null());
692 assert!(!vdso.datapage_offset.is_null());
693 assert!(!vdso.get_syscall_map.is_null());
694 assert!(!vdso.get_tbfreq.is_null());
695 assert!(!vdso.getcpu.is_null());
696 assert!(!vdso.gettimeofday.is_null());
697 assert!(!vdso.sigtramp_rt64.is_null());
698 assert!(!vdso.sigtramp32.is_null());
699 assert!(!vdso.sync_dicache.is_null());
700 assert!(!vdso.sync_dicache_p5.is_null());
701 }
702
703 #[cfg(all(
704 any(target_os = "linux", target_os = "android"),
705 target_arch = "riscv64",
706 target_endian = "little",
707 target_pointer_width = "64"
708 ))]
709 #[test]
710 fn parse() {
711 let data = std::fs::read(FAKE).unwrap();
712 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
713 assert!(vdso.is_some());
714 let vdso = vdso.unwrap();
715 assert!(!vdso.rt_sigreturn.is_null());
716 assert!(!vdso.gettimeofday.is_null());
717 assert!(!vdso.clock_gettime.is_null());
718 assert!(!vdso.clock_getres.is_null());
719 assert!(!vdso.getcpu.is_null());
720 assert!(!vdso.flush_icache.is_null());
721 }
722
723 #[cfg(all(
724 any(target_os = "linux", target_os = "android"),
725 target_arch = "s390x",
726 target_endian = "big",
727 target_pointer_width = "64"
728 ))]
729 #[test]
730 fn parse() {
731 let data = std::fs::read(FAKE).unwrap();
732 let vdso = unsafe { super::Vdso::from_ptr(data.as_ptr().cast()) };
733 assert!(vdso.is_some());
734 let vdso = vdso.unwrap();
735 assert!(!vdso.clock_getres.is_null());
736 assert!(!vdso.clock_gettime.is_null());
737 assert!(!vdso.gettimeofday.is_null());
738 }
739}