Coverage Report

Created: 2024-10-16 07:58

/rust/registry/src/index.crates.io-6f17d22bba15001f/object-0.36.1/src/read/elf/hash.rs
Line
Count
Source (jump to first uncovered line)
1
use core::mem;
2
3
use crate::elf;
4
use crate::endian::{U32, U64};
5
use crate::read::{ReadError, ReadRef, Result, SymbolIndex};
6
7
use super::{FileHeader, Sym, SymbolTable, Version, VersionTable};
8
9
/// A SysV symbol hash table in an ELF file.
10
///
11
/// Returned by [`SectionHeader::hash`](super::SectionHeader::hash).
12
#[derive(Debug)]
13
pub struct HashTable<'data, Elf: FileHeader> {
14
    buckets: &'data [U32<Elf::Endian>],
15
    chains: &'data [U32<Elf::Endian>],
16
}
17
18
impl<'data, Elf: FileHeader> HashTable<'data, Elf> {
19
    /// Parse a SysV hash table.
20
    ///
21
    /// `data` should be from an [`elf::SHT_HASH`] section, or from a
22
    /// segment pointed to via the [`elf::DT_HASH`] entry.
23
    ///
24
    /// The header is read at offset 0 in the given `data`.
25
0
    pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result<Self> {
26
0
        let mut offset = 0;
27
0
        let header = data
28
0
            .read::<elf::HashHeader<Elf::Endian>>(&mut offset)
29
0
            .read_error("Invalid hash header")?;
30
0
        let buckets = data
31
0
            .read_slice(&mut offset, header.bucket_count.get(endian) as usize)
32
0
            .read_error("Invalid hash buckets")?;
33
0
        let chains = data
34
0
            .read_slice(&mut offset, header.chain_count.get(endian) as usize)
35
0
            .read_error("Invalid hash chains")?;
36
0
        Ok(HashTable { buckets, chains })
37
0
    }
38
39
    /// Return the symbol table length.
40
0
    pub fn symbol_table_length(&self) -> u32 {
41
0
        self.chains.len() as u32
42
0
    }
43
44
0
    fn bucket(&self, endian: Elf::Endian, hash: u32) -> SymbolIndex {
45
0
        SymbolIndex(self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize)
46
0
    }
47
48
0
    fn chain(&self, endian: Elf::Endian, index: SymbolIndex) -> SymbolIndex {
49
0
        SymbolIndex(self.chains[index.0].get(endian) as usize)
50
0
    }
51
52
    /// Use the hash table to find the symbol table entry with the given name, hash and version.
53
0
    pub fn find<R: ReadRef<'data>>(
54
0
        &self,
55
0
        endian: Elf::Endian,
56
0
        name: &[u8],
57
0
        hash: u32,
58
0
        version: Option<&Version<'_>>,
59
0
        symbols: &SymbolTable<'data, Elf, R>,
60
0
        versions: &VersionTable<'data, Elf>,
61
0
    ) -> Option<(SymbolIndex, &'data Elf::Sym)> {
62
0
        // Get the chain start from the bucket for this hash.
63
0
        let mut index = self.bucket(endian, hash);
64
0
        // Avoid infinite loop.
65
0
        let mut i = 0;
66
0
        let strings = symbols.strings();
67
0
        while index != SymbolIndex(0) && i < self.chains.len() {
68
0
            if let Ok(symbol) = symbols.symbol(index) {
69
0
                if symbol.name(endian, strings) == Ok(name)
70
0
                    && versions.matches(endian, index, version)
71
                {
72
0
                    return Some((index, symbol));
73
0
                }
74
0
            }
75
0
            index = self.chain(endian, index);
76
0
            i += 1;
77
        }
78
0
        None
79
0
    }
80
}
81
82
/// A GNU symbol hash table in an ELF file.
83
///
84
/// Returned by [`SectionHeader::gnu_hash`](super::SectionHeader::gnu_hash).
85
#[derive(Debug)]
86
pub struct GnuHashTable<'data, Elf: FileHeader> {
87
    symbol_base: u32,
88
    bloom_shift: u32,
89
    bloom_filters: &'data [u8],
90
    buckets: &'data [U32<Elf::Endian>],
91
    values: &'data [U32<Elf::Endian>],
