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