]>
git.proxmox.com Git - rustc.git/blob - vendor/goblin/src/elf/gnu_hash.rs
1 //! A Gnu Hash table as 4 sections:
8 //! The header has is an array of four `u32`s:
16 //! * http://www.linker-aliens.org/blogs/ali/entry/gnu_hash_elf_sections
17 //! or https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2
18 //! * https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
20 /// GNU hash function: accepts a symbol name and returns a value that may be
21 /// used to compute a bucket index.
23 /// Consequently, if the hashing function returns the value `x` for some name,
24 /// `buckets[x % nbuckets]` gives an index, `y`, into both the symbol table
25 /// and the chain table.
26 pub fn hash(symbol
: &str) -> u32 {
27 const HASH_SEED
: u32 = 5381;
28 symbol
.bytes().fold(HASH_SEED
, |hash
, b
| {
29 hash
.wrapping_mul(33).wrapping_add(u32::from(b
))
38 assert_eq
!(hash(""), 0x0000_1505);
39 assert_eq
!(hash("printf"), 0x156b_2bb8);
40 assert_eq
!(hash("exit"), 0x7c96_7e3f);
41 assert_eq
!(hash("syscall"), 0xbac2_12a0);
42 assert_eq
!(hash("flapenguin.me"), 0x8ae9_f18e);
46 macro_rules
! elf_gnu_hash_impl
{
48 use crate::elf
::sym
::Sym
;
49 use crate::strtab
::Strtab
;
54 const INT_SIZE
: usize = mem
::size_of
::<$IntTy
>();
55 const U32_SIZE
: usize = mem
::size_of
::<u32>();
56 /// Size of a bits mask in bloom filter
57 const ELFCLASS_BITS
: u32 = INT_SIZE
as u32 * 8;
59 /// A better hash table for the ELF used by GNU systems in GNU-compatible software.
60 pub struct GnuHash
<'a
> {
61 /// Index of the first symbol in the `.dynsym` table which is accessible with
64 /// Shift count used in the bloom filter
66 /// 2 bit bloom filter on `chains`
67 // Either 32 or 64-bit depending on the class of object
68 bloom_filter
: &'a
[$IntTy
],
69 /// GNU hash table bucket array; indexes start at 0. This array holds symbol
70 /// table indexes and contains the index of hashes in `chains`
72 /// Hash values; indexes start at 0. This array holds symbol table indexes.
73 chains
: &'a
[u32], // => chains[dynsyms.len() - symindex]
77 impl fmt
::Debug
for GnuHash
<'_
> {
78 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
79 f
.debug_struct("GnuHash")
80 .field("nbuckets", &self.buckets
.len())
81 .field("symindex", &self.symindex
)
82 .field("maskwords", &(self.bloom_filter
.len() - 1))
83 .field("shift2", &self.shift2
)
84 .field("bloom_filter", &self.bloom_filter
.as_ptr())
85 .field("bucket", &self.buckets
.as_ptr())
86 .field("chains", &self.chains
.as_ptr())
91 impl<'a
> GnuHash
<'a
> {
92 /// Initialize a GnuHash from a pointer to `.hash` (or `.gnu.hash`) section
93 /// and total number of dynamic symbols.
96 /// This function creates a `GnuHash` directly from a raw pointer
97 pub unsafe fn from_raw_table(
100 ) -> Result
<Self, &'
static str> {
101 if hashtab
.as_ptr() as usize % INT_SIZE
!= 0 {
102 return Err("hashtab is not aligned with 64-bit");
105 if hashtab
.len() <= 16 {
106 return Err("failed to read in number of buckets");
109 let [nbuckets
, symindex
, maskwords
, shift2
] =
110 (hashtab
.as_ptr() as *const u32 as *const [u32; 4]).read();
112 if !maskwords
.is_power_of_two() {
113 return Err("maskwords must be a power of two");
116 let hashtab
= &hashtab
[16..];
118 // SAFETY: Condition to check for an overflow
119 // size_of(chains) + size_of(buckets) + size_of(bloom_filter) == size_of(hashtab)
121 if dynsyms
.len() <= symindex
as usize {
122 return Err("symindex must be smaller than dynsyms.len()");
124 let chains_size
= (dynsyms
.len() - symindex
as usize).checked_mul(U32_SIZE
);
125 let buckets_size
= (nbuckets
as usize).checked_mul(U32_SIZE
);
126 let bloom_size
= (maskwords
as usize).checked_mul(INT_SIZE
);
128 let total_size
= match (chains_size
, buckets_size
, bloom_size
) {
129 (Some(a
), Some(b
), Some(c
)) => {
130 a
.checked_add(b
).and_then(|t
| t
.checked_add(c
))
135 Some(size
) if size
== hashtab
.len() => {}
136 _
=> return Err("index out of bound or non-complete hash section"),
140 let bloom_filter_ptr
= hashtab
.as_ptr() as *const $IntTy
;
141 let buckets_ptr
= bloom_filter_ptr
.add(maskwords
as usize) as *const u32;
142 let chains_ptr
= buckets_ptr
.add(nbuckets
as usize);
143 let bloom_filter
= slice
::from_raw_parts(bloom_filter_ptr
, maskwords
as usize);
144 let buckets
= slice
::from_raw_parts(buckets_ptr
, nbuckets
as usize);
145 let chains
= slice
::from_raw_parts(chains_ptr
, dynsyms
.len() - symindex
as usize);
156 /// Locate the hash chain, and corresponding hash value element.
157 fn lookup(&self, symbol
: &str, hash
: u32, dynstrtab
: &Strtab
) -> Option
<&'a Sym
> {
158 const MASK_LOWEST_BIT
: u32 = 0xffff_fffe;
159 let bucket
= self.buckets
[hash
as usize % self.buckets
.len()];
161 // Empty hash chain, symbol not present
162 if bucket
< self.symindex
{
165 // Walk the chain until the symbol is found or the chain is exhausted.
166 let chain_idx
= bucket
- self.symindex
;
167 let hash
= hash
& MASK_LOWEST_BIT
;
168 let chains
= &self.chains
.get((chain_idx
as usize)..)?
;
169 let dynsyms
= &self.dynsyms
.get((bucket
as usize)..)?
;
170 for (hash2
, symb
) in chains
.iter().zip(dynsyms
.iter()) {
171 if (hash
== (hash2
& MASK_LOWEST_BIT
))
172 && (symbol
== &dynstrtab
[symb
.st_name
as usize])
176 // Chain ends with an element with the lowest bit set to 1.
184 /// Check if symbol maybe is in the hash table, or definitely not in it.
186 fn check_maybe_match(&self, hash
: u32) -> bool
{
187 const MASK
: u32 = ELFCLASS_BITS
- 1;
188 let hash2
= hash
>> self.shift2
;
189 // `x & (N - 1)` is equivalent to `x % N` iff `N = 2^y`.
190 let bitmask
: $IntTy
= 1 << (hash
& (MASK
)) | 1 << (hash2
& MASK
);
191 let bloom_idx
= (hash
/ ELFCLASS_BITS
) & (self.bloom_filter
.len() as u32 - 1);
192 let bitmask_word
= self.bloom_filter
[bloom_idx
as usize];
193 (bitmask_word
& bitmask
) == bitmask
196 /// Given a symbol, a hash of that symbol, a dynamic string table and
197 /// a `dynstrtab` to cross-reference names, maybe returns a Sym.
198 pub fn find(&self, symbol
: &str, dynstrtab
: &Strtab
) -> Option
<&'a Sym
> {
199 let hash
= self::hash(symbol
);
200 self.find_with_hash(symbol
, hash
, dynstrtab
)
203 /// This function will not check if the passed `hash` is really
204 /// the hash of `symbol`
205 pub fn find_with_hash(
210 ) -> Option
<&'a Sym
> {
211 if self.check_maybe_match(hash
) {
212 #[cold] // HACK: This is trick for `unlikely` in C
213 self.lookup(symbol
, hash
, dynstrtab
)