162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * parse_vdso.c: Linux reference vDSO parser 362306a36Sopenharmony_ci * Written by Andrew Lutomirski, 2011-2014. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This code is meant to be linked in to various programs that run on Linux. 662306a36Sopenharmony_ci * As such, it is available with as few restrictions as possible. This file 762306a36Sopenharmony_ci * is licensed under the Creative Commons Zero License, version 1.0, 862306a36Sopenharmony_ci * available at http://creativecommons.org/publicdomain/zero/1.0/legalcode 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * The vDSO is a regular ELF DSO that the kernel maps into user space when 1162306a36Sopenharmony_ci * it starts a program. It works equally well in statically and dynamically 1262306a36Sopenharmony_ci * linked binaries. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This code is tested on x86. In principle it should work on any 1562306a36Sopenharmony_ci * architecture that has a vDSO. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <stdbool.h> 1962306a36Sopenharmony_ci#include <stdint.h> 2062306a36Sopenharmony_ci#include <string.h> 2162306a36Sopenharmony_ci#include <limits.h> 2262306a36Sopenharmony_ci#include <elf.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "parse_vdso.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* And here's the code. */ 2762306a36Sopenharmony_ci#ifndef ELF_BITS 2862306a36Sopenharmony_ci# if ULONG_MAX > 0xffffffffUL 2962306a36Sopenharmony_ci# define ELF_BITS 64 3062306a36Sopenharmony_ci# else 3162306a36Sopenharmony_ci# define ELF_BITS 32 3262306a36Sopenharmony_ci# endif 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x 3662306a36Sopenharmony_ci#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) 3762306a36Sopenharmony_ci#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic struct vdso_info 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci bool valid; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* Load information */ 4462306a36Sopenharmony_ci uintptr_t load_addr; 4562306a36Sopenharmony_ci uintptr_t load_offset; /* load_addr - recorded vaddr */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Symbol table */ 4862306a36Sopenharmony_ci ELF(Sym) *symtab; 4962306a36Sopenharmony_ci const char *symstrings; 5062306a36Sopenharmony_ci ELF(Word) *bucket, *chain; 5162306a36Sopenharmony_ci ELF(Word) nbucket, nchain; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* Version table */ 5462306a36Sopenharmony_ci ELF(Versym) *versym; 5562306a36Sopenharmony_ci ELF(Verdef) *verdef; 5662306a36Sopenharmony_ci} vdso_info; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Straight from the ELF specification. */ 5962306a36Sopenharmony_cistatic unsigned long elf_hash(const unsigned char *name) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci unsigned long h = 0, g; 6262306a36Sopenharmony_ci while (*name) 6362306a36Sopenharmony_ci { 6462306a36Sopenharmony_ci h = (h << 4) + *name++; 6562306a36Sopenharmony_ci if (g = h & 0xf0000000) 6662306a36Sopenharmony_ci h ^= g >> 24; 6762306a36Sopenharmony_ci h &= ~g; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci return h; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_civoid vdso_init_from_sysinfo_ehdr(uintptr_t base) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci size_t i; 7562306a36Sopenharmony_ci bool found_vaddr = false; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci vdso_info.valid = false; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci vdso_info.load_addr = base; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ELF(Ehdr) *hdr = (ELF(Ehdr)*)base; 8262306a36Sopenharmony_ci if (hdr->e_ident[EI_CLASS] != 8362306a36Sopenharmony_ci (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) { 8462306a36Sopenharmony_ci return; /* Wrong ELF class -- check ELF_BITS */ 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff); 8862306a36Sopenharmony_ci ELF(Dyn) *dyn = 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* 9162306a36Sopenharmony_ci * We need two things from the segment table: the load offset 9262306a36Sopenharmony_ci * and the dynamic table. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci for (i = 0; i < hdr->e_phnum; i++) 9562306a36Sopenharmony_ci { 9662306a36Sopenharmony_ci if (pt[i].p_type == PT_LOAD && !found_vaddr) { 9762306a36Sopenharmony_ci found_vaddr = true; 9862306a36Sopenharmony_ci vdso_info.load_offset = base 9962306a36Sopenharmony_ci + (uintptr_t)pt[i].p_offset 10062306a36Sopenharmony_ci - (uintptr_t)pt[i].p_vaddr; 10162306a36Sopenharmony_ci } else if (pt[i].p_type == PT_DYNAMIC) { 10262306a36Sopenharmony_ci dyn = (ELF(Dyn)*)(base + pt[i].p_offset); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (!found_vaddr || !dyn) 10762306a36Sopenharmony_ci return; /* Failed */ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * Fish out the useful bits of the dynamic table. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci ELF(Word) *hash = 0; 11362306a36Sopenharmony_ci vdso_info.symstrings = 0; 11462306a36Sopenharmony_ci vdso_info.symtab = 0; 11562306a36Sopenharmony_ci vdso_info.versym = 0; 11662306a36Sopenharmony_ci vdso_info.verdef = 0; 11762306a36Sopenharmony_ci for (i = 0; dyn[i].d_tag != DT_NULL; i++) { 11862306a36Sopenharmony_ci switch (dyn[i].d_tag) { 11962306a36Sopenharmony_ci case DT_STRTAB: 12062306a36Sopenharmony_ci vdso_info.symstrings = (const char *) 12162306a36Sopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 12262306a36Sopenharmony_ci + vdso_info.load_offset); 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci case DT_SYMTAB: 12562306a36Sopenharmony_ci vdso_info.symtab = (ELF(Sym) *) 12662306a36Sopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 12762306a36Sopenharmony_ci + vdso_info.load_offset); 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case DT_HASH: 13062306a36Sopenharmony_ci hash = (ELF(Word) *) 13162306a36Sopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 13262306a36Sopenharmony_ci + vdso_info.load_offset); 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci case DT_VERSYM: 13562306a36Sopenharmony_ci vdso_info.versym = (ELF(Versym) *) 13662306a36Sopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 13762306a36Sopenharmony_ci + vdso_info.load_offset); 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci case DT_VERDEF: 14062306a36Sopenharmony_ci vdso_info.verdef = (ELF(Verdef) *) 14162306a36Sopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 14262306a36Sopenharmony_ci + vdso_info.load_offset); 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci if (!vdso_info.symstrings || !vdso_info.symtab || !hash) 14762306a36Sopenharmony_ci return; /* Failed */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!vdso_info.verdef) 15062306a36Sopenharmony_ci vdso_info.versym = 0; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Parse the hash table header. */ 15362306a36Sopenharmony_ci vdso_info.nbucket = hash[0]; 15462306a36Sopenharmony_ci vdso_info.nchain = hash[1]; 15562306a36Sopenharmony_ci vdso_info.bucket = &hash[2]; 15662306a36Sopenharmony_ci vdso_info.chain = &hash[vdso_info.nbucket + 2]; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* That's all we need. */ 15962306a36Sopenharmony_ci vdso_info.valid = true; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic bool vdso_match_version(ELF(Versym) ver, 16362306a36Sopenharmony_ci const char *name, ELF(Word) hash) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci /* 16662306a36Sopenharmony_ci * This is a helper function to check if the version indexed by 16762306a36Sopenharmony_ci * ver matches name (which hashes to hash). 16862306a36Sopenharmony_ci * 16962306a36Sopenharmony_ci * The version definition table is a mess, and I don't know how 17062306a36Sopenharmony_ci * to do this in better than linear time without allocating memory 17162306a36Sopenharmony_ci * to build an index. I also don't know why the table has 17262306a36Sopenharmony_ci * variable size entries in the first place. 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * For added fun, I can't find a comprehensible specification of how 17562306a36Sopenharmony_ci * to parse all the weird flags in the table. 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * So I just parse the whole table every time. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* First step: find the version definition */ 18162306a36Sopenharmony_ci ver &= 0x7fff; /* Apparently bit 15 means "hidden" */ 18262306a36Sopenharmony_ci ELF(Verdef) *def = vdso_info.verdef; 18362306a36Sopenharmony_ci while(true) { 18462306a36Sopenharmony_ci if ((def->vd_flags & VER_FLG_BASE) == 0 18562306a36Sopenharmony_ci && (def->vd_ndx & 0x7fff) == ver) 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (def->vd_next == 0) 18962306a36Sopenharmony_ci return false; /* No definition. */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci def = (ELF(Verdef) *)((char *)def + def->vd_next); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Now figure out whether it matches. */ 19562306a36Sopenharmony_ci ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux); 19662306a36Sopenharmony_ci return def->vd_hash == hash 19762306a36Sopenharmony_ci && !strcmp(name, vdso_info.symstrings + aux->vda_name); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_civoid *vdso_sym(const char *version, const char *name) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci unsigned long ver_hash; 20362306a36Sopenharmony_ci if (!vdso_info.valid) 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ver_hash = elf_hash(version); 20762306a36Sopenharmony_ci ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) { 21062306a36Sopenharmony_ci ELF(Sym) *sym = &vdso_info.symtab[chain]; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Check for a defined global or weak function w/ right name. */ 21362306a36Sopenharmony_ci if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) 21462306a36Sopenharmony_ci continue; 21562306a36Sopenharmony_ci if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && 21662306a36Sopenharmony_ci ELF64_ST_BIND(sym->st_info) != STB_WEAK) 21762306a36Sopenharmony_ci continue; 21862306a36Sopenharmony_ci if (sym->st_shndx == SHN_UNDEF) 21962306a36Sopenharmony_ci continue; 22062306a36Sopenharmony_ci if (strcmp(name, vdso_info.symstrings + sym->st_name)) 22162306a36Sopenharmony_ci continue; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Check symbol version. */ 22462306a36Sopenharmony_ci if (vdso_info.versym 22562306a36Sopenharmony_ci && !vdso_match_version(vdso_info.versym[chain], 22662306a36Sopenharmony_ci version, ver_hash)) 22762306a36Sopenharmony_ci continue; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return (void *)(vdso_info.load_offset + sym->st_value); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_civoid vdso_init_from_auxv(void *auxv) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci ELF(auxv_t) *elf_auxv = auxv; 23862306a36Sopenharmony_ci for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++) 23962306a36Sopenharmony_ci { 24062306a36Sopenharmony_ci if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) { 24162306a36Sopenharmony_ci vdso_init_from_sysinfo_ehdr(elf_auxv[i].a_un.a_val); 24262306a36Sopenharmony_ci return; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci vdso_info.valid = false; 24762306a36Sopenharmony_ci} 248