Skip to main content

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