hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 16079e6475f338574113eb1984ecdf3f5868e61d
parent 71be4f501fc5211cf67c77232cc7c879ee5a3b2e
Author: Mykyta Holubakha <hilobakho@gmail.com>
Date:   Thu, 18 Mar 2021 15:37:58 +0200

linux: implement getting symbols from vDSO

Diffstat:
Alinux/vdso/vdso.ha | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 166 insertions(+), 0 deletions(-)

diff --git a/linux/vdso/vdso.ha b/linux/vdso/vdso.ha @@ -0,0 +1,166 @@ +use strings; +use format::elf; +use linux; + +let ehdr: nullable *elf::header64 = null; + +fn sys_infoehdr() nullable *elf::header64 = { + static let ehdr_checked = false; + if (ehdr_checked) { + return ehdr; + }; + ehdr_checked = true; + + for (let i = 0; linux::auxv[i].a_type != 0; i += 1) { + if (linux::auxv[i].a_type != elf::at::SYSINFO_EHDR: u64) + continue; + ehdr = linux::auxv[i].a_val: uintptr: *elf::header64; + return ehdr; + }; + return null; +}; + +type vdso_ctx = struct { + segbase: uintptr, + stringtab: *char, + symtab: *[*]elf::sym64, + hashhdr: *elf::hashhdr, + versym: nullable *[*]u16, + verdef: nullable *elf::verdef64, +}; + +let ctx: nullable *vdso_ctx = null; + +fn get_vdso_ctx() nullable *vdso_ctx = { + static let vdso_checked = false; + if (vdso_checked) { + return ctx; + }; + vdso_checked = true; + + const eh = match (sys_infoehdr()) { + null => return null, + x: *elf::header64 => x, + }; + + const ehui = eh: uintptr; + let phui = ehui + eh.e_phoff: uintptr; + let dynvec: nullable *[*]elf::dyn64 = null; + let baseseg: nullable *void = null; + + for (let i: u16 = 0; i < eh.e_phnum; i += 1) { + const ph = phui: *elf::phdr64; + switch (ph.p_type) { + elf::pt::LOAD => + baseseg = ehui + + ph.p_offset: uintptr - + ph.p_vaddr: uintptr, + elf::pt::DYNAMIC => + dynvec = ehui + ph.p_offset: uintptr, + }; + phui += eh.e_phentsize: uintptr; + }; + + if (dynvec == null || baseseg == null) { + return null; + }; + const dynv = dynvec: *[*]elf::dyn64; + + let segbase = baseseg: uintptr; + let stringtab: nullable *char = null; + let symtab: nullable *[*]elf::sym64 = null; + let hashhdr: nullable *elf::hashhdr = null; + let versym: nullable *[*]u16 = null; + let verdef: nullable *elf::verdef64 = null; + + for (let i = 0; dynv[i].d_tag != elf::dt::NULL; i += 1) { + const tabptr = (segbase + dynv[i].d_val: uintptr): *void; + switch (dynv[i].d_tag) { + elf::dt::STRTAB => stringtab = tabptr: *char, + elf::dt::SYMTAB => symtab = tabptr: *[*]elf::sym64, + elf::dt::HASH => hashhdr = tabptr: *elf::hashhdr, + elf::dt::VERSYM => versym = tabptr: *[*]u16, + elf::dt::VERDEF => verdef = tabptr: *elf::verdef64, + * => continue, + }; + }; + + if (stringtab == null || symtab == null || hashhdr == null) { + return null; + }; + + if (verdef == null) { + versym = null; + }; + + // TODO: use a static variable here somehow(?) + const vctx = alloc(vdso_ctx { + segbase = segbase, + stringtab = stringtab: *char, + symtab = symtab: *[*]elf::sym64, + hashhdr = hashhdr: *elf::hashhdr, + verdef = verdef, + versym = versym, + }); + ctx = vctx; + + return ctx; +}; + +fn vdso_checkver(ctx: *vdso_ctx, version: str, num: u32) bool = { + let prev = null: *elf::verdef64; + let cur = match (ctx.verdef) { + null => return true, + vd: *elf::verdef64 => vd, + }; + const versym = match (ctx.versym) { + null => return true, + vs: *[*]u16 => vs[num] & 0x7fff + }; + for (cur != prev) { + if (cur.vd_flags & elf::ver_flg::BASE: u16 == 0 && + cur.vd_ndx & 0x7fff == versym) { + const aux = (cur: uintptr + + cur.vd_aux: uintptr): *elf::verdaux64; + const name = ctx.stringtab + + aux.vda_name: uintptr: *char; + return version == strings::from_c(name); + }; + prev = cur; + cur += cur.vd_next: uintptr; + }; + return false; +}; + +export fn get_vdso_sym(symname: str, symver: str) nullable *void = { + const ctx = match (get_vdso_ctx()) { + null => return null, + x: *vdso_ctx => x, + }; + + const sym_types = (1 << elf::stt::NOTYPE | + 1 << elf::stt::OBJECT | + 1 << elf::stt::FUNC | + 1 << elf::stt::COMMON): size; + + const sym_binds = (1 << elf::stb::GLOBAL | + 1 << elf::stb::WEAK): size; + + for (let i = 0u32; i < ctx.hashhdr.nchain; i += 1) { + const sym = ctx.symtab[i]; + const symtype = 1 << (sym.st_info & 0xf): size; + const symbind = 1 << (sym.st_info >> 4): size; + if (symtype & sym_types == 0 || symbind & sym_binds == 0 || + sym.st_shndx == 0) { + continue; + }; + const name = ctx.stringtab + sym.st_name: uintptr: *char; + const s: str = strings::from_c(name); + if (s != symname) + continue; + if (!vdso_checkver(ctx, symver, i)) + continue; + return (ctx.segbase + sym.st_value: uintptr): *void; + }; + return null; +};