Skip to main content

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, self.strings) {
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
440            .is_definition(self.endian, self.symbols.strings())
441    }
442
443    #[inline]
444    fn is_common(&self) -> bool {
445        self.symbol.is_common(self.endian)
446    }
447
448    #[inline]
449    fn is_weak(&self) -> bool {
450        self.symbol.is_weak()
451    }
452
453    fn scope(&self) -> SymbolScope {
454        if self.symbol.st_shndx(self.endian) == elf::SHN_UNDEF {
455            SymbolScope::Unknown
456        } else {
457            match self.symbol.st_bind() {
458                elf::STB_LOCAL => SymbolScope::Compilation,
459                elf::STB_GLOBAL | elf::STB_WEAK => {
460                    if self.symbol.st_visibility() == elf::STV_HIDDEN {
461                        SymbolScope::Linkage
462                    } else {
463                        SymbolScope::Dynamic
464                    }
465                }
466                _ => SymbolScope::Unknown,
467            }
468        }
469    }
470
471    #[inline]
472    fn is_global(&self) -> bool {
473        !self.symbol.is_local()
474    }
475
476    #[inline]
477    fn is_local(&self) -> bool {
478        self.symbol.is_local()
479    }
480
481    #[inline]
482    fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> {
483        SymbolFlags::Elf {
484            st_info: self.symbol.st_info(),
485            st_other: self.symbol.st_other(),
486        }
487    }
488}
489
490/// A trait for generic access to [`elf::Sym32`] and [`elf::Sym64`].
491#[allow(missing_docs)]
492pub trait Sym: Debug + Pod {
493    type Word: Into<u64>;
494    type Endian: endian::Endian;
495
496    fn st_name(&self, endian: Self::Endian) -> u32;
497    fn st_info(&self) -> u8;
498    fn st_bind(&self) -> u8;
499    fn st_type(&self) -> u8;
500    fn st_other(&self) -> u8;
501    fn st_visibility(&self) -> u8;
502    fn st_shndx(&self, endian: Self::Endian) -> u16;
503    fn st_value(&self, endian: Self::Endian) -> Self::Word;
504    fn st_size(&self, endian: Self::Endian) -> Self::Word;
505
506    /// Parse the symbol name from the string table.
507    fn name<'data, R: ReadRef<'data>>(
508        &self,
509        endian: Self::Endian,
510        strings: StringTable<'data, R>,
511    ) -> read::Result<&'data [u8]> {
512        strings
513            .get(self.st_name(endian))
514            .read_error("Invalid ELF symbol name offset")
515    }
516
517    /// Return true if the symbol section is `SHN_UNDEF`.
518    #[inline]
519    fn is_undefined(&self, endian: Self::Endian) -> bool {
520        self.st_shndx(endian) == elf::SHN_UNDEF
521    }
522
523    /// Return true if the symbol is a definition of a function or data object.
524    fn is_definition<'data, R: ReadRef<'data>>(
525        &self,
526        endian: Self::Endian,
527        strings: StringTable<'data, R>,
528    ) -> bool {
529        let shndx = self.st_shndx(endian);
530        if shndx == elf::SHN_UNDEF || (shndx >= elf::SHN_LORESERVE && shndx != elf::SHN_XINDEX) {
531            return false;
532        }
533        match self.st_type() {
534            elf::STT_NOTYPE if self.st_bind() == elf::STB_LOCAL => {
535                // Exclude mapping symbols.
536                let Ok(name) = self.name(endian, strings) else {
537                    // Not worth changing API to return an error for this case.
538                    // Caller is likely to call name() themselves.
539                    return true;
540                };
541                !name.starts_with(b"$")
542            }
543            elf::STT_FUNC | elf::STT_OBJECT | elf::STT_NOTYPE => true,
544            _ => false,
545        }
546    }
547
548    /// Return true if the symbol section is `SHN_COMMON`.
549    fn is_common(&self, endian: Self::Endian) -> bool {
550        self.st_shndx(endian) == elf::SHN_COMMON
551    }
552
553    /// Return true if the symbol section is `SHN_ABS`.
554    fn is_absolute(&self, endian: Self::Endian) -> bool {
555        self.st_shndx(endian) == elf::SHN_ABS
556    }
557
558    /// Return true if the symbol binding is `STB_LOCAL`.
559    fn is_local(&self) -> bool {
560        self.st_bind() == elf::STB_LOCAL
561    }
562
563    /// Return true if the symbol binding is `STB_WEAK`.
564    fn is_weak(&self) -> bool {
565        self.st_bind() == elf::STB_WEAK
566    }
567}
568
569impl<Endian: endian::Endian> Sym for elf::Sym32<Endian> {
570    type Word = u32;
571    type Endian = Endian;
572
573    #[inline]
574    fn st_name(&self, endian: Self::Endian) -> u32 {
575        self.st_name.get(endian)
576    }
577
578    #[inline]
579    fn st_info(&self) -> u8 {
580        self.st_info
581    }
582
583    #[inline]
584    fn st_bind(&self) -> u8 {
585        self.st_bind()
586    }
587
588    #[inline]
589    fn st_type(&self) -> u8 {
590        self.st_type()
591    }
592
593    #[inline]
594    fn st_other(&self) -> u8 {
595        self.st_other
596    }
597
598    #[inline]
599    fn st_visibility(&self) -> u8 {
600        self.st_visibility()
601    }
602
603    #[inline]
604    fn st_shndx(&self, endian: Self::Endian) -> u16 {
605        self.st_shndx.get(endian)
606    }
607
608    #[inline]
609    fn st_value(&self, endian: Self::Endian) -> Self::Word {
610        self.st_value.get(endian)
611    }
612
613    #[inline]
614    fn st_size(&self, endian: Self::Endian) -> Self::Word {
615        self.st_size.get(endian)
616    }
617}
618
619impl<Endian: endian::Endian> Sym for elf::Sym64<Endian> {
620    type Word = u64;
621    type Endian = Endian;
622
623    #[inline]
624    fn st_name(&self, endian: Self::Endian) -> u32 {
625        self.st_name.get(endian)
626    }
627
628    #[inline]
629    fn st_info(&self) -> u8 {
630        self.st_info
631    }
632
633    #[inline]
634    fn st_bind(&self) -> u8 {
635        self.st_bind()
636    }
637
638    #[inline]
639    fn st_type(&self) -> u8 {
640        self.st_type()
641    }
642
643    #[inline]
644    fn st_other(&self) -> u8 {
645        self.st_other
646    }
647
648    #[inline]
649    fn st_visibility(&self) -> u8 {
650        self.st_visibility()
651    }
652
653    #[inline]
654    fn st_shndx(&self, endian: Self::Endian) -> u16 {
655        self.st_shndx.get(endian)
656    }
657
658    #[inline]
659    fn st_value(&self, endian: Self::Endian) -> Self::Word {
660        self.st_value.get(endian)
661    }
662
663    #[inline]
664    fn st_size(&self, endian: Self::Endian) -> Self::Word {
665        self.st_size.get(endian)
666    }
667}