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 };