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