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