object/read/elf/
version.rs

1use alloc::vec::Vec;
2
3use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable, SymbolIndex};
4use crate::{elf, endian};
5
6use super::FileHeader;
7
8/// A version index.
9#[derive(Debug, Default, Clone, Copy)]
10pub struct VersionIndex(pub u16);
11
12impl VersionIndex {
13    /// Return the version index.
14    pub fn index(&self) -> u16 {
15        self.0 & elf::VERSYM_VERSION
16    }
17
18    /// Return true if it is the local index.
19    pub fn is_local(&self) -> bool {
20        self.index() == elf::VER_NDX_LOCAL
21    }
22
23    /// Return true if it is the global index.
24    pub fn is_global(&self) -> bool {
25        self.index() == elf::VER_NDX_GLOBAL
26    }
27
28    /// Return the hidden flag.
29    pub fn is_hidden(&self) -> bool {
30        self.0 & elf::VERSYM_HIDDEN != 0
31    }
32}
33
34/// A version definition or requirement.
35///
36/// This is derived from entries in the [`elf::SHT_GNU_VERDEF`] and [`elf::SHT_GNU_VERNEED`] sections.
37#[derive(Debug, Default, Clone, Copy)]
38pub struct Version<'data> {
39    name: &'data [u8],
40    hash: u32,
41    // Used to keep track of valid indices in `VersionTable`.
42    valid: bool,
43    file: Option<&'data [u8]>,
44}
45
46impl<'data> Version<'data> {
47    /// Return the version name.
48    pub fn name(&self) -> &'data [u8] {
49        self.name
50    }
51
52    /// Return hash of the version name.
53    pub fn hash(&self) -> u32 {
54        self.hash
55    }
56
57    /// Return the filename of the library containing this version.
58    ///
59    /// This is the `vn_file` field of the associated entry in [`elf::SHT_GNU_VERNEED`].
60    /// or `None` if the version info was parsed from a [`elf::SHT_GNU_VERDEF`] section.
61    pub fn file(&self) -> Option<&'data [u8]> {
62        self.file
63    }
64}
65
66/// A table of version definitions and requirements.
67///
68/// It allows looking up the version information for a given symbol index.
69///
70/// This is derived from entries in the [`elf::SHT_GNU_VERSYM`], [`elf::SHT_GNU_VERDEF`]
71/// and [`elf::SHT_GNU_VERNEED`] sections.
72///
73/// Returned by [`SectionTable::versions`](super::SectionTable::versions).
74#[derive(Debug, Clone)]
75pub struct VersionTable<'data, Elf: FileHeader> {
76    symbols: &'data [elf::Versym<Elf::Endian>],
77    versions: Vec<Version<'data>>,
78}
79
80impl<'data, Elf: FileHeader> Default for VersionTable<'data, Elf> {
81    fn default() -> Self {
82        VersionTable {
83            symbols: &[],
84            versions: Vec::new(),
85        }
86    }
87}
88
89impl<'data, Elf: FileHeader> VersionTable<'data, Elf> {
90    /// Parse the version sections.
91    pub fn parse<R: ReadRef<'data>>(
92        endian: Elf::Endian,
93        versyms: &'data [elf::Versym<Elf::Endian>],
94        verdefs: Option<VerdefIterator<'data, Elf>>,
95        verneeds: Option<VerneedIterator<'data, Elf>>,
96        strings: StringTable<'data, R>,
97    ) -> Result<Self> {
98        let mut max_index = 0;
99        if let Some(mut verdefs) = verdefs.clone() {
100            while let Some((verdef, _)) = verdefs.next()? {
101                if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 {
102                    continue;
103                }
104                let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION;
105                if max_index < index {
106                    max_index = index;
107                }
108            }
109        }
110        if let Some(mut verneeds) = verneeds.clone() {
111            while let Some((_, mut vernauxs)) = verneeds.next()? {
112                while let Some(vernaux) = vernauxs.next()? {
113                    let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION;
114                    if max_index < index {
115                        max_index = index;
116                    }
117                }
118            }
119        }
120
121        // Indices should be sequential, but this could be up to
122        // 32k * size_of::<Version>() if max_index is bad.
123        let mut versions = vec![Version::default(); max_index as usize + 1];
124
125        if let Some(mut verdefs) = verdefs {
126            while let Some((verdef, mut verdauxs)) = verdefs.next()? {
127                if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 {
128                    continue;
129                }
130                let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION;
131                if index <= elf::VER_NDX_GLOBAL {
132                    // TODO: return error?
133                    continue;
134                }
135                if let Some(verdaux) = verdauxs.next()? {
136                    versions[usize::from(index)] = Version {
137                        name: verdaux.name(endian, strings)?,
138                        hash: verdef.vd_hash.get(endian),
139                        valid: true,
140                        file: None,
141                    };
142                }
143            }
144        }
145        if let Some(mut verneeds) = verneeds {
146            while let Some((verneed, mut vernauxs)) = verneeds.next()? {
147                while let Some(vernaux) = vernauxs.next()? {
148                    let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION;
149                    if index <= elf::VER_NDX_GLOBAL {
150                        // TODO: return error?
151                        continue;
152                    }
153                    versions[usize::from(index)] = Version {
154                        name: vernaux.name(endian, strings)?,
155                        hash: vernaux.vna_hash.get(endian),
156                        valid: true,
157                        file: Some(verneed.file(endian, strings)?),
158                    };
159                }
160            }
161        }
162
163        Ok(VersionTable {
164            symbols: versyms,
165            versions,
166        })
167    }
168
169    /// Return true if the version table is empty.
170    pub fn is_empty(&self) -> bool {
171        self.symbols.is_empty()
172    }
173
174    /// Return version index for a given symbol index.
175    pub fn version_index(&self, endian: Elf::Endian, index: SymbolIndex) -> VersionIndex {
176        let version_index = match self.symbols.get(index.0) {
177            Some(x) => x.0.get(endian),
178            // Ideally this would be VER_NDX_LOCAL for undefined symbols,
179            // but currently there are no checks that need this distinction.
180            None => elf::VER_NDX_GLOBAL,
181        };
182        VersionIndex(version_index)
183    }
184
185    /// Return version information for a given symbol version index.
186    ///
187    /// Returns `Ok(None)` for local and global versions.
188    /// Returns `Err(_)` if index is invalid.
189    pub fn version(&self, index: VersionIndex) -> Result<Option<&Version<'data>>> {
190        if index.index() <= elf::VER_NDX_GLOBAL {
191            return Ok(None);
192        }
193        self.versions
194            .get(usize::from(index.index()))
195            .filter(|version| version.valid)
196            .read_error("Invalid ELF symbol version index")
197            .map(Some)
198    }
199
200    /// Return true if the given symbol index satisfies the requirements of `need`.
201    ///
202    /// Returns false for any error.
203    ///
204    /// Note: this function hasn't been fully tested and is likely to be incomplete.
205    pub fn matches(
206        &self,
207        endian: Elf::Endian,
208        index: SymbolIndex,
209        need: Option<&Version<'_>>,
210    ) -> bool {
211        let version_index = self.version_index(endian, index);
212        let def = match self.version(version_index) {
213            Ok(def) => def,
214            Err(_) => return false,
215        };
216        match (def, need) {
217            (Some(def), Some(need)) => need.hash == def.hash && need.name == def.name,
218            (None, Some(_need)) => {
219                // Version must be present if needed.
220                false
221            }
222            (Some(_def), None) => {
223                // For a dlsym call, use the newest version.
224                // TODO: if not a dlsym call, then use the oldest version.
225                !version_index.is_hidden()
226            }
227            (None, None) => true,
228        }
229    }
230}
231
232/// An iterator for the entries in an ELF [`elf::SHT_GNU_VERDEF`] section.
233#[derive(Debug, Clone)]
234pub struct VerdefIterator<'data, Elf: FileHeader> {
235    endian: Elf::Endian,
236    data: Bytes<'data>,
237}
238
239impl<'data, Elf: FileHeader> VerdefIterator<'data, Elf> {
240    pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self {
241        VerdefIterator {
242            endian,
243            data: Bytes(data),
244        }
245    }
246
247    /// Return the next `Verdef` entry.
248    pub fn next(
249        &mut self,
250    ) -> Result<Option<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)>> {
251        if self.data.is_empty() {
252            return Ok(None);
253        }
254
255        let result = self.parse().map(Some);
256        if result.is_err() {
257            self.data = Bytes(&[]);
258        }
259        result
260    }
261
262    fn parse(&mut self) -> Result<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)> {
263        let verdef = self
264            .data
265            .read_at::<elf::Verdef<_>>(0)
266            .read_error("ELF verdef is too short")?;
267
268        let mut verdaux_data = self.data;
269        verdaux_data
270            .skip(verdef.vd_aux.get(self.endian) as usize)
271            .read_error("Invalid ELF vd_aux")?;
272        let verdaux =
273            VerdauxIterator::new(self.endian, verdaux_data.0, verdef.vd_cnt.get(self.endian));
274
275        let next = verdef.vd_next.get(self.endian);
276        if next != 0 {
277            self.data
278                .skip(next as usize)
279                .read_error("Invalid ELF vd_next")?;
280        } else {
281            self.data = Bytes(&[]);
282        }
283        Ok((verdef, verdaux))
284    }
285}
286
287impl<'data, Elf: FileHeader> Iterator for VerdefIterator<'data, Elf> {
288    type Item = Result<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)>;
289
290    fn next(&mut self) -> Option<Self::Item> {
291        self.next().transpose()
292    }
293}
294
295/// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERDEF`] section.
296#[derive(Debug, Clone)]
297pub struct VerdauxIterator<'data, Elf: FileHeader> {
298    endian: Elf::Endian,
299    data: Bytes<'data>,
300    count: u16,
301}
302
303impl<'data, Elf: FileHeader> VerdauxIterator<'data, Elf> {
304    pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self {
305        VerdauxIterator {
306            endian,
307            data: Bytes(data),
308            count,
309        }
310    }
311
312    /// Return the next `Verdaux` entry.
313    pub fn next(&mut self) -> Result<Option<&'data elf::Verdaux<Elf::Endian>>> {
314        if self.count == 0 {
315            return Ok(None);
316        }
317
318        let result = self.parse().map(Some);
319        if result.is_err() {
320            self.count = 0;
321        } else {
322            self.count -= 1;
323        }
324        result
325    }
326
327    fn parse(&mut self) -> Result<&'data elf::Verdaux<Elf::Endian>> {
328        let verdaux = self
329            .data
330            .read_at::<elf::Verdaux<_>>(0)
331            .read_error("ELF verdaux is too short")?;
332
333        self.data
334            .skip(verdaux.vda_next.get(self.endian) as usize)
335            .read_error("Invalid ELF vda_next")?;
336        Ok(verdaux)
337    }
338}
339
340impl<'data, Elf: FileHeader> Iterator for VerdauxIterator<'data, Elf> {
341    type Item = Result<&'data elf::Verdaux<Elf::Endian>>;
342
343    fn next(&mut self) -> Option<Self::Item> {
344        self.next().transpose()
345    }
346}
347
348/// An iterator for the entries in an ELF [`elf::SHT_GNU_VERNEED`] section.
349#[derive(Debug, Clone)]
350pub struct VerneedIterator<'data, Elf: FileHeader> {
351    endian: Elf::Endian,
352    data: Bytes<'data>,
353}
354
355impl<'data, Elf: FileHeader> VerneedIterator<'data, Elf> {
356    pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self {
357        VerneedIterator {
358            endian,
359            data: Bytes(data),
360        }
361    }
362
363    /// Return the next `Verneed` entry.
364    pub fn next(
365        &mut self,
366    ) -> Result<
367        Option<(
368            &'data elf::Verneed<Elf::Endian>,
369            VernauxIterator<'data, Elf>,
370        )>,
371    > {
372        if self.data.is_empty() {
373            return Ok(None);
374        }
375
376        let result = self.parse().map(Some);
377        if result.is_err() {
378            self.data = Bytes(&[]);
379        }
380        result
381    }
382
383    fn parse(
384        &mut self,
385    ) -> Result<(
386        &'data elf::Verneed<Elf::Endian>,
387        VernauxIterator<'data, Elf>,
388    )> {
389        let verneed = self
390            .data
391            .read_at::<elf::Verneed<_>>(0)
392            .read_error("ELF verneed is too short")?;
393
394        let mut vernaux_data = self.data;
395        vernaux_data
396            .skip(verneed.vn_aux.get(self.endian) as usize)
397            .read_error("Invalid ELF vn_aux")?;
398        let vernaux =
399            VernauxIterator::new(self.endian, vernaux_data.0, verneed.vn_cnt.get(self.endian));
400
401        let next = verneed.vn_next.get(self.endian);
402        if next != 0 {
403            self.data
404                .skip(next as usize)
405                .read_error("Invalid ELF vn_next")?;
406        } else {
407            self.data = Bytes(&[]);
408        }
409        Ok((verneed, vernaux))
410    }
411}
412
413impl<'data, Elf: FileHeader> Iterator for VerneedIterator<'data, Elf> {
414    type Item = Result<(
415        &'data elf::Verneed<Elf::Endian>,
416        VernauxIterator<'data, Elf>,
417    )>;
418
419    fn next(&mut self) -> Option<Self::Item> {
420        self.next().transpose()
421    }
422}
423
424/// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERNEED`] section.
425#[derive(Debug, Clone)]
426pub struct VernauxIterator<'data, Elf: FileHeader> {
427    endian: Elf::Endian,
428    data: Bytes<'data>,
429    count: u16,
430}
431
432impl<'data, Elf: FileHeader> VernauxIterator<'data, Elf> {
433    pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self {
434        VernauxIterator {
435            endian,
436            data: Bytes(data),
437            count,
438        }
439    }
440
441    /// Return the next `Vernaux` entry.
442    pub fn next(&mut self) -> Result<Option<&'data elf::Vernaux<Elf::Endian>>> {
443        if self.count == 0 {
444            return Ok(None);
445        }
446
447        let result = self.parse().map(Some);
448        if result.is_err() {
449            self.count = 0;
450        } else {
451            self.count -= 1;
452        }
453        result
454    }
455
456    fn parse(&mut self) -> Result<&'data elf::Vernaux<Elf::Endian>> {
457        let vernaux = self
458            .data
459            .read_at::<elf::Vernaux<_>>(0)
460            .read_error("ELF vernaux is too short")?;
461        self.data
462            .skip(vernaux.vna_next.get(self.endian) as usize)
463            .read_error("Invalid ELF vna_next")?;
464        Ok(vernaux)
465    }
466}
467
468impl<'data, Elf: FileHeader> Iterator for VernauxIterator<'data, Elf> {
469    type Item = Result<&'data elf::Vernaux<Elf::Endian>>;
470
471    fn next(&mut self) -> Option<Self::Item> {
472        self.next().transpose()
473    }
474}
475
476impl<Endian: endian::Endian> elf::Verdaux<Endian> {
477    /// Parse the version name from the string table.
478    pub fn name<'data, R: ReadRef<'data>>(
479        &self,
480        endian: Endian,
481        strings: StringTable<'data, R>,
482    ) -> Result<&'data [u8]> {
483        strings
484            .get(self.vda_name.get(endian))
485            .read_error("Invalid ELF vda_name")
486    }
487}
488
489impl<Endian: endian::Endian> elf::Verneed<Endian> {
490    /// Parse the file from the string table.
491    pub fn file<'data, R: ReadRef<'data>>(
492        &self,
493        endian: Endian,
494        strings: StringTable<'data, R>,
495    ) -> Result<&'data [u8]> {
496        strings
497            .get(self.vn_file.get(endian))
498            .read_error("Invalid ELF vn_file")
499    }
500}
501
502impl<Endian: endian::Endian> elf::Vernaux<Endian> {
503    /// Parse the version name from the string table.
504    pub fn name<'data, R: ReadRef<'data>>(
505        &self,
506        endian: Endian,
507        strings: StringTable<'data, R>,
508    ) -> Result<&'data [u8]> {
509        strings
510            .get(self.vna_name.get(endian))
511            .read_error("Invalid ELF vna_name")
512    }
513}