Commit c09b7c2f authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by Pavel Emelyanov

vdso: x86 -- Use dynamic symbols for parsing

New vDSO are in stripped format so use dynamic
symbols instead of sectioned ones.
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 3ca8b12e
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <elf.h> #include <elf.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -56,17 +57,6 @@ int vdso_redirect_calls(void *base_to, void *base_from, ...@@ -56,17 +57,6 @@ int vdso_redirect_calls(void *base_to, void *base_from,
return 0; 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 */ /* Check if pointer is out-of-bound */
static bool __ptr_oob(void *ptr, void *start, size_t size) static bool __ptr_oob(void *ptr, void *start, size_t size)
...@@ -75,20 +65,38 @@ static bool __ptr_oob(void *ptr, void *start, size_t size) ...@@ -75,20 +65,38 @@ static bool __ptr_oob(void *ptr, void *start, size_t size)
return ptr > end || ptr < start; return ptr > end || ptr < start;
} }
/*
* Elf hash, see format specification.
*/
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) 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_Ehdr *ehdr = (void *)mem;
Elf64_Shdr *shdr, *shdr_strtab; Elf64_Dyn *dyn_strtab = NULL;
Elf64_Shdr *shdr_dynsym; Elf64_Dyn *dyn_symtab = NULL;
Elf64_Shdr *shdr_dynstr; Elf64_Dyn *dyn_strsz = NULL;
Elf64_Dyn *dyn_syment = NULL;
Elf64_Dyn *dyn_hash = NULL;
Elf64_Word *hash = NULL;
Elf64_Phdr *phdr; Elf64_Phdr *phdr;
Elf64_Shdr *text; Elf64_Dyn *d;
Elf64_Sym *sym;
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 for this magic values. * See Elf specification for this magic values.
...@@ -105,115 +113,136 @@ int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t) ...@@ -105,115 +113,136 @@ int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t)
[VDSO_SYMBOL_TIME] = VDSO_SYMBOL_TIME_NAME, [VDSO_SYMBOL_TIME] = VDSO_SYMBOL_TIME_NAME,
}; };
char *dynsymbol_names;
unsigned int i, j, k;
BUILD_BUG_ON(sizeof(elf_ident) != sizeof(ehdr->e_ident)); BUILD_BUG_ON(sizeof(elf_ident) != sizeof(ehdr->e_ident));
pr_debug("Parsing at %lx %lx\n", pr_debug("Parsing at %lx %lx\n", (long)mem, (long)mem + (long)size);
(long)mem, (long)mem + (long)size);
/* /*
* Make sure it's a file we support. * Make sure it's a file we support.
*/ */
if (builtin_memcmp(ehdr->e_ident, elf_ident, sizeof(elf_ident))) { if (builtin_memcmp(ehdr->e_ident, elf_ident, sizeof(elf_ident))) {
pr_debug("Elf header magic mismatch\n"); pr_err("Elf header magic mismatch\n");
goto err; return -EINVAL;
} }
/* /*
* Figure out base virtual address. * We need PT_LOAD and PT_DYNAMIC here. Each once.
*/ */
phdr = (void *)&mem[ehdr->e_phoff]; phdr = (void *)&mem[ehdr->e_phoff];
for (i = 0; i < ehdr->e_phnum; i++, phdr++) { for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
if (__ptr_oob(phdr, mem, size)) if (__ptr_oob(phdr, mem, size))
goto err; goto err_oob;
if (phdr->p_type == PT_LOAD) { switch (phdr->p_type) {
base = phdr->p_vaddr; 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; break;
} }
} }
if (base != VDSO_BAD_ADDR) {
pr_debug("Base address %lx\n", base); if (!load || !dynamic) {
} else { pr_err("One of obligated program headers is missed\n");
pr_debug("No base address found\n"); return -EINVAL;
goto err;
} }
pr_debug("PT_LOAD p_vaddr: %lx\n", (unsigned long)load->p_vaddr);
/* /*
* Where the section names lays. * Dynamic section tags should provide us the rest of information
* needed. Note that we're interested in a small set of tags.
*/ */
if (ehdr->e_shstrndx == SHN_UNDEF) { d = (void *)&mem[dynamic->p_offset];
pr_err("Section names are not found\n"); for (i = 0; i < dynamic->p_filesz / sizeof(*d); i++, d++) {
goto err; if (__ptr_oob(d, mem, size))
} goto err_oob;
shdr = (void *)&mem[ehdr->e_shoff]; if (d->d_tag == DT_NULL) {
shdr_strtab = &shdr[ehdr->e_shstrndx]; break;
if (__ptr_oob(shdr_strtab, mem, size)) } else if (d->d_tag == DT_STRTAB) {
goto err; dyn_strtab = d;
pr_debug("DT_STRTAB: %p\n", (void *)d->d_un.d_ptr);
section_names = (void *)&mem[shdr_strtab->sh_offset]; } else if (d->d_tag == DT_SYMTAB) {
shdr_dynsym = shdr_dynstr = text = NULL; dyn_symtab = d;
pr_debug("DT_SYMTAB: %p\n", (void *)d->d_un.d_ptr);
for (i = 0; i < ehdr->e_shnum; i++, shdr++) { } else if (d->d_tag == DT_STRSZ) {
if (__ptr_oob(shdr, mem, size)) dyn_strsz = d;
goto err; pr_debug("DT_STRSZ: %lu\n", (unsigned long)d->d_un.d_val);
if (__ptr_oob(&section_names[shdr->sh_name], mem, size)) } else if (d->d_tag == DT_SYMENT) {
goto err; dyn_syment = d;
pr_debug("DT_SYMENT: %lu\n", (unsigned long)d->d_un.d_val);
if (shdr->sh_type == SHT_DYNSYM && } else if (d->d_tag == DT_HASH) {
builtin_strcmp(&section_names[shdr->sh_name], dyn_hash = d;
".dynsym") == 0) { pr_debug("DT_HASH: %p\n", (void *)d->d_un.d_ptr);
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 (!shdr_dynsym || !shdr_dynstr || !text) { if (!dyn_strtab || !dyn_symtab || !dyn_strsz || !dyn_syment || !dyn_hash) {
pr_debug("No required sections found\n"); pr_err("Not all dynamic entries are present\n");
goto err; return -EINVAL;
} }
dynsymbol_names = (void *)&mem[shdr_dynstr->sh_offset]; dynsymbol_names = &mem[dyn_strtab->d_un.d_val - load->p_vaddr];
if (__ptr_oob(dynsymbol_names, mem, size) || if (__ptr_oob(dynsymbol_names, mem, size))
__ptr_oob(shdr_dynsym, mem, size) || goto err_oob;
__ptr_oob(text, mem, size))
goto err;
/* hash = (void *)&mem[(unsigned long)dyn_hash->d_un.d_ptr - (unsigned long)load->p_vaddr];
* Walk over global symbols and choose ones we need. if (__ptr_oob(hash, mem, size))
*/ goto err_oob;
j = shdr_dynsym->sh_size / sizeof(*sym);
sym = (void *)&mem[shdr_dynsym->sh_offset];
for (i = 0; i < j; i++, sym++) { nbucket = hash[0];
if (__ptr_oob(sym, mem, size)) nchain = hash[1];
goto err; bucket = &hash[2];
chain = &hash[nbucket + 2];
if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL || pr_debug("nbucket %lu nchain %lu bucket %p chain %p\n",
ELF64_ST_TYPE(sym->st_info) != STT_FUNC) (long)nbucket, (long)nchain, bucket, chain);
continue;
if (__ptr_oob(&dynsymbol_names[sym->st_name], mem, size)) for (i = 0; i < ARRAY_SIZE(vdso_symbols); i++) {
goto err; k = elf_hash((const unsigned char *)vdso_symbols[i]);
k = get_symbol_index(&dynsymbol_names[sym->st_name], for (j = bucket[k % nbucket]; j < nchain && chain[j] != STN_UNDEF; j = chain[j]) {
(char **)vdso_symbols, Elf64_Sym *sym = (void *)&mem[dyn_symtab->d_un.d_ptr - load->p_vaddr];
ARRAY_SIZE(vdso_symbols)); char *name;
if (k != VDSO_SYMBOL_MAX) {
builtin_memcpy(t->symbols[k].name, vdso_symbols[k], sym = &sym[j];
sizeof(t->symbols[k].name)); if (__ptr_oob(sym, mem, size))
t->symbols[k].offset = (unsigned long)sym->st_value - base; continue;
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, vdso_symbols[i]))
continue;
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; 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) int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment