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