92
}
93
94
impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> {
95
    /// Parse a GNU hash table.
96
    ///
97
    /// `data` should be from an [`elf::SHT_GNU_HASH`] section, or from a
98
    /// segment pointed to via the [`elf::DT_GNU_HASH`] entry.
99
    ///
100
    /// The header is read at offset 0 in the given `data`.
101
    ///
102
    /// The header does not contain a length field, and so all of `data`
103
    /// will be used as the hash table values. It does not matter if this
104
    /// is longer than needed, and this will often the case when accessing
105
    /// the hash table via the [`elf::DT_GNU_HASH`] entry.
106
0
    pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result<Self> {
107
0
        let mut offset = 0;
108
0
        let header = data
109
0
            .read::<elf::GnuHashHeader<Elf::Endian>>(&mut offset)
110
0
            .read_error("Invalid GNU hash header")?;
111
0
        let bloom_len =
112
0
            u64::from(header.bloom_count.get(endian)) * mem::size_of::<Elf::Word>() as u64;
113
0
        let bloom_filters = data
114
0
            .read_bytes(&mut offset, bloom_len)
115
0
            .read_error("Invalid GNU hash bloom filters")?;
116
0
        let buckets = data
117
0
            .read_slice(&mut offset, header.bucket_count.get(endian) as usize)
118
0
            .read_error("Invalid GNU hash buckets")?;
119
0
        let chain_count = (data.len() - offset as usize) / 4;
120
0
        let values = data
121
0
            .read_slice(&mut offset, chain_count)
122
0
            .read_error("Invalid GNU hash values")?;
123
0
        Ok(GnuHashTable {
124
0
            symbol_base: header.symbol_base.get(endian),
125
0
            bloom_shift: header.bloom_shift.get(endian),
126
0
            bloom_filters,
127
0
            buckets,
128
0
            values,
129
0
        })
130
0
    }
131
132
    /// Return the symbol table index of the first symbol in the hash table.
133
0
    pub fn symbol_base(&self) -> u32 {
134
0
        self.symbol_base
135
0
    }
136
137
    /// Determine the symbol table length by finding the last entry in the hash table.
138
    ///
139
    /// Returns `None` if the hash table is empty or invalid.
140
0
    pub fn symbol_table_length(&self, endian: Elf::Endian) -> Option<u32> {
141
0
        // Ensure we find a non-empty bucket.
142
0
        if self.symbol_base == 0 {
143
0
            return None;
144
0
        }
145
0
146
0
        // Find the highest chain index in a bucket.
147
0
        let mut max_symbol = 0;
148
0
        for bucket in self.buckets {
149
0
            let bucket = bucket.get(endian);
150
0
            if max_symbol < bucket {
151
0
                max_symbol = bucket;
152
0
            }
153
        }
154
155
        // Find the end of the chain.
156
0
        for value in self
157
0
            .values
158
0
            .get(max_symbol.checked_sub(self.symbol_base)? as usize..)?
159
        {
160
0
            max_symbol += 1;
161
0
            if value.get(endian) & 1 != 0 {
162
0
                return Some(max_symbol);
163
0
            }
164
        }
165
166
0
        None
167
0
    }
168
169
0
    fn bucket(&self, endian: Elf::Endian, hash: u32) -> SymbolIndex {
170
0
        SymbolIndex(self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize)
171
0
    }
172
173
    /// Use the hash table to find the symbol table entry with the given name, hash, and version.
174
0
    pub fn find<R: ReadRef<'data>>(
175
0
        &self,
176
0
        endian: Elf::Endian,
177
0
        name: &[u8],
178
0
        hash: u32,
179
0
        version: Option<&Version<'_>>,
180
0
        symbols: &SymbolTable<'data, Elf, R>,
181
0
        versions: &VersionTable<'data, Elf>,
182
0
    ) -> Option<(SymbolIndex, &'data Elf::Sym)> {
183
0
        let word_bits = mem::size_of::<Elf::Word>() as u32 * 8;
184
0
185
0
        // Test against bloom filter.
186
0
        let bloom_count = self.bloom_filters.len() / mem::size_of::<Elf::Word>();
187
0
        let offset =
188
0
            ((hash / word_bits) & (bloom_count as u32 - 1)) * mem::size_of::<Elf::Word>() as u32;
189
0
        let filter = if word_bits == 64 {
190
0
            self.bloom_filters
191
0
                .read_at::<U64<Elf::Endian>>(offset.into())
192
0
                .ok()?
193
0
                .get(endian)
194
        } else {
195
0
            self.bloom_filters
196
0
                .read_at::<U32<Elf::Endian>>(offset.into())
197
0
                .ok()?
198
0
                .get(endian)
199
0
                .into()
200
        };
201
0
        if filter & (1 << (hash % word_bits)) == 0 {
202
0
            return None;
203
0
        }
204
0
        if filter & (1 << ((hash >> self.bloom_shift) % word_bits)) == 0 {
205
0
            return None;
206
0
        }
207
0
208
0
        // Get the chain start from the bucket for this hash.
209
0
        let mut index = self.bucket(endian, hash);
210
0
        if index == SymbolIndex(0) {
211
0
            return None;
212
0
        }
213
0
214
0
        // Test symbols in the chain.
215
0
        let strings = symbols.strings();
216
0
        let symbols = symbols.symbols().get(index.0..)?;
217
0
        let values = self
218
0
            .values
219
0
            .get(index.0.checked_sub(self.symbol_base as usize)?..)?;
220
0
        for (symbol, value) in symbols.iter().zip(values.iter()) {
221
0
            let value = value.get(endian);
222
0
            if value | 1 == hash | 1 {
223
0
                if symbol.name(endian, strings) == Ok(name)
224
0
                    && versions.matches(endian, index, version)
225
                {
226
0
                    return Some((index, symbol));
227
0
                }
228
0
            }
229
0
            if value & 1 != 0 {
230
0
                break;
231
0
            }
232
0
            index.0 += 1;
233
        }
234
0
        None
235
0
    }
236
}