object/read/elf/
symbol.rs

1use alloc::fmt;
2use alloc::vec::Vec;
3use core::fmt::Debug;
4use core::slice;
5use core::str;
6
7use crate::elf;
8use crate::endian::{self, Endianness, U32};
9use crate::pod::Pod;
10use crate::read::util::StringTable;
11use crate::read::{
12    self, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, SectionIndex, SymbolFlags,
13    SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection,
14};
15
16use super::{FileHeader, SectionHeader, SectionTable};
17
18/// A table of symbol entries in an ELF file.
19///
20/// Also includes the string table used for the symbol names.
21///
22/// Returned by [`SectionTable::symbols`].
23#[derive(Debug, Clone, Copy)]
24pub struct SymbolTable<'data, Elf: FileHeader, R = &'data [u8]>
25where
26    R: ReadRef<'data>,
27{
28    section: SectionIndex,
29    string_section: SectionIndex,
30    shndx_section: SectionIndex,
31    symbols: &'data [Elf::Sym],
32    strings: StringTable<'data, R>,
33    shndx: &'data [U32<Elf::Endian>],
34}
35
36impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Elf, R> {
37    fn default() -> Self {
38        SymbolTable {
39            section: SectionIndex(0),
40            string_section: SectionIndex(0),
41            shndx_section: SectionIndex(0),
42            symbols: &[],
43            strings: Default::default(),
44            shndx: &[],
45        }
46    }
47}
48
49impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> {
50    /// Parse the given symbol table section.
51    pub fn parse(
52        endian: Elf::Endian,
53        data: R,
54        sections: &SectionTable<'data, Elf, R>,
55        section_index: SectionIndex,
56        section: &Elf::SectionHeader,
57    ) -> read::Result<SymbolTable<'data, Elf, R>> {
58        debug_assert!(
59            section.sh_type(endian) == elf::SHT_DYNSYM
60                || section.sh_type(endian) == elf::SHT_SYMTAB
61        );
62
63        let symbols = section
64            .data_as_array(endian, data)
65            .read_error("Invalid ELF symbol table data")?;
66
67        let link = SectionIndex(section.sh_link(endian) as usize);
68        let strings = sections.strings(endian, data, link)?;
69
70        let mut shndx_section = SectionIndex(0);
71        let mut shndx = &[][..];
72        for (i, s) in sections.enumerate() {
73            if s.sh_type(endian) == elf::SHT_SYMTAB_SHNDX && s.link(endian) == section_index {
74                shndx_section = i;
75                shndx = s
76                    .data_as_array(endian, data)
77                    .read_error("Invalid ELF symtab_shndx data")?;
78            }
79        }
80
81        Ok(SymbolTable {
82            section: section_index,
83            string_section: link,
84            symbols,
85            strings,
86            shndx,
87            shndx_section,
88        })
89    }
90
91    /// Return the section index of this symbol table.
92    #[inline]
93    pub fn section(&self) -> SectionIndex {
94        self.section
95    }
96
97    /// Return the section index of the shndx table.
98    #[inline]
99    pub fn shndx_section(&self) -> SectionIndex {
100        self.shndx_section
101    }
102
103    /// Return the section index of the linked string table.
104    #[inline]
105    pub fn string_section(&self) -> SectionIndex {
106        self.string_section
107    }
108
109    /// Return the string table used for the symbol names.
110    #[inline]
111    pub fn strings(&self) -> StringTable<'data, R> {
112        self.strings
113    }
114
115    /// Return the symbol table.
116    #[inline]
117    pub fn symbols(&self) -> &'data [Elf::Sym] {
118        self.symbols
119    }
120
121    /// Iterate over the symbols.
122    ///
123    /// This includes the null symbol at index 0, which you will usually need to skip.
124    #[inline]
125    pub fn iter(&self) -> slice::Iter<'data, Elf::Sym> {
126        self.symbols.iter()
127    }
128
129    /// Iterate over the symbols and their indices.
130    ///
131    /// This includes the null symbol at index 0, which you will usually need to skip.
132    #[inline]
133    pub fn enumerate(&self) -> impl Iterator<Item = (SymbolIndex, &'data Elf::Sym)> {
134        self.symbols
135            .iter()
136            .enumerate()
137            .map(|(i, sym)| (SymbolIndex(i), sym))
138    }
139
140    /// Return true if the symbol table is empty.
141    #[inline]
142    pub fn is_empty(&self) -> bool {
143        self.symbols.is_empty()
144    }
145
146    /// The number of symbols.
147    #[inline]
148    pub fn len(&self) -> usize {
149        self.symbols.len()
150    }
151
152    /// Get the symbol at the given index.
153    ///
154    /// Returns an error for null entry at index 0.
155    pub fn symbol(&self, index: SymbolIndex) -> read::Result<&'data Elf::Sym> {
156        if index == SymbolIndex(0) {
157            return Err(read::Error("Invalid ELF symbol index"));
158        }
159        self.symbols
160            .get(index.0)
161            .read_error("Invalid ELF symbol index")
162    }
163
164    /// Return the extended section index for the given symbol if present.
165    #[inline]
166    pub fn shndx(&self, endian: Elf::Endian, index: SymbolIndex) -> Option<u32> {
167        self.shndx.get(index.0).map(|x| x.get(endian))
168    }
169
170    /// Return the section index for the given symbol.
171    ///
172    /// This uses the extended section index if present.
173    pub fn symbol_section(
174        &self,
175        endian: Elf::Endian,
176        symbol: &Elf::Sym,
177        index: SymbolIndex,
178    ) -> read::Result<Option<SectionIndex>> {
179        match symbol.st_shndx(endian) {
180            elf::SHN_UNDEF => Ok(None),
181            elf::SHN_XINDEX => {
182                let shndx = self
183                    .shndx(endian, index)
184                    .read_error("Missing ELF symbol extended index")?;
185                if shndx == 0 {
186                    Ok(None)
187                } else {
188                    Ok(Some(SectionIndex(shndx as usize)))
189                }
190            }
191            shndx if shndx < elf::SHN_LORESERVE => Ok(Some(SectionIndex(shndx.into()))),
192            _ => Ok(None),
193        }
194    }
195
196    /// Return the symbol name for the given symbol.
197    pub fn symbol_name(&self, endian: Elf::Endian, symbol: &Elf::Sym) -> read::Result<&'data [u8]> {
198        symbol.name(endian, self.strings)
199    }
200
201    /// Construct a map from addresses to a user-defined map entry.
202    pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Elf::Sym) -> Option<Entry>>(
203        &self,
204        endian: Elf::Endian,
205        f: F,
206    ) -> SymbolMap<Entry> {
207        let mut symbols = Vec::with_capacity(self.symbols.len());
208        for symbol in self.symbols {
209            if !symbol.is_definition(endian) {
210                continue;
211            }
212            if let Some(entry) = f(symbol) {
213                symbols.push(entry);
214            }
215        }
216        SymbolMap::new(symbols)
217    }
218}
219
220/// A symbol table in an [`ElfFile32`](super::ElfFile32).
221pub type ElfSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
222    ElfSymbolTable<'data, 'file, elf::FileHeader32<Endian>, R>;
223/// A symbol table in an [`ElfFile32`](super::ElfFile32).
224pub type ElfSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
225    ElfSymbolTable<'data, 'file, elf::FileHeader64<Endian>, R>;
226
227/// A symbol table in an [`ElfFile`](super::ElfFile).
228#[derive(Debug, Clone, Copy)]
229pub struct ElfSymbolTable<'data, 'file, Elf, R = &'data [u8]>
230where
231    Elf: FileHeader,
232    R: ReadRef<'data>,
233{
234    pub(super) endian: Elf::Endian,
235    pub(super) symbols: &'file SymbolTable<'data, Elf, R>,
236}
237
238impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed
239    for ElfSymbolTable<'data, 'file, Elf, R>
240{
241}
242
243impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbolTable<'data>
244    for ElfSymbolTable<'data, 'file, Elf, R>
245{
246    type Symbol = ElfSymbol<'data, 'file, Elf, R>;
247    type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf, R>;
248
249    fn symbols(&self) -> Self::SymbolIterator {
250        ElfSymbolIterator::new(self.endian, self.symbols)
251    }
252
253    fn symbol_by_index(&self, index: SymbolIndex) -> read::Result<Self::Symbol> {
254        let symbol = self.symbols.symbol(index)?;
255        Ok(ElfSymbol {
256            endian: self.endian,
257            symbols: self.symbols,
258            index,
259            symbol,
260        })
261    }
262}
263
264/// An iterator for the symbols in an [`ElfFile32`](super::ElfFile32).
265pub type ElfSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
266    ElfSymbolIterator<'data, 'file, elf::FileHeader32<Endian>, R>;
267/// An iterator for the symbols in an [`ElfFile64`](super::ElfFile64).
268pub type ElfSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
269    ElfSymbolIterator<'data, 'file, elf::FileHeader64<Endian>, R>;
270
271/// An iterator for the symbols in an [`ElfFile`](super::ElfFile).
272pub struct ElfSymbolIterator<'data, 'file, Elf, R = &'data [u8]>
273where
274    Elf: FileHeader,
275    R: ReadRef<'data>,
276{
277    endian: Elf::Endian,
278    symbols: &'file SymbolTable<'data, Elf, R>,
279    index: SymbolIndex,
280}
281
282impl<'data, 'file, Elf, R> ElfSymbolIterator<'data, 'file, Elf, R>
283where
284    Elf: FileHeader,
285    R: ReadRef<'data>,
286{
287    pub(super) fn new(endian: Elf::Endian, symbols: &'file SymbolTable<'data, Elf, R>) -> Self {
288        ElfSymbolIterator {
289            endian,
290            symbols,
291            index: SymbolIndex(1),
292        }
293    }
294}
295
296impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> fmt::Debug
297    for ElfSymbolIterator<'data, 'file, Elf, R>
298{
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        f.debug_struct("ElfSymbolIterator").finish()
301    }
302}
303
304impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> Iterator
305    for ElfSymbolIterator<'data, 'file, Elf, R>
306{
307    type Item = ElfSymbol<'data, 'file, Elf, R>;
308
309    fn next(&mut self) -> Option<Self::Item> {
310        let index = self.index;
311        let symbol = self.symbols.symbols.get(index.0)?;
312        self.index.0 += 1;
313        Some(ElfSymbol {
314            endian: self.endian,
315            symbols: self.symbols,
316            index,
317            symbol,
318        })
319    }
320}
321
322/// A symbol in an [`ElfFile32`](super::ElfFile32).
323pub type ElfSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
324    ElfSymbol<'data, 'file, elf::FileHeader32<Endian>, R>;
325/// A symbol in an [`ElfFile64`](super::ElfFile64).
326pub type ElfSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
327    ElfSymbol<'data, 'file, elf::FileHeader64<Endian>, R>;
328
329/// A symbol in an [`ElfFile`](super::ElfFile).
330///
331/// Most functionality is provided by the [`ObjectSymbol`] trait implementation.
332#[derive(Debug, Clone, Copy)]
333pub struct ElfSymbol<'data, 'file, Elf, R = &'data [u8]>
334where
335    Elf: FileHeader,
336    R: ReadRef<'data>,
337{
338    pub(super) endian: Elf::Endian,
339    pub(super) symbols: &'file SymbolTable<'data, Elf, R>,
340    pub(super) index: SymbolIndex,
341    pub(super) symbol: &'data Elf::Sym,
342}
343
344impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSymbol<'data, 'file, Elf, R> {
345    /// Get the endianness of the ELF file.
346    pub fn endian(&self) -> Elf::Endian {
347        self.endian
348    }
349
350    /// Return a reference to the raw symbol structure.
351    #[inline]
352    #[deprecated(note = "Use `elf_symbol` instead")]
353    pub fn raw_symbol(&self) -> &'data Elf::Sym {
354        self.symbol
355    }
356
357    /// Get the raw ELF symbol structure.
358    pub fn elf_symbol(&self) -> &'data Elf::Sym {
359        self.symbol
360    }
361}
362
363impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed
364    for ElfSymbol<'data, 'file, Elf, R>
365{
366}
367
368impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data>
369    for ElfSymbol<'data, 'file, Elf, R>
370{
371    #[inline]
372    fn index(&self) -> SymbolIndex {
373        self.index
374    }
375
376    fn name_bytes(&self) -> read::Result<&'data [u8]> {
377        self.symbol.name(self.endian, self.symbols.strings())
378    }
379
380    fn name(&self) -> read::Result<&'data str> {
381        let name = self.name_bytes()?;
382        str::from_utf8(name)
383            .ok()
384            .read_error("Non UTF-8 ELF symbol name")
385    }
386
387    #[inline]
388    fn address(&self) -> u64 {
389        self.symbol.st_value(self.endian).into()
390    }
391
392    #[inline]
393    fn size(&self) -> u64 {
394        self.symbol.st_size(self.endian).into()
395    }
396
397    fn kind(&self) -> SymbolKind {
398        match self.symbol.st_type() {
399            elf::STT_NOTYPE => SymbolKind::Unknown,
400            elf::STT_OBJECT | elf::STT_COMMON => SymbolKind::Data,
401            elf::STT_FUNC | elf::STT_GNU_IFUNC => SymbolKind::Text,
402            elf::STT_SECTION => SymbolKind::Section,
403            elf::STT_FILE => SymbolKind::File,
404            elf::STT_TLS => SymbolKind::Tls,
405            _ => SymbolKind::Unknown,
406        }
407    }
408
409    fn section(&self) -> SymbolSection {
410        match self.symbol.st_shndx(self.endian) {
411            elf::SHN_UNDEF => SymbolSection::Undefined,
412            elf::SHN_ABS => {
413                if self.symbol.st_type() == elf::STT_FILE {
414                    SymbolSection::None
415                } else {
416                    SymbolSection::Absolute
417                }
418            }
419            elf::SHN_COMMON => SymbolSection::Common,
420            elf::SHN_XINDEX => match self.symbols.shndx(self.endian, self.index) {
421                Some(0) => SymbolSection::None,
422                Some(index) => SymbolSection::Section(SectionIndex(index as usize)),
423                None => SymbolSection::Unknown,
424            },
425            index if index < elf::SHN_LORESERVE => {
426                SymbolSection::Section(SectionIndex(index as usize))
427            }
428            _ => SymbolSection::Unknown,
429        }
430    }
431
432    #[inline]
433    fn is_undefined(&self) -> bool {
434        self.symbol.is_undefined(self.endian)
435    }
436
437    #[inline]
438    fn is_definition(&self) -> bool {
439        self.symbol.is_definition(self.endian)
440    }
441
442    #[inline]
443    fn is_common(&self) -> bool {
444        self.symbol.is_common(self.endian)
445    }
446
447    #[inline]
448    fn is_weak(&self) -> bool {
449        self.symbol.is_weak()
450    }
451
452    fn scope(&self) -> SymbolScope {
453        if self.symbol.st_shndx(self.endian) == elf::SHN_UNDEF {
454            SymbolScope::Unknown
455        } else {
456            match self.symbol.st_bind() {
457                elf::STB_LOCAL => SymbolScope::Compilation,
458                elf::STB_GLOBAL | elf::STB_WEAK => {
459                    if self.symbol.st_visibility() == elf::STV_HIDDEN {
460                        SymbolScope::Linkage
461                    } else {
462                        SymbolScope::Dynamic
463                    }
464                }
465                _ => SymbolScope::Unknown,
466            }
467        }
468    }
469
470    #[inline]
471    fn is_global(&self) -> bool {
472        !self.symbol.is_local()
473    }
474
475    #[inline]
476    fn is_local(&self) -> bool {
477        self.symbol.is_local()
478    }
479
480    #[inline]
481    fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> {
482        SymbolFlags::Elf {
483            st_info: self.symbol.st_info(),
484            st_other: self.symbol.st_other(),
485        }
486    }
487}
488
489/// A trait for generic access to [`elf::Sym32`] and [`elf::Sym64`].
490#[allow(missing_docs)]
491pub trait Sym: Debug + Pod {
492    type Word: Into<u64>;
493    type Endian: endian::Endian;
494
495    fn st_name(&self, endian: Self::Endian) -> u32;
496    fn st_info(&self) -> u8;
497    fn st_bind(&self) -> u8;
498    fn st_type(&self) -> u8;
499    fn st_other(&self) -> u8;
500    fn st_visibility(&self) -> u8;
501    fn st_shndx(&self, endian: Self::Endian) -> u16;
502    fn st_value(&self, endian: Self::Endian) -> Self::Word;
503    fn st_size(&self, endian: Self::Endian) -> Self::Word;
504
505    /// Parse the symbol name from the string table.
506    fn name<'data, R: ReadRef<'data>>(
507        &self,
508        endian: Self::Endian,
509        strings: StringTable<'data, R>,
510    ) -> read::Result<&'data [u8]> {
511        strings
512            .get(self.st_name(endian))
513            .read_error("Invalid ELF symbol name offset")
514    }
515
516    /// Return true if the symbol section is `SHN_UNDEF`.
517    #[inline]
518    fn is_undefined(&self, endian: Self::Endian) -> bool {
519        self.st_shndx(endian) == elf::SHN_UNDEF
520    }
521
522    /// Return true if the symbol is a definition of a function or data object.
523    fn is_definition(&self, endian: Self::Endian) -> bool {
524        let shndx = self.st_shndx(endian);
525        if shndx == elf::SHN_UNDEF || (shndx >= elf::SHN_LORESERVE && shndx != elf::SHN_XINDEX) {
526            return false;
527        }
528        match self.st_type() {
529            elf::STT_NOTYPE => self.st_size(endian).into() != 0,
530            elf::STT_FUNC | elf::STT_OBJECT => true,
531            _ => false,
532        }
533    }
534
535    /// Return true if the symbol section is `SHN_COMMON`.
536    fn is_common(&self, endian: Self::Endian) -> bool {
537        self.st_shndx(endian) == elf::SHN_COMMON
538    }
539
540    /// Return true if the symbol section is `SHN_ABS`.
541    fn is_absolute(&self, endian: Self::Endian) -> bool {
542        self.st_shndx(endian) == elf::SHN_ABS
543    }
544
545    /// Return true if the symbol binding is `STB_LOCAL`.
546    fn is_local(&self) -> bool {
547        self.st_bind() == elf::STB_LOCAL
548    }
549
550    /// Return true if the symbol binding is `STB_WEAK`.
551    fn is_weak(&self) -> bool {
552        self.st_bind() == elf::STB_WEAK
553    }
554}
555
556impl<Endian: endian::Endian> Sym for elf::Sym32<Endian> {
557    type Word = u32;
558    type Endian = Endian;
559
560    #[inline]
561    fn st_name(&self, endian: Self::Endian) -> u32 {
562        self.st_name.get(endian)
563    }
564
565    #[inline]
566    fn st_info(&self) -> u8 {
567        self.st_info
568    }
569
570    #[inline]
571    fn st_bind(&self) -> u8 {
572        self.st_bind()
573    }
574
575    #[inline]
576    fn st_type(&self) -> u8 {
577        self.st_type()
578    }
579
580    #[inline]
581    fn st_other(&self) -> u8 {
582        self.st_other
583    }
584
585    #[inline]
586    fn st_visibility(&self) -> u8 {
587        self.st_visibility()
588    }
589
590    #[inline]
591    fn st_shndx(&self, endian: Self::Endian) -> u16 {
592        self.st_shndx.get(endian)
593    }
594
595    #[inline]
596    fn st_value(&self, endian: Self::Endian) -> Self::Word {
597        self.st_value.get(endian)
598    }
599
600    #[inline]
601    fn st_size(&self, endian: Self::Endian) -> Self::Word {
602        self.st_size.get(endian)
603    }
604}
605
606impl<Endian: endian::Endian> Sym for elf::Sym64<Endian> {
607    type Word = u64;
608    type Endian = Endian;
609
610    #[inline]
611    fn st_name(&self, endian: Self::Endian) -> u32 {
612        self.st_name.get(endian)
613    }
614
615    #[inline]
616    fn st_info(&self) -> u8 {
617        self.st_info
618    }
619
620    #[inline]
621    fn st_bind(&self) -> u8 {
622        self.st_bind()
623    }
624
625    #[inline]
626    fn st_type(&self) -> u8 {
627        self.st_type()
628    }
629
630    #[inline]
631    fn st_other(&self) -> u8 {
632        self.st_other
633    }
634
635    #[inline]
636    fn st_visibility(&self) -> u8 {
637        self.st_visibility()
638    }
639
640    #[inline]
641    fn st_shndx(&self, endian: Self::Endian) -> u16 {
642        self.st_shndx.get(endian)
643    }
644
645    #[inline]
646    fn st_value(&self, endian: Self::Endian) -> Self::Word {
647        self.st_value.get(endian)
648    }
649
650    #[inline]
651    fn st_size(&self, endian: Self::Endian) -> Self::Word {
652        self.st_size.get(endian)
653    }
654}