hare

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

vdso.ha (4488B)


      1 // License: MPL-2.0
      2 // (c) 2021 Drew DeVault <sir@cmpwn.com>
      3 // (c) 2021 Ember Sawady <ecs@d2evs.net>
      4 // (c) 2021 Mykyta Holubakha <hilobakho@gmail.com>
      5 use format::elf;
      6 use linux;
      7 use types::c;
      8 
      9 let ehdr: nullable *elf::header64 = null;
     10 
     11 fn sys_infoehdr() nullable *elf::header64 = {
     12 	static let ehdr_checked = false;
     13 	if (ehdr_checked) {
     14 		return ehdr;
     15 	};
     16 	ehdr_checked = true;
     17 
     18 	for (let i = 0; linux::auxv[i].a_type != 0; i += 1) {
     19 		if (linux::auxv[i].a_type != elf::at::SYSINFO_EHDR)
     20 			continue;
     21 		ehdr = linux::auxv[i].a_val: uintptr: *elf::header64;
     22 		return ehdr;
     23 	};
     24 	return null;
     25 };
     26 
     27 type vdso_ctx = struct {
     28 	segbase: uintptr,
     29 	stringtab: *c::char,
     30 	symtab: *[*]elf::sym64,
     31 	hashhdr: *elf::hashhdr,
     32 	versym: nullable *[*]u16,
     33 	verdef: nullable *elf::verdef64,
     34 };
     35 
     36 let ctx: nullable *vdso_ctx = null;
     37 
     38 fn get_vdso_ctx() nullable *vdso_ctx = {
     39 	static let vdso_checked = false;
     40 	if (vdso_checked) {
     41 		return ctx;
     42 	};
     43 	vdso_checked = true;
     44 
     45 	const eh = match (sys_infoehdr()) {
     46 	case null =>
     47 		return null;
     48 	case let x: *elf::header64 =>
     49 		yield x;
     50 	};
     51 
     52 	const ehui = eh: uintptr;
     53 	let phui = ehui + eh.e_phoff: uintptr;
     54 	let dynvec: nullable *[*]elf::dyn64 = null;
     55 	let baseseg: nullable *void = null;
     56 
     57 	for (let i: u16 = 0; i < eh.e_phnum; i += 1) {
     58 		const ph = phui: *elf::phdr64;
     59 		switch (ph.p_type) {
     60 		case elf::pt::LOAD =>
     61 			baseseg = (ehui +
     62 				ph.p_offset: uintptr -
     63 				ph.p_vaddr: uintptr): nullable *void;
     64 		case elf::pt::DYNAMIC =>
     65 			dynvec = (ehui +
     66 				ph.p_offset: uintptr): *[*]elf::dyn64;
     67 		case => void;
     68 		};
     69 		phui += eh.e_phentsize: uintptr;
     70 	};
     71 
     72 	if (dynvec == null || baseseg == null) {
     73 		return null;
     74 	};
     75 	const dynv = dynvec: *[*]elf::dyn64;
     76 
     77 	let segbase = baseseg: uintptr;
     78 	let stringtab: nullable *c::char = null;
     79 	let symtab: nullable *[*]elf::sym64 = null;
     80 	let hashhdr: nullable *elf::hashhdr = null;
     81 	let versym: nullable *[*]u16 = null;
     82 	let verdef: nullable *elf::verdef64 = null;
     83 
     84 	for (let i = 0; dynv[i].d_tag != elf::dt::NULL; i += 1) {
     85 		const tabptr = (segbase + dynv[i].d_val: uintptr): *void;
     86 		switch (dynv[i].d_tag) {
     87 		case elf::dt::STRTAB =>
     88 			stringtab = tabptr: *c::char;
     89 		case elf::dt::SYMTAB =>
     90 			symtab = tabptr: *[*]elf::sym64;
     91 		case elf::dt::HASH =>
     92 			hashhdr = tabptr: *elf::hashhdr;
     93 		case elf::dt::VERSYM =>
     94 			versym = tabptr: *[*]u16;
     95 		case elf::dt::VERDEF =>
     96 			verdef = tabptr: *elf::verdef64;
     97 		case =>
     98 			continue;
     99 		};
    100 	};
    101 
    102 	if (stringtab == null || symtab == null || hashhdr == null) {
    103 		return null;
    104 	};
    105 
    106 	if (verdef == null) {
    107 		versym = null;
    108 	};
    109 
    110 	// TODO: use a static variable here somehow(?)
    111 	const vctx = alloc(vdso_ctx {
    112 		segbase = segbase,
    113 		stringtab = stringtab: *c::char,
    114 		symtab = symtab: *[*]elf::sym64,
    115 		hashhdr = hashhdr: *elf::hashhdr,
    116 		verdef = verdef,
    117 		versym = versym,
    118 	});
    119 	ctx = vctx;
    120 
    121 	return ctx;
    122 };
    123 
    124 fn vdso_checkver(ctx: *vdso_ctx, version: str, num: u32) bool = {
    125 	let prev = null: *elf::verdef64;
    126 	let cur = match (ctx.verdef) {
    127 	case null =>
    128 		return true;
    129 	case let vd: *elf::verdef64 =>
    130 		yield vd;
    131 	};
    132 	const versym = match (ctx.versym) {
    133 	case null =>
    134 		return true;
    135 	case let vs: *[*]u16 =>
    136 		yield vs[num] & 0x7ff;
    137 	};
    138 	for (cur != prev) {
    139 		if (cur.vd_flags & elf::ver_flg::BASE: u16 == 0 &&
    140 			cur.vd_ndx & 0x7fff == versym) {
    141 			const aux = (cur: uintptr +
    142 				cur.vd_aux: uintptr): *elf::verdaux64;
    143 			const name = ctx.stringtab: uintptr +
    144 				aux.vda_name: uintptr;
    145 			return version == c::tostr(name: *c::char)!;
    146 		};
    147 		prev = cur;
    148 		cur = (cur: uintptr + cur.vd_next: uintptr): *elf::verdef64;
    149 	};
    150 	return false;
    151 };
    152 
    153 export fn getsym(symname: str, symver: str) nullable *void = {
    154 	const ctx = match (get_vdso_ctx()) {
    155 	case null =>
    156 		return null;
    157 	case let x: *vdso_ctx =>
    158 		yield x;
    159 	};
    160 
    161 	const sym_types = (1 << elf::stt::NOTYPE |
    162 			1 << elf::stt::OBJECT |
    163 			1 << elf::stt::FUNC |
    164 			1 << elf::stt::COMMON): size;
    165 
    166 	const sym_binds = (1 << elf::stb::GLOBAL |
    167 			1 << elf::stb::WEAK): size;
    168 
    169 	for (let i = 0u32; i < ctx.hashhdr.nchain; i += 1) {
    170 		const sym = ctx.symtab[i];
    171 		const symtype = 1 << (sym.st_info & 0xf): size;
    172 		const symbind = 1 << (sym.st_info >> 4): size;
    173 		if (symtype & sym_types == 0 || symbind & sym_binds == 0 ||
    174 			sym.st_shndx == 0) {
    175 			continue;
    176 		};
    177 		const name = ctx.stringtab: uintptr + sym.st_name: uintptr;
    178 		const s: str = c::tostr(name: *const c::char)!;
    179 		if (s != symname)
    180 			continue;
    181 		if (!vdso_checkver(ctx, symver, i))
    182 			continue;
    183 		return (ctx.segbase + sym.st_value: uintptr): *void;
    184 	};
    185 	return null;
    186 };