brintos

brintos / linux-shallow public Read only

0
0
Text · 6.3 KiB · 7dd5668 Raw
261 lines · c
1/*2 * parse_vdso.c: Linux reference vDSO parser3 * 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 file7 * is licensed under the Creative Commons Zero License, version 1.0,8 * available at http://creativecommons.org/publicdomain/zero/1.0/legalcode9 *10 * The vDSO is a regular ELF DSO that the kernel maps into user space when11 * it starts a program.  It works equally well in statically and dynamically12 * linked binaries.13 *14 * This code is tested on x86.  In principle it should work on any15 * 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_BITS28# if ULONG_MAX > 0xffffffffUL29#  define ELF_BITS 6430# else31#  define ELF_BITS 3232# endif33#endif34 35#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x36#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)37#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x)38 39#ifdef __s390x__40#define ELF_HASH_ENTRY ELF(Xword)41#else42#define ELF_HASH_ENTRY ELF(Word)43#endif44 45static struct vdso_info46{47	bool valid;48 49	/* Load information */50	uintptr_t load_addr;51	uintptr_t load_offset;  /* load_addr - recorded vaddr */52 53	/* Symbol table */54	ELF(Sym) *symtab;55	const char *symstrings;56	ELF_HASH_ENTRY *bucket, *chain;57	ELF_HASH_ENTRY nbucket, nchain;58 59	/* Version table */60	ELF(Versym) *versym;61	ELF(Verdef) *verdef;62} vdso_info;63 64/*65 * Straight from the ELF specification...and then tweaked slightly, in order to66 * avoid a few clang warnings.67 */68static unsigned long elf_hash(const char *name)69{70	unsigned long h = 0, g;71	const unsigned char *uch_name = (const unsigned char *)name;72 73	while (*uch_name)74	{75		h = (h << 4) + *uch_name++;76		g = h & 0xf0000000;77		if (g)78			h ^= g >> 24;79		h &= ~g;80	}81	return h;82}83 84void vdso_init_from_sysinfo_ehdr(uintptr_t base)85{86	size_t i;87	bool found_vaddr = false;88 89	vdso_info.valid = false;90 91	vdso_info.load_addr = base;92 93	ELF(Ehdr) *hdr = (ELF(Ehdr)*)base;94	if (hdr->e_ident[EI_CLASS] !=95	    (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) {96		return;  /* Wrong ELF class -- check ELF_BITS */97	}98 99	ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff);100	ELF(Dyn) *dyn = 0;101 102	/*103	 * We need two things from the segment table: the load offset104	 * and the dynamic table.105	 */106	for (i = 0; i < hdr->e_phnum; i++)107	{108		if (pt[i].p_type == PT_LOAD && !found_vaddr) {109			found_vaddr = true;110			vdso_info.load_offset =	base111				+ (uintptr_t)pt[i].p_offset112				- (uintptr_t)pt[i].p_vaddr;113		} else if (pt[i].p_type == PT_DYNAMIC) {114			dyn = (ELF(Dyn)*)(base + pt[i].p_offset);115		}116	}117 118	if (!found_vaddr || !dyn)119		return;  /* Failed */120 121	/*122	 * Fish out the useful bits of the dynamic table.123	 */124	ELF_HASH_ENTRY *hash = 0;125	vdso_info.symstrings = 0;126	vdso_info.symtab = 0;127	vdso_info.versym = 0;128	vdso_info.verdef = 0;129	for (i = 0; dyn[i].d_tag != DT_NULL; i++) {130		switch (dyn[i].d_tag) {131		case DT_STRTAB:132			vdso_info.symstrings = (const char *)133				((uintptr_t)dyn[i].d_un.d_ptr134				 + vdso_info.load_offset);135			break;136		case DT_SYMTAB:137			vdso_info.symtab = (ELF(Sym) *)138				((uintptr_t)dyn[i].d_un.d_ptr139				 + vdso_info.load_offset);140			break;141		case DT_HASH:142			hash = (ELF_HASH_ENTRY *)143				((uintptr_t)dyn[i].d_un.d_ptr144				 + vdso_info.load_offset);145			break;146		case DT_VERSYM:147			vdso_info.versym = (ELF(Versym) *)148				((uintptr_t)dyn[i].d_un.d_ptr149				 + vdso_info.load_offset);150			break;151		case DT_VERDEF:152			vdso_info.verdef = (ELF(Verdef) *)153				((uintptr_t)dyn[i].d_un.d_ptr154				 + vdso_info.load_offset);155			break;156		}157	}158	if (!vdso_info.symstrings || !vdso_info.symtab || !hash)159		return;  /* Failed */160 161	if (!vdso_info.verdef)162		vdso_info.versym = 0;163 164	/* Parse the hash table header. */165	vdso_info.nbucket = hash[0];166	vdso_info.nchain = hash[1];167	vdso_info.bucket = &hash[2];168	vdso_info.chain = &hash[vdso_info.nbucket + 2];169 170	/* That's all we need. */171	vdso_info.valid = true;172}173 174static bool vdso_match_version(ELF(Versym) ver,175			       const char *name, ELF(Word) hash)176{177	/*178	 * This is a helper function to check if the version indexed by179	 * ver matches name (which hashes to hash).180	 *181	 * The version definition table is a mess, and I don't know how182	 * to do this in better than linear time without allocating memory183	 * to build an index.  I also don't know why the table has184	 * variable size entries in the first place.185	 *186	 * For added fun, I can't find a comprehensible specification of how187	 * to parse all the weird flags in the table.188	 *189	 * So I just parse the whole table every time.190	 */191 192	/* First step: find the version definition */193	ver &= 0x7fff;  /* Apparently bit 15 means "hidden" */194	ELF(Verdef) *def = vdso_info.verdef;195	while(true) {196		if ((def->vd_flags & VER_FLG_BASE) == 0197		    && (def->vd_ndx & 0x7fff) == ver)198			break;199 200		if (def->vd_next == 0)201			return false;  /* No definition. */202 203		def = (ELF(Verdef) *)((char *)def + def->vd_next);204	}205 206	/* Now figure out whether it matches. */207	ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux);208	return def->vd_hash == hash209		&& !strcmp(name, vdso_info.symstrings + aux->vda_name);210}211 212void *vdso_sym(const char *version, const char *name)213{214	unsigned long ver_hash;215	if (!vdso_info.valid)216		return 0;217 218	ver_hash = elf_hash(version);219	ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];220 221	for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) {222		ELF(Sym) *sym = &vdso_info.symtab[chain];223 224		/* Check for a defined global or weak function w/ right name. */225		if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC &&226		    ELF64_ST_TYPE(sym->st_info) != STT_NOTYPE)227			continue;228		if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&229		    ELF64_ST_BIND(sym->st_info) != STB_WEAK)230			continue;231		if (sym->st_shndx == SHN_UNDEF)232			continue;233		if (strcmp(name, vdso_info.symstrings + sym->st_name))234			continue;235 236		/* Check symbol version. */237		if (vdso_info.versym238		    && !vdso_match_version(vdso_info.versym[chain],239					   version, ver_hash))240			continue;241 242		return (void *)(vdso_info.load_offset + sym->st_value);243	}244 245	return 0;246}247 248void vdso_init_from_auxv(void *auxv)249{250	ELF(auxv_t) *elf_auxv = auxv;251	for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++)252	{253		if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) {254			vdso_init_from_sysinfo_ehdr(elf_auxv[i].a_un.a_val);255			return;256		}257	}258 259	vdso_info.valid = false;260}261