| 1 | const std = @import("../../std.zig"); |
| 2 | const elf = std.elf; |
| 3 | const linux = std.os.linux; |
| 4 | const mem = std.mem; |
| 5 | const maxInt = std.math.maxInt; |
| 6 | |
| 7 | pub fn lookup(vername: []const u8, name: []const u8) usize { |
| 8 | const vdso_addr = linux.getauxval(std.elf.AT_SYSINFO_EHDR); |
| 9 | if (vdso_addr == 0) return 0; |
| 10 | |
| 11 | const eh = @as(*elf.Ehdr, @ptrFromInt(vdso_addr)); |
| 12 | var ph_addr: usize = vdso_addr + eh.e_phoff; |
| 13 | |
| 14 | var maybe_dynv: ?[*]usize = null; |
| 15 | var base: usize = maxInt(usize); |
| 16 | { |
| 17 | var i: usize = 0; |
| 18 | while (i < eh.e_phnum) : ({ |
| 19 | i += 1; |
| 20 | ph_addr += eh.e_phentsize; |
| 21 | }) { |
| 22 | const this_ph = @as(*elf.ElfN.Phdr, @ptrFromInt(ph_addr)); |
| 23 | switch (this_ph.type) { |
| 24 | // On WSL1 as well as older kernels, the VDSO ELF image is pre-linked in the upper half |
| 25 | // of the memory space (e.g. vaddr = 0xffffffffff700000 on WSL1). |
| 26 | // Wrapping operations are used on this line as well as subsequent calculations relative to base |
| 27 | // (lines 47, 78) to ensure no overflow check is tripped. |
| 28 | .LOAD => base = vdso_addr +% this_ph.offset -% this_ph.vaddr, |
| 29 | .DYNAMIC => maybe_dynv = @as([*]usize, @ptrFromInt(vdso_addr + this_ph.offset)), |
| 30 | else => {}, |
| 31 | } |
| 32 | } |
| 33 | } |
| 34 | const dynv = maybe_dynv orelse return 0; |
| 35 | if (base == maxInt(usize)) return 0; |
| 36 | |
| 37 | var maybe_strings: ?[*:0]u8 = null; |
| 38 | var maybe_syms: ?[*]elf.Sym = null; |
| 39 | var maybe_hashtab: ?[*]linux.Elf_Symndx = null; |
| 40 | var maybe_versym: ?[*]elf.Versym = null; |
| 41 | var maybe_verdef: ?*elf.Verdef = null; |
| 42 | |
| 43 | { |
| 44 | var i: usize = 0; |
| 45 | while (dynv[i] != 0) : (i += 2) { |
| 46 | const p = base +% dynv[i + 1]; |
| 47 | switch (dynv[i]) { |
| 48 | elf.DT_STRTAB => maybe_strings = @ptrFromInt(p), |
| 49 | elf.DT_SYMTAB => maybe_syms = @ptrFromInt(p), |
| 50 | elf.DT_HASH => maybe_hashtab = @ptrFromInt(p), |
| 51 | elf.DT_VERSYM => maybe_versym = @ptrFromInt(p), |
| 52 | elf.DT_VERDEF => maybe_verdef = @ptrFromInt(p), |
| 53 | else => {}, |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | const strings = maybe_strings orelse return 0; |
| 59 | const syms = maybe_syms orelse return 0; |
| 60 | const hashtab = maybe_hashtab orelse return 0; |
| 61 | if (maybe_verdef == null) maybe_versym = null; |
| 62 | |
| 63 | const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); |
| 64 | const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE); |
| 65 | |
| 66 | var i: usize = 0; |
| 67 | while (i < hashtab[1]) : (i += 1) { |
| 68 | if (0 == (@as(u32, 1) << @as(u5, @intCast(syms[i].st_info & 0xf)) & OK_TYPES)) continue; |
| 69 | if (0 == (@as(u32, 1) << @as(u5, @intCast(syms[i].st_info >> 4)) & OK_BINDS)) continue; |
| 70 | if (0 == syms[i].st_shndx) continue; |
| 71 | const sym_name = @as([*:0]u8, @ptrCast(strings + syms[i].st_name)); |
| 72 | if (!mem.eql(u8, name, mem.sliceTo(sym_name, 0))) continue; |
| 73 | if (maybe_versym) |versym| { |
| 74 | if (!checkver(maybe_verdef.?, versym[i], vername, strings)) |
| 75 | continue; |
| 76 | } |
| 77 | return base +% syms[i].st_value; |
| 78 | } |
| 79 | |
| 80 | return 0; |
| 81 | } |
| 82 | |
| 83 | fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, strings: [*:0]u8) bool { |
| 84 | var def = def_arg; |
| 85 | const vsym_index = vsym_arg.VERSION; |
| 86 | while (true) { |
| 87 | if (0 == (def.flags & elf.VER_FLG_BASE) and @backingInt(def.ndx) == vsym_index) break; |
| 88 | if (def.next == 0) return false; |
| 89 | def = @ptrFromInt(@intFromPtr(def) + def.next); |
| 90 | } |
| 91 | const aux: *elf.Verdaux = @ptrFromInt(@intFromPtr(def) + def.aux); |
| 92 | return mem.eql(u8, vername, mem.sliceTo(strings + aux.name, 0)); |
| 93 | } |