use core::mem::MaybeUninit;
use rusl::platform::{ElfHeader, ElfSymbol, SectionHeader, TimeSpec};
use rusl::string::unix_str::UnixStr;
pub(crate) static mut VDSO_CLOCK_GET_TIME: Option<extern "C" fn(i32, *mut TimeSpec) -> i32> = None;
pub(crate) unsafe fn init_vdso_get_time() {
let elf_start = crate::elf::aux::AUX_VALUES.at_sysinfo_ehdr;
if elf_start != 0 {
let get_time = find_vdso_clock_get_time(elf_start as _);
VDSO_CLOCK_GET_TIME = get_time;
}
}
const DYNSTR_NAME: &[u8] = b".dynstr\0";
const DYNSYM_NAME: &[u8] = b".dynsym\0";
const CLOCK_GETTIME_NAME: &[u8] = b"__vdso_clock_gettime\0";
#[expect(clippy::cast_possible_truncation)]
unsafe fn find_vdso_clock_get_time(
vdso: *const u8,
) -> Option<extern "C" fn(i32, *mut TimeSpec) -> i32> {
let mut elf_ptr = MaybeUninit::<ElfHeader>::uninit();
vdso.copy_to(
elf_ptr.as_mut_ptr().cast::<u8>(),
core::mem::size_of::<ElfHeader>(),
);
let header = elf_ptr.assume_init();
let mut dyn_syms = None;
#[allow(clippy::cast_ptr_alignment)]
let section_start = vdso
.add(header.0.e_shoff as usize)
.cast::<rusl::platform::SectionHeader>();
let name_section = if header.0.e_shstrndx == 0 {
return None;
} else {
section_start.add(header.0.e_shstrndx as usize).read()
};
let mut clock_gettime_st_name_offset: Option<u32> = None;
for i in 0..header.0.e_shnum as usize {
let sect = section_start.add(i).read();
if match_name(DYNSTR_NAME, §, &name_section, vdso) {
clock_gettime_st_name_offset =
find_dynstr_st_name_offset_of(CLOCK_GETTIME_NAME, §, vdso);
} else if match_name(DYNSYM_NAME, §, &name_section, vdso) {
dyn_syms = Some(sect);
}
if dyn_syms.is_some() && clock_gettime_st_name_offset.is_some() {
break;
}
}
let dyn_syms = dyn_syms?;
let clock_alias = clock_gettime_st_name_offset?;
let function_pointer_info = find_dynsym_ptr_of_name_offset(clock_alias, &dyn_syms, vdso)?;
let containing_section = section_start.add(function_pointer_info.section).read();
let fptr_align = containing_section.0.sh_addralign as usize;
let fn_addr = vdso.add(align(function_pointer_info.addr_offset, fptr_align));
let func: extern "C" fn(i32, *mut TimeSpec) -> i32 = core::mem::transmute(fn_addr);
Some(func)
}
#[inline]
#[expect(clippy::cast_possible_truncation)]
unsafe fn match_name(
search_for: &[u8],
candidate_section: &SectionHeader,
name_section: &SectionHeader,
vdso: *const u8,
) -> bool {
let name_start = candidate_section.0.sh_name as usize;
let ns_start = name_section.0.sh_offset as usize;
let ns_ptr = vdso.add(ns_start);
let start_at = ns_ptr.add(align(name_start, name_section.0.sh_addralign as usize));
let name = UnixStr::from_ptr(start_at);
search_for == name.as_slice()
}
#[inline]
#[expect(clippy::cast_possible_truncation)]
unsafe fn find_dynstr_st_name_offset_of(
search_for: &[u8],
dyn_str_section: &SectionHeader,
vdso: *const u8,
) -> Option<u32> {
let mut offset = 1;
while offset < dyn_str_section.0.sh_size as usize {
let start = vdso.add(align(
dyn_str_section.0.sh_offset as usize + offset,
dyn_str_section.0.sh_addralign as usize,
));
let first_sym = UnixStr::from_ptr(start);
if search_for == first_sym.as_slice() {
return u32::try_from(offset).ok();
}
offset += first_sym.len();
}
None
}
struct FnPtrInfo {
addr_offset: usize,
section: usize,
}
#[inline]
#[expect(clippy::cast_possible_truncation)]
unsafe fn find_dynsym_ptr_of_name_offset(
st_name: u32,
dynsym: &SectionHeader,
vdso: *const u8,
) -> Option<FnPtrInfo> {
let mut offset = 0;
while offset < dynsym.0.sh_size as usize {
let search_addr = align(
dynsym.0.sh_offset as usize + offset,
dynsym.0.sh_addralign as usize,
);
let start = vdso.add(search_addr);
let mut sym_h = MaybeUninit::<ElfSymbol>::uninit();
start.copy_to(sym_h.as_mut_ptr().cast(), core::mem::size_of::<ElfSymbol>());
let sym = sym_h.assume_init();
if sym.0.st_name == st_name {
return Some(FnPtrInfo {
addr_offset: sym.0.st_value as usize,
section: sym.0.st_shndx as usize,
});
}
offset += core::mem::size_of::<ElfSymbol>();
}
None
}
#[inline]
fn align(offset: usize, alignment: usize) -> usize {
offset + (alignment - (offset % alignment)) % alignment
}
#[inline]
#[expect(dead_code)]
fn info_to_type(st_info: u8) -> u32 {
u32::from(st_info) & 0xf
}
#[inline]
#[expect(dead_code)]
fn info_to_bind(st_info: u8) -> u8 {
st_info >> 4
}