[CRIU] [PATCH 2/2] vdso: x86 -- Use dynamic symbols for parsing

Cyrill Gorcunov gorcunov at openvz.org
Mon May 26 04:02:57 PDT 2014


New vDSO are in stripped format so use dynamic
symbols instead of sectioned ones.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 arch/x86/include/asm/vdso.h |  14 ---
 arch/x86/vdso-pie.c         | 242 ++++++++++++++++++++++++++------------------
 2 files changed, 145 insertions(+), 111 deletions(-)

diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index d91215c0ed71..5facf6002da2 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -106,20 +106,6 @@ static inline void vdso_put_mark(void *where, unsigned long proxy_addr)
 #define VDSO_SYMBOL_TIME_NAME		"__vdso_time"
 
 
-#define DECLARE_VDSO(ident_name, symtab_name)					\
-										\
-char ident_name[] = {								\
-	0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,				\
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,				\
-};										\
-										\
-char *symtab_name[VDSO_SYMBOL_MAX] = {						\
-	[VDSO_SYMBOL_CLOCK_GETTIME]	= VDSO_SYMBOL_CLOCK_GETTIME_NAME,	\
-	[VDSO_SYMBOL_GETCPU]		= VDSO_SYMBOL_GETCPU_NAME,		\
-	[VDSO_SYMBOL_GETTIMEOFDAY]	= VDSO_SYMBOL_GETTIMEOFDAY_NAME,	\
-	[VDSO_SYMBOL_TIME]		= VDSO_SYMBOL_TIME_NAME,		\
-};
-
 extern struct vdso_symtable vdso_sym_rt;
 extern u64 vdso_pfn;
 
diff --git a/arch/x86/vdso-pie.c b/arch/x86/vdso-pie.c
index d04791916727..209e0bdeea21 100644
--- a/arch/x86/vdso-pie.c
+++ b/arch/x86/vdso-pie.c
@@ -4,6 +4,7 @@
 #include <string.h>
 #include <elf.h>
 #include <fcntl.h>
+#include <errno.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -56,18 +57,6 @@ int vdso_redirect_calls(void *base_to, void *base_from,
 	return 0;
 }
 
-static unsigned int get_symbol_index(char *symbol, char *symbols[], size_t size)
-{
-	unsigned int i;
-
-	for (i = 0; symbol && i < size; i++) {
-		if (!builtin_strcmp(symbol, symbols[i]))
-			return i;
-	}
-
-	return VDSO_SYMBOL_MAX;
-}
-
 /* Check if pointer is out-of-bound */
 static bool __ptr_oob(void *ptr, void *start, size_t size)
 {
@@ -75,132 +64,191 @@ static bool __ptr_oob(void *ptr, void *start, size_t size)
 	return ptr > end || ptr < start;
 }
 
