use std::slice;
use elf::strtab;
use elf::sym;
static HASH_SEED: u32 = 5381;
pub fn hash(symbol: &str) -> u32 {
let bytes = symbol.as_bytes();
let mut hash = HASH_SEED;
for b in bytes {
hash = hash.wrapping_mul(32).wrapping_add(*b as u32).wrapping_add(hash);
}
hash
}
pub struct GnuHash<'process> {
nbuckets: u32,
symindex: usize,
shift2: u32,
maskbits: u32,
bloomwords: &'process [usize], maskwords_bitmask: u32,
buckets: &'process [u32],
hashvalues: &'process [u32],
}
impl<'process> GnuHash<'process> {
pub fn new(hashtab: *const u32, total_dynsyms: usize) -> GnuHash<'process> {
unsafe {
let nbuckets = *hashtab;
let symindex = *hashtab.offset(1) as usize;
let maskwords = *hashtab.offset(2) as usize; let shift2 = *hashtab.offset(3);
let bloomwords_ptr = hashtab.offset(4) as *const usize;
let buckets_ptr = bloomwords_ptr.offset(maskwords as isize) as *const u32;
let buckets = slice::from_raw_parts(buckets_ptr, nbuckets as usize);
let hashvalues_ptr = buckets_ptr.offset(nbuckets as isize);
let hashvalues = slice::from_raw_parts(hashvalues_ptr, total_dynsyms - symindex);
let bloomwords = slice::from_raw_parts(bloomwords_ptr, maskwords);
GnuHash {
nbuckets: nbuckets,
symindex: symindex,
shift2: shift2,
maskbits: 64, bloomwords: bloomwords,
hashvalues: hashvalues,
buckets: buckets,
maskwords_bitmask: ((maskwords as i32) - 1) as u32,
}
}
}
#[inline(always)]
fn lookup(&self,
hash: u32,
symbol: &str,
strtab: &'process strtab::Strtab,
symtab: &'process [sym::Sym])
-> Option<sym::Sym> {
let mut idx = self.buckets[(hash % self.nbuckets) as usize] as usize;
if idx == 0 {
return None;
}
let mut hash_idx = idx - self.symindex;
let hash = hash & !1;
loop {
let symbol_ = &symtab[idx];
let h2 = self.hashvalues[hash_idx];
idx += 1;
hash_idx += 1;
let name = &strtab[symbol_.st_name as usize];
if hash == (h2 & !1) && name == symbol {
return Some(symbol_.clone());
}
if h2 & 1 == 1 {
break;
} }
None
}
#[inline(always)]
fn filter(&self, hash: u32) -> bool {
let bloom_idx = (hash / self.maskbits) & self.maskwords_bitmask;
let h2 = hash >> self.shift2;
let bitmask = (1u64 << (hash % self.maskbits)) | (1u64 << (h2 % self.maskbits));
let filter = self.bloomwords[bloom_idx as usize];
filter & (bitmask as usize) != (bitmask as usize) }
pub fn find(&self,
name: &str,
hash: u32,
strtab: &'process strtab::Strtab,
symtab: &'process [sym::Sym])
-> Option<sym::Sym> {
if self.filter(hash) {
None
} else {
self.lookup(hash, name, strtab, symtab)
}
}
}