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