+static unsigned long elf_hash(const unsigned char *name)
+{
+	unsigned long h = 0, g;
+
+	while (*name) {
+		h = (h << 4) + *name++;
+		g = h & 0xf0000000ul;
+		if (g)
+			h ^= g >> 24;
+		h &= ~g;
+	}
+	return h;
+}
+
 int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t)
 {
+	Elf64_Phdr *dynamic = NULL, *load = NULL;
 	Elf64_Ehdr *ehdr = (void *)mem;
-	Elf64_Shdr *shdr, *shdr_strtab;
-	Elf64_Shdr *shdr_dynsym;
-	Elf64_Shdr *shdr_dynstr;
+	Elf64_Dyn *dyn_strtab = NULL;
+	Elf64_Dyn *dyn_symtab = NULL;
+	Elf64_Dyn *dyn_strsz = NULL;
+	Elf64_Dyn *dyn_syment = NULL;
+	Elf64_Dyn *dyn_hash = NULL;
+	Elf64_Word *hash = NULL;
 	Elf64_Phdr *phdr;
-	Elf64_Shdr *text;
-	Elf64_Sym *sym;
+	Elf64_Dyn *d;
 
-	char *section_names, *dynsymbol_names;
+	Elf64_Word *bucket, *chain;
+	Elf64_Word nbucket, nchain;
 
-	unsigned long base = VDSO_BAD_ADDR;
-	unsigned int i, j, k;
+	/*
+	 * See Elf specification.
+	 */
+	const char elf_ident[] = {
+		0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	};
 
-	DECLARE_VDSO(vdso_ident, vdso_symbols);
+	const char *symtab_name[VDSO_SYMBOL_MAX] = {
+		[VDSO_SYMBOL_CLOCK_GETTIME]	= VDSO_SYMBOL_CLOCK_GETTIME_NAME,
+		[VDSO_SYMBOL_GETCPU]		= VDSO_SYMBOL_GETCPU_NAME,
+		[VDSO_SYMBOL_GETTIMEOFDAY]	= VDSO_SYMBOL_GETTIMEOFDAY_NAME,
+		[VDSO_SYMBOL_TIME]		= VDSO_SYMBOL_TIME_NAME,
+	};
+
+	char *dynsymbol_names;
+	unsigned int i, j, k;
 
-	BUILD_BUG_ON(sizeof(vdso_ident) != sizeof(ehdr->e_ident));
+	BUILD_BUG_ON(sizeof(elf_ident) != sizeof(ehdr->e_ident));
 
-	pr_debug("Parsing at %lx %lx\n",
-		 (long)mem, (long)mem + (long)size);
+	pr_debug("Parsing at %lx %lx\n", (long)mem, (long)mem + (long)size);
 
 	/*
 	 * Make sure it's a file we support.
 	 */
-	if (builtin_memcmp(ehdr->e_ident, vdso_ident, sizeof(vdso_ident))) {
-		pr_debug("Elf header magic mismatch\n");
-		goto err;
+	if (builtin_memcmp(ehdr->e_ident, elf_ident, sizeof(elf_ident))) {
+		pr_err("Elf header magic mismatch\n");
+		return -EINVAL;
 	}
 
 	/*
-	 * Figure out base virtual address.
+	 * We need PT_LOAD and PT_DYNAMIC here. Each once.
 	 */
 	phdr = (void *)&mem[ehdr->e_phoff];
 	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
 		if (__ptr_oob(phdr, mem, size))
-			goto err;
-		if (phdr->p_type == PT_LOAD) {
-			base = phdr->p_vaddr;
+			goto err_oob;
+		switch (phdr->p_type) {
+		case PT_DYNAMIC:
+			if (dynamic) {
+				pr_err("Second PT_DYNAMIC header\n");
+				return -EINVAL;
+			}
+			dynamic = phdr;
+			break;
+		case PT_LOAD:
+			if (load) {
+				pr_err("Second PT_LOAD header\n");
+				return -EINVAL;
+			}
+			load = phdr;
 			break;
 		}
 	}
-	if (base != VDSO_BAD_ADDR) {
-		pr_debug("Base address %lx\n", base);
-	} else {
-		pr_debug("No base address found\n");
-		goto err;
+
+	if (!load || !dynamic) {
+		pr_err("One of obligated program headers is missed\n");
+		return -EINVAL;
 	}
 
+	pr_debug("PT_LOAD p_vaddr: %lx\n", (unsigned long)load->p_vaddr);
+
 	/*
-	 * Where the section names lays.
+	 * Dynamic section should provide us the rest of
+	 * information needed.
 	 */
-	if (ehdr->e_shstrndx == SHN_UNDEF) {
-		pr_err("Section names are not found\n");
-		goto err;
-	}
+	d = (void *)&mem[dynamic->p_offset];
+	for (i = 0; i < dynamic->p_filesz / sizeof(*d); i++, d++) {
+		if (__ptr_oob(d, mem, size))
+			goto err_oob;
 
-	shdr = (void *)&mem[ehdr->e_shoff];
-	shdr_strtab = &shdr[ehdr->e_shstrndx];
-	if (__ptr_oob(shdr_strtab, mem, size))
-		goto err;
-
-	section_names = (void *)&mem[shdr_strtab->sh_offset];
-	shdr_dynsym = shdr_dynstr = text = NULL;
-
-	for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
-		if (__ptr_oob(shdr, mem, size))
-			goto err;
-		if (__ptr_oob(&section_names[shdr->sh_name], mem, size))
-			goto err;
-
-		if (shdr->sh_type == SHT_DYNSYM &&
-		    builtin_strcmp(&section_names[shdr->sh_name],
-				   ".dynsym") == 0) {
-			shdr_dynsym = shdr;
-		} else if (shdr->sh_type == SHT_STRTAB &&
-			   builtin_strcmp(&section_names[shdr->sh_name],
-					  ".dynstr") == 0) {
-			shdr_dynstr = shdr;
-		} else if (shdr->sh_type == SHT_PROGBITS &&
-			   builtin_strcmp(&section_names[shdr->sh_name],
-					  ".text") == 0) {
-			text = shdr;
+		if (d->d_tag == DT_NULL) {
+			break;
+		}else if (d->d_tag == DT_STRTAB) {
+			dyn_strtab = d;
+			pr_debug("DT_STRTAB: %p\n", (void *)d->d_un.d_ptr);
+		} else if (d->d_tag == DT_SYMTAB) {
+			dyn_symtab = d;
+			pr_debug("DT_SYMTAB: %p\n", (void *)d->d_un.d_ptr);
+		} else if (d->d_tag == DT_STRSZ) {
+			dyn_strsz = d;
+			pr_debug("DT_STRSZ: %lu\n", (unsigned long)d->d_un.d_val);
+		} else if (d->d_tag == DT_SYMENT) {
+			dyn_syment = d;
+			pr_debug("DT_SYMENT: %lu\n", (unsigned long)d->d_un.d_val);
+		} else if (d->d_tag == DT_HASH) {
+			dyn_hash = d;
+			pr_debug("DT_HASH: %p\n", (void *)d->d_un.d_ptr);
 		}
 	}
 
-	if (!shdr_dynsym || !shdr_dynstr || !text) {
-		pr_debug("No required sections found\n");
-		goto err;
+	if (!dyn_strtab || !dyn_symtab || !dyn_strsz || !dyn_syment || !dyn_hash) {
+		pr_err("Not all dynamic entries are present\n");
+		return -EINVAL;
 	}
 
-	dynsymbol_names = (void *)&mem[shdr_dynstr->sh_offset];
-	if (__ptr_oob(dynsymbol_names, mem, size)	||
-	    __ptr_oob(shdr_dynsym, mem, size)		||
-	    __ptr_oob(text, mem, size))
-		goto err;
+	dynsymbol_names = &mem[dyn_strtab->d_un.d_val - load->p_vaddr];
+	if (__ptr_oob(dynsymbol_names, mem, size))
+		goto err_oob;
 
-	/*
-	 * Walk over global symbols and choose ones we need.
-	 */
-	j = shdr_dynsym->sh_size / sizeof(*sym);
-	sym = (void *)&mem[shdr_dynsym->sh_offset];
+	hash = (void *)&mem[(unsigned long)dyn_hash->d_un.d_ptr - (unsigned long)load->p_vaddr];
+	if (__ptr_oob(hash, mem, size))
+		goto err_oob;
 
-	for (i = 0; i < j; i++, sym++) {
-		if (__ptr_oob(sym, mem, size))
-			goto err;
+	nbucket = hash[0];
+	nchain = hash[1];
+	bucket = &hash[2];
+	chain = &hash[nbucket + 2];
 
-		if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL ||
-		    ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
-			continue;
+	pr_debug("nbucket %lu nchain %lu bucket %p chain %p\n",
+		 (long)nbucket, (long)nchain, bucket, chain);
+
+	for (i = 0; i < ARRAY_SIZE(symtab_name); i++) {
+		k = elf_hash((const unsigned char *)symtab_name[i]);
 
-		if (__ptr_oob(&dynsymbol_names[sym->st_name], mem, size))
-			goto err;
+		for (j = bucket[k % nbucket]; j < nchain && chain[j] != STN_UNDEF; j = chain[j]) {
+			Elf64_Sym *sym = (void *)&mem[dyn_symtab->d_un.d_ptr - load->p_vaddr];
+			char *name;
 
-		k = get_symbol_index(&dynsymbol_names[sym->st_name],
-				     vdso_symbols,
-				     ARRAY_SIZE(vdso_symbols));
-		if (k != VDSO_SYMBOL_MAX) {
-			builtin_memcpy(t->symbols[k].name, vdso_symbols[k],
-				       sizeof(t->symbols[k].name));
-			t->symbols[k].offset = (unsigned long)sym->st_value - base;
+			sym = &sym[j];
+			if (__ptr_oob(sym, mem, size))
+				continue;
+
+#if 0
+			pr_debug("type %lu bind %lu st_name %lu st_value %lx\n",
+				 (unsigned long)ELF64_ST_TYPE(sym->st_info),
+				 (unsigned long)ELF64_ST_BIND(sym->st_info),
+				 (unsigned long)sym->st_name,
+				 (unsigned long)sym->st_value);
+#endif
+
+			if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC &&
+			    ELF64_ST_BIND(sym->st_info) != STB_GLOBAL)
+				continue;
+
+			name = &dynsymbol_names[sym->st_name];
+			if (__ptr_oob(name, mem, size))
+				continue;
+
+			if (builtin_strcmp(name, symtab_name[i]))
+				continue;
+#if 0
+			pr_debug("\tSymbol %s at %lx\n", name, (unsigned long)sym->st_value - load->p_vaddr);
+#endif
+			builtin_memcpy(t->symbols[i].name, name, sizeof(t->symbols[i].name));
+			t->symbols[i].offset = (unsigned long)sym->st_value - load->p_vaddr;
+			break;
 		}
 	}
+
 	return 0;
-err:
-	return -1;
+
+err_oob:
+	pr_err("Corrupted Elf data\n");
+	return -EFAULT;
 }
 
 int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size)
-- 
1.9.3



More information about the CRIU mailing list