mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-22 09:58:09 +00:00
vdso: Fixes in DT_GNU_HASH handling
* Hash buckets is an array of 32-bit words. While DT_HASH is 32-bit on most platforms except s390 (where it's 64-bit). * The bloom filter word size differs between 32-bit and 64-bit ELF files. This commit adjusts the code to handle both cases. Signed-off-by: Andrei Vagin <avagin@google.com>
This commit is contained in:
parent
4b099510b3
commit
e5fe6cc16d
@ -121,7 +121,8 @@ static int has_elf_identity(Ehdr_t *ehdr)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_elf_phdr(uintptr_t mem, size_t size, Phdr_t **dynamic, Phdr_t **load)
|
static int parse_elf_phdr(uintptr_t mem, size_t size,
|
||||||
|
Phdr_t **dynamic, Phdr_t **load, bool *is_32bit)
|
||||||
{
|
{
|
||||||
Ehdr_t *ehdr = (void *)mem;
|
Ehdr_t *ehdr = (void *)mem;
|
||||||
uintptr_t addr;
|
uintptr_t addr;
|
||||||
@ -136,6 +137,8 @@ static int parse_elf_phdr(uintptr_t mem, size_t size, Phdr_t **dynamic, Phdr_t *
|
|||||||
if (!has_elf_identity(ehdr))
|
if (!has_elf_identity(ehdr))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
*is_32bit = ehdr->e_ident[EI_CLASS] != ELFCLASS64;
|
||||||
|
|
||||||
addr = mem + ehdr->e_phoff;
|
addr = mem + ehdr->e_phoff;
|
||||||
if (__ptr_oob(addr, mem, size))
|
if (__ptr_oob(addr, mem, size))
|
||||||
goto err_oob;
|
goto err_oob;
|
||||||
@ -272,6 +275,8 @@ typedef unsigned long Hash_t;
|
|||||||
typedef Word_t Hash_t;
|
typedef Word_t Hash_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef uint32_t Hash32_t;
|
||||||
|
|
||||||
static bool elf_symbol_match(uintptr_t mem, size_t size,
|
static bool elf_symbol_match(uintptr_t mem, size_t size,
|
||||||
uintptr_t dynsymbol_names, Sym_t *sym,
|
uintptr_t dynsymbol_names, Sym_t *sym,
|
||||||
const char *symbol, const size_t vdso_symbol_length)
|
const char *symbol, const size_t vdso_symbol_length)
|
||||||
@ -297,21 +302,22 @@ static bool elf_symbol_match(uintptr_t mem, size_t size,
|
|||||||
static unsigned long elf_symbol_lookup(uintptr_t mem, size_t size,
|
static unsigned long elf_symbol_lookup(uintptr_t mem, size_t size,
|
||||||
const char *symbol, uint32_t symbol_hash, unsigned int sym_off,
|
const char *symbol, uint32_t symbol_hash, unsigned int sym_off,
|
||||||
uintptr_t dynsymbol_names, Dyn_t *dyn_symtab, Phdr_t *load,
|
uintptr_t dynsymbol_names, Dyn_t *dyn_symtab, Phdr_t *load,
|
||||||
Hash_t nbucket, Hash_t nchain, Hash_t *bucket, Hash_t *chain,
|
uint64_t nbucket, uint64_t nchain, void *_bucket, Hash_t *chain,
|
||||||
const size_t vdso_symbol_length, bool use_gnu_hash)
|
const size_t vdso_symbol_length, bool use_gnu_hash)
|
||||||
{
|
{
|
||||||
unsigned int j;
|
unsigned int j;
|
||||||
uintptr_t addr;
|
uintptr_t addr;
|
||||||
|
|
||||||
j = bucket[symbol_hash % nbucket];
|
|
||||||
if (j == STN_UNDEF)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
addr = mem + dyn_symtab->d_un.d_ptr - load->p_vaddr;
|
addr = mem + dyn_symtab->d_un.d_ptr - load->p_vaddr;
|
||||||
|
|
||||||
if (use_gnu_hash) {
|
if (use_gnu_hash) {
|
||||||
uint32_t *h = bucket + nbucket + (j - sym_off);
|
Hash32_t *h, hash_val, *bucket = _bucket;
|
||||||
uint32_t hash_val;
|
|
||||||
|
j = bucket[symbol_hash % nbucket];
|
||||||
|
if (j == STN_UNDEF)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
h = bucket + nbucket + (j - sym_off);
|
||||||
|
|
||||||
symbol_hash |= 1;
|
symbol_hash |= 1;
|
||||||
do {
|
do {
|
||||||
@ -325,6 +331,12 @@ static unsigned long elf_symbol_lookup(uintptr_t mem, size_t size,
|
|||||||
j++;
|
j++;
|
||||||
} while (!(hash_val & 1));
|
} while (!(hash_val & 1));
|
||||||
} else {
|
} else {
|
||||||
|
Hash_t *bucket = _bucket;
|
||||||
|
|
||||||
|
j = bucket[symbol_hash % nbucket];
|
||||||
|
if (j == STN_UNDEF)
|
||||||
|
return 0;
|
||||||
|
|
||||||
for (; j < nchain && j != STN_UNDEF; j = chain[j]) {
|
for (; j < nchain && j != STN_UNDEF; j = chain[j]) {
|
||||||
Sym_t *sym = (void *)addr + sizeof(Sym_t) * j;
|
Sym_t *sym = (void *)addr + sizeof(Sym_t) * j;
|
||||||
|
|
||||||
@ -338,17 +350,17 @@ static unsigned long elf_symbol_lookup(uintptr_t mem, size_t size,
|
|||||||
|
|
||||||
static int parse_elf_symbols(uintptr_t mem, size_t size, Phdr_t *load,
|
static int parse_elf_symbols(uintptr_t mem, size_t size, Phdr_t *load,
|
||||||
struct vdso_symtable *t, uintptr_t dynsymbol_names,
|
struct vdso_symtable *t, uintptr_t dynsymbol_names,
|
||||||
Hash_t *hash, Dyn_t *dyn_symtab, bool use_gnu_hash)
|
Hash_t *hash, Dyn_t *dyn_symtab, bool use_gnu_hash,
|
||||||
|
bool is_32bit)
|
||||||
{
|
{
|
||||||
ARCH_VDSO_SYMBOLS_LIST
|
ARCH_VDSO_SYMBOLS_LIST
|
||||||
|
|
||||||
const char *vdso_symbols[VDSO_SYMBOL_MAX] = { ARCH_VDSO_SYMBOLS };
|
const char *vdso_symbols[VDSO_SYMBOL_MAX] = { ARCH_VDSO_SYMBOLS };
|
||||||
const size_t vdso_symbol_length = sizeof(t->symbols[0].name) - 1;
|
const size_t vdso_symbol_length = sizeof(t->symbols[0].name) - 1;
|
||||||
|
|
||||||
Hash_t *bucket = NULL;
|
void *bucket = NULL;
|
||||||
Hash_t *chain = NULL;
|
Hash_t *chain = NULL;
|
||||||
Hash_t nbucket = 0;
|
uint64_t nbucket, nchain = 0;
|
||||||
Hash_t nchain = 0;
|
|
||||||
|
|
||||||
unsigned int sym_off = 0;
|
unsigned int sym_off = 0;
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
@ -358,17 +370,23 @@ static int parse_elf_symbols(uintptr_t mem, size_t size, Phdr_t *load,
|
|||||||
if (use_gnu_hash) {
|
if (use_gnu_hash) {
|
||||||
uint32_t *gnu_hash = (uint32_t *)hash;
|
uint32_t *gnu_hash = (uint32_t *)hash;
|
||||||
uint32_t bloom_sz;
|
uint32_t bloom_sz;
|
||||||
size_t *bloom;
|
|
||||||
|
|
||||||
nbucket = gnu_hash[0];
|
nbucket = gnu_hash[0];
|
||||||
sym_off = gnu_hash[1];
|
sym_off = gnu_hash[1];
|
||||||
bloom_sz = gnu_hash[2];
|
bloom_sz = gnu_hash[2];
|
||||||
bloom = (size_t *)&gnu_hash[4];
|
if (is_32bit) {
|
||||||
bucket = (Hash_t *)(&bloom[bloom_sz]);
|
uint32_t *bloom;
|
||||||
|
bloom = (uint32_t *)&gnu_hash[4];
|
||||||
|
bucket = (Hash_t *)(&bloom[bloom_sz]);
|
||||||
|
} else {
|
||||||
|
uint64_t *bloom;
|
||||||
|
bloom = (uint64_t *)&gnu_hash[4];
|
||||||
|
bucket = (Hash_t *)(&bloom[bloom_sz]);
|
||||||
|
}
|
||||||
elf_hash = &elf_gnu_hash;
|
elf_hash = &elf_gnu_hash;
|
||||||
pr_debug("nbucket %lx sym_off %lx bloom_sz %lx bloom %lx bucket %lx\n",
|
pr_debug("nbucket %lx sym_off %lx bloom_sz %lx bucket %lx\n",
|
||||||
(unsigned long)nbucket, (unsigned long)sym_off,
|
(unsigned long)nbucket, (unsigned long)sym_off,
|
||||||
(unsigned long)bloom_sz, (unsigned long)bloom,
|
(unsigned long)bloom_sz,
|
||||||
(unsigned long)bucket);
|
(unsigned long)bucket);
|
||||||
} else {
|
} else {
|
||||||
nbucket = hash[0];
|
nbucket = hash[0];
|
||||||
@ -417,6 +435,7 @@ int vdso_fill_symtable(uintptr_t mem, size_t size, struct vdso_symtable *t)
|
|||||||
Dyn_t *dyn_hash = NULL;
|
Dyn_t *dyn_hash = NULL;
|
||||||
Hash_t *hash = NULL;
|
Hash_t *hash = NULL;
|
||||||
bool use_gnu_hash;
|
bool use_gnu_hash;
|
||||||
|
bool is_32bit;
|
||||||
|
|
||||||
uintptr_t dynsymbol_names;
|
uintptr_t dynsymbol_names;
|
||||||
uintptr_t addr;
|
uintptr_t addr;
|
||||||
@ -427,7 +446,7 @@ int vdso_fill_symtable(uintptr_t mem, size_t size, struct vdso_symtable *t)
|
|||||||
/*
|
/*
|
||||||
* We need PT_LOAD and PT_DYNAMIC here. Each once.
|
* We need PT_LOAD and PT_DYNAMIC here. Each once.
|
||||||
*/
|
*/
|
||||||
ret = parse_elf_phdr(mem, size, &dynamic, &load);
|
ret = parse_elf_phdr(mem, size, &dynamic, &load, &is_32bit);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
if (!load || !dynamic) {
|
if (!load || !dynamic) {
|
||||||
@ -458,7 +477,7 @@ int vdso_fill_symtable(uintptr_t mem, size_t size, struct vdso_symtable *t)
|
|||||||
hash = (void *)addr;
|
hash = (void *)addr;
|
||||||
|
|
||||||
ret = parse_elf_symbols(mem, size, load, t, dynsymbol_names, hash, dyn_symtab,
|
ret = parse_elf_symbols(mem, size, load, t, dynsymbol_names, hash, dyn_symtab,
|
||||||
use_gnu_hash);
|
use_gnu_hash, is_32bit);
|
||||||
|
|
||||||
if (ret <0)
|
if (ret <0)
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user