//! A Gnu Hash table as 4 sections: //! //! 1. Header //! 2. Bloom Filter //! 3. Hash Buckets //! 4. Hash Values //! //! The header has is an array of four (4) u32s: //! //! 1. nbuckets //! 2. symndx //! 3. maskwords //! 4. shift2 //! //! See: https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2 /// GNU hash function: takes a string and returns the u32 hash of that string pub fn hash(symbol: &str) -> u32 { const HASH_SEED: u32 = 5381; let mut hash = HASH_SEED; for b in symbol.as_bytes() { hash = hash .wrapping_mul(33) .wrapping_add(u32::from(*b)); } hash } #[cfg(test)] mod tests { use super::hash; #[test] fn test_hash() { assert_eq!(hash("") , 0x0000_1505); assert_eq!(hash("printf") , 0x156b_2bb8); assert_eq!(hash("exit") , 0x7c96_7e3f); assert_eq!(hash("syscall") , 0xbac2_12a0); assert_eq!(hash("flapenguin.me"), 0x8ae9_f18e); } } macro_rules! elf_gnu_hash_impl { ($size:ty) => { use core::slice; use core::mem; use crate::strtab::Strtab; use super::sym; pub struct GnuHash<'process> { nbuckets: u32, symindex: usize, shift2: u32, maskbits: u32, bloomwords: &'process [$size], // either 32 or 64 bit masks, depending on platform maskwords_bitmask: u32, buckets: &'process [u32], hashvalues: &'process [u32], symtab: &'process [sym::Sym], } impl<'process> GnuHash<'process> { pub unsafe fn new(hashtab: *const u32, total_dynsyms: usize, symtab: &'process [sym::Sym]) -> GnuHash<'process> { let nbuckets = *hashtab; let symindex = *hashtab.add(1) as usize; let maskwords = *hashtab.add(2) as usize; // how many words our bloom filter mask has let shift2 = *hashtab.add(3); let bloomwords_ptr = hashtab.add(4) as *const $size; let buckets_ptr = bloomwords_ptr.add(maskwords) as *const u32; let buckets = slice::from_raw_parts(buckets_ptr, nbuckets as usize); let hashvalues_ptr = buckets_ptr.add(nbuckets as usize); let hashvalues = slice::from_raw_parts(hashvalues_ptr, total_dynsyms - symindex); let bloomwords = slice::from_raw_parts(bloomwords_ptr, maskwords); GnuHash { nbuckets, symindex, shift2, maskbits: mem::size_of::() as u32, bloomwords, hashvalues, buckets, maskwords_bitmask: ((maskwords as i32) - 1) as u32, symtab, } } #[inline(always)] fn lookup(&self, symbol: &str, hash: u32, strtab: &Strtab) -> Option<&sym::Sym> { let mut idx = self.buckets[(hash % self.nbuckets) as usize] as usize; // println!("lookup idx = buckets[hash % nbuckets] = {}", idx); if idx == 0 { return None; } let mut hash_idx = idx - self.symindex; let hash = hash & !1; // TODO: replace this with an iterator loop { let symbol_ = &self.symtab[idx]; let h2 = self.hashvalues[hash_idx]; idx += 1; hash_idx += 1; let name = &strtab[symbol_.st_name as usize]; // println!("{}: h2 0x{:x} resolves to: {}", i, h2, name); if hash == (h2 & !1) && name == symbol { // println!("lookup match for {} at: 0x{:x}", symbol, symbol_.st_value); return Some(symbol_); } if h2 & 1 == 1 { break; } // end of chain } 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)); // println!("lookup: maskwords: {} bloom_idx: {} bitmask: {} shift2: {}", self.maskwords, bloom_idx, bitmask, self.shift2); let filter = self.bloomwords[bloom_idx as usize] as usize; // FIXME: verify this is safe ;) filter & (bitmask as usize) != (bitmask as usize) // if true, def _don't have_ } /// Given a name, a hash of that name, a strtab to cross-reference names, maybe returns a Sym pub fn find(&self, name: &str, hash: u32, strtab: &Strtab) -> Option<&sym::Sym> { if self.filter(hash) { None } else { self.lookup(name, hash, strtab) } } } } }