linux_raw_vdso/
lib.rs

1//! A tiny library to parse the in-memory vDSO.
2//! For more documentation of what's vDSO and why we need it on Linux see
3//! [here](https://man7.org/linux/man-pages/man7/vdso.7.html).
4
5#![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        // Test magic number
350        if head.0.e_ident[..elf::ELFMAG.len()] != elf::ELFMAG[..] {
351            return None;
352        }
353
354        // Test class
355        if head.0.e_ident[elf::EI_CLASS] != elf::ELFCLASS {
356            return None;
357        }
358
359        // Test OS ABI
360        match head.0.e_ident[elf::EI_OSABI] {
361            elf::ELFOSABI_SYSV | elf::ELFOSABI_LINUX => (),
362            _ => return None,
363        }
364
365        // Test ABI version
366        if head.0.e_ident[elf::EI_ABIVERSION] != 0 {
367            return None;
368        }
369
370        // Test elf type, it must be dynamic
371        if head.0.e_type != elf::ET_DYN {
372            return None;
373        }
374
375        // Test elf version
376        if head.0.e_ident[elf::EI_VERSION] != elf::EV_CURRENT {
377            return None;
378        }
379
380        // Test some sizes
381        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        // Test endianness
396        if head.0.e_ident[elf::EI_DATA] != elf::ELFDATA {
397            return None;
398        }
399
400        // Test arch
401        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}