#![deny(missing_docs)]
#![deny(clippy::all)]
use crt0stack::{Entry, Reader};
use std::ffi::CStr;
use std::os::raw::c_char;
use std::slice::from_raw_parts;
#[cfg(target_pointer_width = "64")]
mod elf {
pub use goblin::elf64::dynamic::*;
pub use goblin::elf64::header::*;
pub use goblin::elf64::program_header::*;
pub use goblin::elf64::section_header::*;
pub use goblin::elf64::sym::Sym;
pub const CLASS: u8 = ELFCLASS64;
pub type Word = u64;
}
#[cfg(target_pointer_width = "32")]
mod elf {
pub use goblin::elf32::dynamic::*;
pub use goblin::elf32::header::*;
pub use goblin::elf32::program_header::*;
pub use goblin::elf32::section_header::*;
pub use goblin::elf32::sym::Sym;
pub const CLASS: u8 = ELFCLASS32;
pub type Word = u32;
}
#[repr(transparent)]
struct Header(elf::Header);
impl Header {
#[allow(clippy::trivially_copy_pass_by_ref)]
pub unsafe fn from_ptr(ptr: &()) -> Option<&Self> {
let hdr = &*(ptr as *const _ as *const Self);
if hdr.0.e_ident[..elf::ELFMAG.len()] != elf::ELFMAG[..] {
return None;
}
if hdr.0.e_ident[elf::EI_CLASS] != elf::CLASS {
return None;
}
Some(hdr)
}
unsafe fn ptr<T>(&self, off: impl Into<elf::Word>) -> *const T {
let addr = self as *const _ as *const u8;
addr.add(off.into() as usize) as *const T
}
unsafe fn slice<T>(&self, off: impl Into<elf::Word>, len: impl Into<elf::Word>) -> &[T] {
from_raw_parts::<u8>(self.ptr(off), len.into() as usize)
.align_to()
.1
}
unsafe fn shtab(&self) -> &[elf::SectionHeader] {
self.slice(self.0.e_shoff, self.0.e_shentsize * self.0.e_shnum)
}
unsafe fn section<T>(&self, kind: u32) -> Option<&[T]> {
for sh in self.shtab() {
if sh.sh_type == kind {
return Some(self.slice(sh.sh_offset, sh.sh_size));
}
}
None
}
unsafe fn symbol(&self, name: &str) -> Option<&Symbol> {
let symstrtab: &[c_char] = self.section(elf::SHT_STRTAB)?;
let symtab: &[elf::Sym] = self.section(elf::SHT_DYNSYM)?;
for sym in symtab {
let cstr = CStr::from_ptr(&symstrtab[sym.st_name as usize]);
if let Ok(s) = cstr.to_str() {
if s == name {
let addr = self.ptr(sym.st_value) as *const Symbol;
return Some(&*addr);
}
}
}
None
}
}
pub enum Symbol {}
pub struct Vdso<'a>(&'a Header);
impl Vdso<'static> {
pub fn locate() -> Option<Self> {
for aux in Reader::from_environ().done() {
if let Entry::SysInfoEHdr(addr) = aux {
let hdr = unsafe { Header::from_ptr(&*(addr as *const _))? };
return Some(Self(hdr));
}
}
None
}
}
impl<'a> Vdso<'a> {
pub fn lookup(&self, name: &str) -> Option<&'a Symbol> {
unsafe { self.0.symbol(name) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use libc::time_t;
use std::mem::transmute;
use std::ptr::null_mut;
#[test]
fn time() {
let vdso = Vdso::locate().unwrap();
let func = vdso.lookup("time").unwrap();
let func: extern "C" fn(*mut time_t) -> time_t = unsafe { transmute(func) };
let libc = unsafe { libc::time(null_mut()) };
let vdso = func(null_mut());
assert!(vdso - libc <= 1);
}
}