rustix/backend/linux_raw/
vdso.rs
1#![allow(unsafe_code)]
18
19use super::c;
20use crate::ffi::CStr;
21use crate::utils::check_raw_pointer;
22use core::ffi::c_void;
23use core::mem::size_of;
24use core::ptr::{null, null_mut};
25use linux_raw_sys::elf::*;
26
27#[cfg(target_arch = "s390x")]
28type ElfHashEntry = u64;
29#[cfg(not(target_arch = "s390x"))]
30type ElfHashEntry = u32;
31
32pub(super) struct Vdso {
33 load_addr: *const Elf_Ehdr,
35 load_end: *const c_void, pv_offset: usize, symtab: *const Elf_Sym,
40 symstrings: *const u8,
41 gnu_hash: *const u32,
42 bucket: *const ElfHashEntry,
43 chain: *const ElfHashEntry,
44 nbucket: ElfHashEntry,
45 versym: *const u16,
49 verdef: *const Elf_Verdef,
50}
51
52fn elf_hash(name: &CStr) -> u32 {
56 let mut h: u32 = 0;
57 for b in name.to_bytes() {
58 h = (h << 4).wrapping_add(u32::from(*b));
59 let g = h & 0xf000_0000;
60 if g != 0 {
61 h ^= g >> 24;
62 }
63 h &= !g;
64 }
65 h
66}
67
68fn gnu_hash(name: &CStr) -> u32 {
69 let mut h: u32 = 5381;
70 for s in name.to_bytes() {
71 h = h
72 .wrapping_add(h.wrapping_mul(32))
73 .wrapping_add(u32::from(*s));
74 }
75 h
76}
77
78fn init_from_sysinfo_ehdr() -> Option<Vdso> {
80 unsafe {
84 let hdr = super::param::auxv::sysinfo_ehdr();
85
86 if hdr.is_null() {
89 return None;
90 }
91
92 let mut vdso = Vdso {
93 load_addr: hdr,
94 load_end: hdr.cast(),
95 pv_offset: 0,
96 symtab: null(),
97 symstrings: null(),
98 gnu_hash: null(),
99 bucket: null(),
100 chain: null(),
101 nbucket: 0,
102 versym: null(),
104 verdef: null(),
105 };
106
107 let hdr = &*hdr;
108 let pt = check_raw_pointer::<Elf_Phdr>(vdso.base_plus(hdr.e_phoff)? as *mut _)?.as_ptr();
109 let mut dyn_: *const Elf_Dyn = null();
110 let mut num_dyn = 0;
111
112 let mut found_vaddr = false;
115 for i in 0..hdr.e_phnum {
116 let phdr = &*pt.add(i as usize);
117 if phdr.p_type == PT_LOAD && !found_vaddr {
118 if phdr.p_flags & (PF_R | PF_X) != (PF_R | PF_X) {
121 return None;
122 }
123 found_vaddr = true;
124 vdso.load_end = vdso.base_plus(phdr.p_offset.checked_add(phdr.p_memsz)?)?;
125 vdso.pv_offset = phdr.p_offset.wrapping_sub(phdr.p_vaddr);
126 } else if phdr.p_type == PT_DYNAMIC {
127 if phdr.p_offset < size_of::<Elf_Ehdr>() {
131 return None;
132 }
133
134 dyn_ = check_raw_pointer::<Elf_Dyn>(vdso.base_plus(phdr.p_offset)? as *mut _)?
135 .as_ptr();
136 num_dyn = phdr.p_memsz / size_of::<Elf_Dyn>();
137 } else if phdr.p_type == PT_INTERP || phdr.p_type == PT_GNU_RELRO {
138 return None;
142 }
143 }
144
145 if !found_vaddr || dyn_.is_null() {
146 return None; }
148
149 let mut hash: *const ElfHashEntry = null();
151 vdso.symstrings = null();
152 vdso.symtab = null();
153 vdso.versym = null();
154 vdso.verdef = null();
155 let mut i = 0;
156 loop {
157 if i == num_dyn {
158 return None;
159 }
160 let d = &*dyn_.add(i);
161 match d.d_tag {
162 DT_STRTAB => {
163 vdso.symstrings =
164 check_raw_pointer::<u8>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
165 .as_ptr();
166 }
167 DT_SYMTAB => {
168 vdso.symtab =
169 check_raw_pointer::<Elf_Sym>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
170 .as_ptr();
171 }
172 DT_HASH => {
173 hash = check_raw_pointer::<ElfHashEntry>(
174 vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _
175 )?
176 .as_ptr();
177 }
178 DT_GNU_HASH => {
179 vdso.gnu_hash =
180 check_raw_pointer::<u32>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
181 .as_ptr()
182 }
183 DT_VERSYM => {
184 vdso.versym =
185 check_raw_pointer::<u16>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
186 .as_ptr();
187 }
188 DT_VERDEF => {
189 vdso.verdef = check_raw_pointer::<Elf_Verdef>(
190 vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _,
191 )?
192 .as_ptr();
193 }
194 DT_SYMENT => {
195 if d.d_un.d_ptr != size_of::<Elf_Sym>() {
196 return None; }
198 }
199 DT_NULL => break,
200 _ => {}
201 }
202 i = i.checked_add(1)?;
203 }
204 if vdso.symstrings.is_null()
208 || vdso.symtab.is_null()
209 || (hash.is_null() && vdso.gnu_hash.is_null())
210 {
211 return None; }
213
214 if vdso.verdef.is_null() {
215 vdso.versym = null();
216 }
217
218 if !vdso.gnu_hash.is_null() {
220 vdso.nbucket = ElfHashEntry::from(*vdso.gnu_hash);
221 vdso.bucket = vdso
224 .gnu_hash
225 .add(4)
226 .add(size_of::<c::size_t>() / 4 * *vdso.gnu_hash.add(2) as usize)
227 .cast();
228 } else {
229 vdso.nbucket = *hash.add(0);
230 vdso.bucket = hash.add(2);
232 vdso.chain = hash.add(vdso.nbucket as usize + 2);
233 }
234
235 Some(vdso)
237 }
238}
239
240impl Vdso {
241 #[inline]
246 pub(super) fn new() -> Option<Self> {
247 init_from_sysinfo_ehdr()
248 }
249
250 unsafe fn match_version(&self, mut ver: u16, name: &CStr, hash: u32) -> bool {
256 ver &= 0x7fff; let mut def = self.verdef;
272 loop {
273 if (*def).vd_version != VER_DEF_CURRENT {
274 return false; }
276
277 if ((*def).vd_flags & VER_FLG_BASE) == 0 && ((*def).vd_ndx & 0x7fff) == ver {
278 break;
279 }
280
281 if (*def).vd_next == 0 {
282 return false; }
284
285 def = def
286 .cast::<u8>()
287 .add((*def).vd_next as usize)
288 .cast::<Elf_Verdef>();
289 }
290
291 let aux = &*(def.cast::<u8>())
293 .add((*def).vd_aux as usize)
294 .cast::<Elf_Verdaux>();
295 (*def).vd_hash == hash
296 && (name == CStr::from_ptr(self.symstrings.add(aux.vda_name as usize).cast()))
297 }
298
299 unsafe fn check_sym(
305 &self,
306 sym: &Elf_Sym,
307 i: ElfHashEntry,
308 name: &CStr,
309 version: &CStr,
310 ver_hash: u32,
311 ) -> bool {
312 if ELF_ST_TYPE(sym.st_info) != STT_FUNC && ELF_ST_TYPE(sym.st_info) != STT_NOTYPE {
320 return false;
321 }
322 if ELF_ST_BIND(sym.st_info) != STB_GLOBAL && ELF_ST_BIND(sym.st_info) != STB_WEAK {
323 return false;
324 }
325 if name != CStr::from_ptr(self.symstrings.add(sym.st_name as usize).cast()) {
326 return false;
327 }
328
329 if !self.versym.is_null()
331 && !self.match_version(*self.versym.add(i as usize), version, ver_hash)
332 {
333 return false;
334 }
335
336 true
337 }
338
339 pub(super) fn sym(&self, version: &CStr, name: &CStr) -> *mut c::c_void {
341 let ver_hash = elf_hash(version);
342
343 unsafe {
345 if !self.gnu_hash.is_null() {
346 let mut h1: u32 = gnu_hash(name);
347
348 let mut i = *self
353 .bucket
354 .cast::<u32>()
355 .add((ElfHashEntry::from(h1) % self.nbucket) as usize);
356 if i == 0 {
357 return null_mut();
358 }
359 h1 |= 1;
360 let mut hashval = self
363 .bucket
364 .cast::<u32>()
365 .add(self.nbucket as usize)
366 .add((i - *self.gnu_hash.add(1)) as usize);
367 loop {
368 let sym: &Elf_Sym = &*self.symtab.add(i as usize);
369 let h2 = *hashval;
370 hashval = hashval.add(1);
371 if h1 == (h2 | 1)
372 && self.check_sym(sym, ElfHashEntry::from(i), name, version, ver_hash)
373 {
374 let sum = self.addr_from_elf(sym.st_value).unwrap();
375 assert!(
376 sum as usize >= self.load_addr as usize
377 && sum as usize <= self.load_end as usize
378 );
379 return sum as *mut c::c_void;
380 }
381 if (h2 & 1) != 0 {
382 break;
383 }
384 i += 1;
385 }
386 } else {
387 let mut i = *self
388 .bucket
389 .add((ElfHashEntry::from(elf_hash(name)) % self.nbucket) as usize);
390 while i != 0 {
391 let sym: &Elf_Sym = &*self.symtab.add(i as usize);
392 if sym.st_shndx != SHN_UNDEF && self.check_sym(sym, i, name, version, ver_hash)
393 {
394 let sum = self.addr_from_elf(sym.st_value).unwrap();
395 assert!(
396 sum as usize >= self.load_addr as usize
397 && sum as usize <= self.load_end as usize
398 );
399 return sum as *mut c::c_void;
400 }
401 i = *self.chain.add(i as usize);
402 }
403 }
404 }
405
406 null_mut()
407 }
408
409 unsafe fn base_plus(&self, offset: usize) -> Option<*const c_void> {
411 let _ = (self.load_addr as usize).checked_add(offset)?;
413 Some(self.load_addr.cast::<u8>().add(offset).cast())
415 }
416
417 unsafe fn addr_from_elf(&self, elf_addr: usize) -> Option<*const c_void> {
419 self.base_plus(elf_addr.wrapping_add(self.pv_offset))
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 #[cfg(linux_raw)]
429 #[test]
430 #[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)]
431 #[allow(unused_variables)]
432 fn test_vdso() {
433 let vdso = Vdso::new().unwrap();
434 assert!(!vdso.symtab.is_null());
435 assert!(!vdso.symstrings.is_null());
436
437 {
438 #[cfg(target_arch = "x86_64")]
439 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime"));
440 #[cfg(target_arch = "arm")]
441 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64"));
442 #[cfg(target_arch = "aarch64")]
443 let ptr = vdso.sym(cstr!("LINUX_2.6.39"), cstr!("__kernel_clock_gettime"));
444 #[cfg(target_arch = "x86")]
445 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64"));
446 #[cfg(target_arch = "riscv64")]
447 let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_clock_gettime"));
448 #[cfg(target_arch = "powerpc")]
449 let _ptr = vdso.sym(cstr!("LINUX_5.11"), cstr!("__kernel_clock_gettime64"));
450 #[cfg(target_arch = "powerpc64")]
451 let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_clock_gettime"));
452 #[cfg(target_arch = "s390x")]
453 let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_clock_gettime"));
454 #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
455 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64"));
456 #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))]
457 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime"));
458
459 #[cfg(not(any(target_arch = "powerpc", target_arch = "x86")))]
464 assert!(!ptr.is_null());
465 }
466
467 {
468 #[cfg(target_arch = "x86_64")]
469 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres"));
470 #[cfg(target_arch = "arm")]
471 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres"));
472 #[cfg(target_arch = "aarch64")]
473 let ptr = vdso.sym(cstr!("LINUX_2.6.39"), cstr!("__kernel_clock_getres"));
474 #[cfg(target_arch = "x86")]
475 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres"));
476 #[cfg(target_arch = "riscv64")]
477 let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_clock_getres"));
478 #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
479 let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_clock_getres"));
480 #[cfg(target_arch = "s390x")]
481 let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_clock_getres"));
482 #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
483 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres"));
484 #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))]
485 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres"));
486
487 #[cfg(not(target_arch = "x86"))]
489 assert!(!ptr.is_null());
490 }
491
492 {
493 #[cfg(target_arch = "x86_64")]
494 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday"));
495 #[cfg(target_arch = "arm")]
496 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday"));
497 #[cfg(target_arch = "aarch64")]
498 let ptr = vdso.sym(cstr!("LINUX_2.6.39"), cstr!("__kernel_gettimeofday"));
499 #[cfg(target_arch = "x86")]
500 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday"));
501 #[cfg(target_arch = "riscv64")]
502 let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_gettimeofday"));
503 #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
504 let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_gettimeofday"));
505 #[cfg(target_arch = "s390x")]
506 let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_gettimeofday"));
507 #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
508 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday"));
509 #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))]
510 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday"));
511
512 #[cfg(not(target_arch = "x86"))]
514 assert!(!ptr.is_null());
515 }
516
517 #[cfg(any(
518 target_arch = "x86_64",
519 target_arch = "x86",
520 target_arch = "riscv64",
521 target_arch = "powerpc",
522 target_arch = "powerpc64",
523 target_arch = "s390x",
524 ))]
525 {
526 #[cfg(target_arch = "x86_64")]
527 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_getcpu"));
528 #[cfg(target_arch = "x86")]
529 let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_getcpu"));
530 #[cfg(target_arch = "riscv64")]
531 let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_getcpu"));
532 #[cfg(target_arch = "powerpc")]
533 let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_getcpu"));
534 #[cfg(target_arch = "powerpc64")]
535 let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_getcpu"));
536 #[cfg(target_arch = "s390x")]
537 let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_getcpu"));
538
539 #[cfg(not(any(target_arch = "powerpc", target_arch = "x86")))]
542 assert!(!ptr.is_null());
543 }
544 }
545}