1f08c3bdfSopenharmony_ci/* 2f08c3bdfSopenharmony_ci * parse_vdso.c: Linux reference vDSO parser 3f08c3bdfSopenharmony_ci * Written by Andrew Lutomirski, 2011-2014. 4f08c3bdfSopenharmony_ci * 5f08c3bdfSopenharmony_ci * This code is meant to be linked in to various programs that run on Linux. 6f08c3bdfSopenharmony_ci * As such, it is available with as few restrictions as possible. This file 7f08c3bdfSopenharmony_ci * is licensed under the Creative Commons Zero License, version 1.0, 8f08c3bdfSopenharmony_ci * available at http://creativecommons.org/publicdomain/zero/1.0/legalcode 9f08c3bdfSopenharmony_ci * 10f08c3bdfSopenharmony_ci * The vDSO is a regular ELF DSO that the kernel maps into user space when 11f08c3bdfSopenharmony_ci * it starts a program. It works equally well in statically and dynamically 12f08c3bdfSopenharmony_ci * linked binaries. 13f08c3bdfSopenharmony_ci * 14f08c3bdfSopenharmony_ci * This code is tested on x86. In principle it should work on any 15f08c3bdfSopenharmony_ci * architecture that has a vDSO. 16f08c3bdfSopenharmony_ci */ 17f08c3bdfSopenharmony_ci 18f08c3bdfSopenharmony_ci#include <stdbool.h> 19f08c3bdfSopenharmony_ci#include <stdint.h> 20f08c3bdfSopenharmony_ci#include <string.h> 21f08c3bdfSopenharmony_ci#include <limits.h> 22f08c3bdfSopenharmony_ci#include <elf.h> 23f08c3bdfSopenharmony_ci 24f08c3bdfSopenharmony_ci/* And here's the code. */ 25f08c3bdfSopenharmony_ci#ifndef ELF_BITS 26f08c3bdfSopenharmony_ci# if ULONG_MAX > 0xffffffffUL 27f08c3bdfSopenharmony_ci# define ELF_BITS 64 28f08c3bdfSopenharmony_ci# else 29f08c3bdfSopenharmony_ci# define ELF_BITS 32 30f08c3bdfSopenharmony_ci# endif 31f08c3bdfSopenharmony_ci#endif 32f08c3bdfSopenharmony_ci 33f08c3bdfSopenharmony_ci#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x 34f08c3bdfSopenharmony_ci#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) 35f08c3bdfSopenharmony_ci#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x) 36f08c3bdfSopenharmony_ci 37f08c3bdfSopenharmony_cistatic struct vdso_info 38f08c3bdfSopenharmony_ci{ 39f08c3bdfSopenharmony_ci bool valid; 40f08c3bdfSopenharmony_ci 41f08c3bdfSopenharmony_ci /* Load information */ 42f08c3bdfSopenharmony_ci uintptr_t load_addr; 43f08c3bdfSopenharmony_ci uintptr_t load_offset; /* load_addr - recorded vaddr */ 44f08c3bdfSopenharmony_ci 45f08c3bdfSopenharmony_ci /* Symbol table */ 46f08c3bdfSopenharmony_ci ELF(Sym) *symtab; 47f08c3bdfSopenharmony_ci const char *symstrings; 48f08c3bdfSopenharmony_ci void *bucket, *chain; 49f08c3bdfSopenharmony_ci ELF(Word) nbucket, nchain; 50f08c3bdfSopenharmony_ci bool hash_ent_is_dword; 51f08c3bdfSopenharmony_ci 52f08c3bdfSopenharmony_ci /* Version table */ 53f08c3bdfSopenharmony_ci ELF(Versym) *versym; 54f08c3bdfSopenharmony_ci ELF(Verdef) *verdef; 55f08c3bdfSopenharmony_ci} vdso_info; 56f08c3bdfSopenharmony_ci 57f08c3bdfSopenharmony_ci/* Straight from the ELF specification. */ 58f08c3bdfSopenharmony_cistatic unsigned long elf_hash(const unsigned char *name) 59f08c3bdfSopenharmony_ci{ 60f08c3bdfSopenharmony_ci unsigned long h = 0, g; 61f08c3bdfSopenharmony_ci while (*name) 62f08c3bdfSopenharmony_ci { 63f08c3bdfSopenharmony_ci h = (h << 4) + *name++; 64f08c3bdfSopenharmony_ci if ((g = h & 0xf0000000)) 65f08c3bdfSopenharmony_ci h ^= g >> 24; 66f08c3bdfSopenharmony_ci h &= ~g; 67f08c3bdfSopenharmony_ci } 68f08c3bdfSopenharmony_ci return h; 69f08c3bdfSopenharmony_ci} 70f08c3bdfSopenharmony_ci 71f08c3bdfSopenharmony_ci/* return value of hash table entry */ 72f08c3bdfSopenharmony_ciELF(Word) get_hash_val(void *ptr, ELF(Word) idx) 73f08c3bdfSopenharmony_ci{ 74f08c3bdfSopenharmony_ci if (vdso_info.hash_ent_is_dword) { 75f08c3bdfSopenharmony_ci ELF(Xword) *table = ptr; 76f08c3bdfSopenharmony_ci /* for vdso assume all values fit in Elf Word */ 77f08c3bdfSopenharmony_ci return (ELF(Word)) table[idx]; 78f08c3bdfSopenharmony_ci } 79f08c3bdfSopenharmony_ci 80f08c3bdfSopenharmony_ci ELF(Word) *table = ptr; 81f08c3bdfSopenharmony_ci return table[idx]; 82f08c3bdfSopenharmony_ci} 83f08c3bdfSopenharmony_ci 84f08c3bdfSopenharmony_ci/* return pointer to hash table entry */ 85f08c3bdfSopenharmony_civoid *get_hash_ptr(void *ptr, ELF(Word) idx) 86f08c3bdfSopenharmony_ci{ 87f08c3bdfSopenharmony_ci if (vdso_info.hash_ent_is_dword) 88f08c3bdfSopenharmony_ci return &((ELF(Xword) *) ptr)[idx]; 89f08c3bdfSopenharmony_ci 90f08c3bdfSopenharmony_ci return &((ELF(Word) *) ptr)[idx]; 91f08c3bdfSopenharmony_ci} 92f08c3bdfSopenharmony_ci 93f08c3bdfSopenharmony_civoid vdso_init_from_sysinfo_ehdr(uintptr_t base) 94f08c3bdfSopenharmony_ci{ 95f08c3bdfSopenharmony_ci size_t i; 96f08c3bdfSopenharmony_ci bool found_vaddr = false; 97f08c3bdfSopenharmony_ci 98f08c3bdfSopenharmony_ci vdso_info.valid = false; 99f08c3bdfSopenharmony_ci 100f08c3bdfSopenharmony_ci vdso_info.load_addr = base; 101f08c3bdfSopenharmony_ci 102f08c3bdfSopenharmony_ci ELF(Ehdr) *hdr = (ELF(Ehdr)*)base; 103f08c3bdfSopenharmony_ci if (hdr->e_ident[EI_CLASS] != 104f08c3bdfSopenharmony_ci (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) { 105f08c3bdfSopenharmony_ci return; /* Wrong ELF class -- check ELF_BITS */ 106f08c3bdfSopenharmony_ci } 107f08c3bdfSopenharmony_ci 108f08c3bdfSopenharmony_ci /* 64bit s390 and alpha have hash entry size of 8 bytes */ 109f08c3bdfSopenharmony_ci if ((hdr->e_machine == EM_ALPHA 110f08c3bdfSopenharmony_ci || hdr->e_machine == EM_S390) 111f08c3bdfSopenharmony_ci && hdr->e_ident[EI_CLASS] == ELFCLASS64) 112f08c3bdfSopenharmony_ci vdso_info.hash_ent_is_dword = true; 113f08c3bdfSopenharmony_ci else 114f08c3bdfSopenharmony_ci vdso_info.hash_ent_is_dword = false; 115f08c3bdfSopenharmony_ci 116f08c3bdfSopenharmony_ci ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff); 117f08c3bdfSopenharmony_ci ELF(Dyn) *dyn = 0; 118f08c3bdfSopenharmony_ci 119f08c3bdfSopenharmony_ci /* 120f08c3bdfSopenharmony_ci * We need two things from the segment table: the load offset 121f08c3bdfSopenharmony_ci * and the dynamic table. 122f08c3bdfSopenharmony_ci */ 123f08c3bdfSopenharmony_ci for (i = 0; i < hdr->e_phnum; i++) 124f08c3bdfSopenharmony_ci { 125f08c3bdfSopenharmony_ci if (pt[i].p_type == PT_LOAD && !found_vaddr) { 126f08c3bdfSopenharmony_ci found_vaddr = true; 127f08c3bdfSopenharmony_ci vdso_info.load_offset = base 128f08c3bdfSopenharmony_ci + (uintptr_t)pt[i].p_offset 129f08c3bdfSopenharmony_ci - (uintptr_t)pt[i].p_vaddr; 130f08c3bdfSopenharmony_ci } else if (pt[i].p_type == PT_DYNAMIC) { 131f08c3bdfSopenharmony_ci dyn = (ELF(Dyn)*)(base + pt[i].p_offset); 132f08c3bdfSopenharmony_ci } 133f08c3bdfSopenharmony_ci } 134f08c3bdfSopenharmony_ci 135f08c3bdfSopenharmony_ci if (!found_vaddr || !dyn) 136f08c3bdfSopenharmony_ci return; /* Failed */ 137f08c3bdfSopenharmony_ci 138f08c3bdfSopenharmony_ci /* 139f08c3bdfSopenharmony_ci * Fish out the useful bits of the dynamic table. 140f08c3bdfSopenharmony_ci */ 141f08c3bdfSopenharmony_ci ELF(Word) *hash = 0; 142f08c3bdfSopenharmony_ci vdso_info.symstrings = 0; 143f08c3bdfSopenharmony_ci vdso_info.symtab = 0; 144f08c3bdfSopenharmony_ci vdso_info.versym = 0; 145f08c3bdfSopenharmony_ci vdso_info.verdef = 0; 146f08c3bdfSopenharmony_ci for (i = 0; dyn[i].d_tag != DT_NULL; i++) { 147f08c3bdfSopenharmony_ci switch (dyn[i].d_tag) { 148f08c3bdfSopenharmony_ci case DT_STRTAB: 149f08c3bdfSopenharmony_ci vdso_info.symstrings = (const char *) 150f08c3bdfSopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 151f08c3bdfSopenharmony_ci + vdso_info.load_offset); 152f08c3bdfSopenharmony_ci break; 153f08c3bdfSopenharmony_ci case DT_SYMTAB: 154f08c3bdfSopenharmony_ci vdso_info.symtab = (ELF(Sym) *) 155f08c3bdfSopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 156f08c3bdfSopenharmony_ci + vdso_info.load_offset); 157f08c3bdfSopenharmony_ci break; 158f08c3bdfSopenharmony_ci case DT_HASH: 159f08c3bdfSopenharmony_ci hash = (ELF(Word) *) 160f08c3bdfSopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 161f08c3bdfSopenharmony_ci + vdso_info.load_offset); 162f08c3bdfSopenharmony_ci break; 163f08c3bdfSopenharmony_ci case DT_VERSYM: 164f08c3bdfSopenharmony_ci vdso_info.versym = (ELF(Versym) *) 165f08c3bdfSopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 166f08c3bdfSopenharmony_ci + vdso_info.load_offset); 167f08c3bdfSopenharmony_ci break; 168f08c3bdfSopenharmony_ci case DT_VERDEF: 169f08c3bdfSopenharmony_ci vdso_info.verdef = (ELF(Verdef) *) 170f08c3bdfSopenharmony_ci ((uintptr_t)dyn[i].d_un.d_ptr 171f08c3bdfSopenharmony_ci + vdso_info.load_offset); 172f08c3bdfSopenharmony_ci break; 173f08c3bdfSopenharmony_ci } 174f08c3bdfSopenharmony_ci } 175f08c3bdfSopenharmony_ci if (!vdso_info.symstrings || !vdso_info.symtab || !hash) 176f08c3bdfSopenharmony_ci return; /* Failed */ 177f08c3bdfSopenharmony_ci 178f08c3bdfSopenharmony_ci if (!vdso_info.verdef) 179f08c3bdfSopenharmony_ci vdso_info.versym = 0; 180f08c3bdfSopenharmony_ci 181f08c3bdfSopenharmony_ci 182f08c3bdfSopenharmony_ci vdso_info.nbucket = get_hash_val(hash, 0); 183f08c3bdfSopenharmony_ci vdso_info.nchain = get_hash_val(hash, 1); 184f08c3bdfSopenharmony_ci vdso_info.bucket = get_hash_ptr(hash, 2); 185f08c3bdfSopenharmony_ci vdso_info.chain = get_hash_ptr(hash, vdso_info.nbucket + 2); 186f08c3bdfSopenharmony_ci 187f08c3bdfSopenharmony_ci /* That's all we need. */ 188f08c3bdfSopenharmony_ci vdso_info.valid = true; 189f08c3bdfSopenharmony_ci} 190f08c3bdfSopenharmony_ci 191f08c3bdfSopenharmony_cistatic bool vdso_match_version(ELF(Versym) ver, 192f08c3bdfSopenharmony_ci const char *name, ELF(Word) hash) 193f08c3bdfSopenharmony_ci{ 194f08c3bdfSopenharmony_ci /* 195f08c3bdfSopenharmony_ci * This is a helper function to check if the version indexed by 196f08c3bdfSopenharmony_ci * ver matches name (which hashes to hash). 197f08c3bdfSopenharmony_ci * 198f08c3bdfSopenharmony_ci * The version definition table is a mess, and I don't know how 199f08c3bdfSopenharmony_ci * to do this in better than linear time without allocating memory 200f08c3bdfSopenharmony_ci * to build an index. I also don't know why the table has 201f08c3bdfSopenharmony_ci * variable size entries in the first place. 202f08c3bdfSopenharmony_ci * 203f08c3bdfSopenharmony_ci * For added fun, I can't find a comprehensible specification of how 204f08c3bdfSopenharmony_ci * to parse all the weird flags in the table. 205f08c3bdfSopenharmony_ci * 206f08c3bdfSopenharmony_ci * So I just parse the whole table every time. 207f08c3bdfSopenharmony_ci */ 208f08c3bdfSopenharmony_ci 209f08c3bdfSopenharmony_ci /* First step: find the version definition */ 210f08c3bdfSopenharmony_ci ver &= 0x7fff; /* Apparently bit 15 means "hidden" */ 211f08c3bdfSopenharmony_ci ELF(Verdef) *def = vdso_info.verdef; 212f08c3bdfSopenharmony_ci while(true) { 213f08c3bdfSopenharmony_ci if ((def->vd_flags & VER_FLG_BASE) == 0 214f08c3bdfSopenharmony_ci && (def->vd_ndx & 0x7fff) == ver) 215f08c3bdfSopenharmony_ci break; 216f08c3bdfSopenharmony_ci 217f08c3bdfSopenharmony_ci if (def->vd_next == 0) 218f08c3bdfSopenharmony_ci return false; /* No definition. */ 219f08c3bdfSopenharmony_ci 220f08c3bdfSopenharmony_ci def = (ELF(Verdef) *)((char *)def + def->vd_next); 221f08c3bdfSopenharmony_ci } 222f08c3bdfSopenharmony_ci 223f08c3bdfSopenharmony_ci /* Now figure out whether it matches. */ 224f08c3bdfSopenharmony_ci ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux); 225f08c3bdfSopenharmony_ci return def->vd_hash == hash 226f08c3bdfSopenharmony_ci && !strcmp(name, vdso_info.symstrings + aux->vda_name); 227f08c3bdfSopenharmony_ci} 228f08c3bdfSopenharmony_ci 229f08c3bdfSopenharmony_civoid *vdso_sym(const char *version, const char *name) 230f08c3bdfSopenharmony_ci{ 231f08c3bdfSopenharmony_ci unsigned long ver_hash; 232f08c3bdfSopenharmony_ci if (!vdso_info.valid) 233f08c3bdfSopenharmony_ci return 0; 234f08c3bdfSopenharmony_ci 235f08c3bdfSopenharmony_ci ver_hash = elf_hash((const void*)version); 236f08c3bdfSopenharmony_ci ELF(Word) chain = get_hash_val(vdso_info.bucket, 237f08c3bdfSopenharmony_ci elf_hash((const void*)name) % vdso_info.nbucket); 238f08c3bdfSopenharmony_ci 239f08c3bdfSopenharmony_ci for (; chain != STN_UNDEF; chain = get_hash_val(vdso_info.chain, chain)) { 240f08c3bdfSopenharmony_ci ELF(Sym) *sym = &vdso_info.symtab[chain]; 241f08c3bdfSopenharmony_ci 242f08c3bdfSopenharmony_ci /* Check for a defined global or weak function w/ right name. */ 243f08c3bdfSopenharmony_ci if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) 244f08c3bdfSopenharmony_ci continue; 245f08c3bdfSopenharmony_ci if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && 246f08c3bdfSopenharmony_ci ELF64_ST_BIND(sym->st_info) != STB_WEAK) 247f08c3bdfSopenharmony_ci continue; 248f08c3bdfSopenharmony_ci if (sym->st_shndx == SHN_UNDEF) 249f08c3bdfSopenharmony_ci continue; 250f08c3bdfSopenharmony_ci if (strcmp(name, vdso_info.symstrings + sym->st_name)) 251f08c3bdfSopenharmony_ci continue; 252f08c3bdfSopenharmony_ci 253f08c3bdfSopenharmony_ci /* Check symbol version. */ 254f08c3bdfSopenharmony_ci if (vdso_info.versym 255f08c3bdfSopenharmony_ci && !vdso_match_version(vdso_info.versym[chain], 256f08c3bdfSopenharmony_ci version, ver_hash)) 257f08c3bdfSopenharmony_ci continue; 258f08c3bdfSopenharmony_ci 259f08c3bdfSopenharmony_ci return (void *)(vdso_info.load_offset + sym->st_value); 260f08c3bdfSopenharmony_ci } 261f08c3bdfSopenharmony_ci 262f08c3bdfSopenharmony_ci return 0; 263f08c3bdfSopenharmony_ci} 264f08c3bdfSopenharmony_ci 265f08c3bdfSopenharmony_civoid vdso_init_from_auxv(void *auxv) 266f08c3bdfSopenharmony_ci{ 267f08c3bdfSopenharmony_ci int i; 268f08c3bdfSopenharmony_ci 269f08c3bdfSopenharmony_ci ELF(auxv_t) *elf_auxv = auxv; 270f08c3bdfSopenharmony_ci for (i = 0; elf_auxv[i].a_type != AT_NULL; i++) { 271f08c3bdfSopenharmony_ci if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) { 272f08c3bdfSopenharmony_ci vdso_init_from_sysinfo_ehdr(elf_auxv[i].a_un.a_val); 273f08c3bdfSopenharmony_ci return; 274f08c3bdfSopenharmony_ci } 275f08c3bdfSopenharmony_ci } 276f08c3bdfSopenharmony_ci 277f08c3bdfSopenharmony_ci vdso_info.valid = false; 278f08c3bdfSopenharmony_ci} 279