Skip to main content

solana_sbpf/elf_parser/
mod.rs

1//! Dependency-less 64 bit ELF parser
2
3pub mod consts;
4pub mod types;
5
6use std::{fmt, mem, ops::Range, slice};
7
8use crate::{ArithmeticOverflow, ErrCheckedArithmetic};
9use {consts::*, types::*};
10
11/// Maximum length of section name allowed.
12pub const SECTION_NAME_LENGTH_MAXIMUM: usize = 16;
13const SYMBOL_NAME_LENGTH_MAXIMUM: usize = 64;
14
15/// Error definitions
16#[derive(Debug, PartialEq, Eq, thiserror::Error)]
17pub enum ElfParserError {
18    /// ELF file header is inconsistent or unsupported
19    #[error("invalid file header")]
20    InvalidFileHeader,
21    /// Program header is inconsistent or unsupported
22    #[error("invalid program header")]
23    InvalidProgramHeader,
24    /// Section header is inconsistent or unsupported
25    #[error("invalid section header")]
26    InvalidSectionHeader,
27    /// Section or symbol name is not UTF8 or too long
28    #[error("invalid string")]
29    InvalidString,
30    /// Section or symbol name is too long
31    #[error("Section or symbol name `{0}` is longer than `{1}` bytes")]
32    StringTooLong(String, usize),
33    /// An index or memory range does exceed its boundaries
34    #[error("value out of bounds")]
35    OutOfBounds,
36    /// The size isn't valid
37    #[error("invalid size")]
38    InvalidSize,
39    /// Headers, tables or sections do overlap in the file
40    #[error("values overlap")]
41    Overlap,
42    /// Sections are not sorted in ascending order
43    #[error("sections not in ascending order")]
44    SectionNotInOrder,
45    /// No section name string table present in the file
46    #[error("no section name string table found")]
47    NoSectionNameStringTable,
48    /// Invalid .dynamic section table
49    #[error("invalid dynamic section table")]
50    InvalidDynamicSectionTable,
51    /// Invalid relocation table
52    #[error("invalid relocation table")]
53    InvalidRelocationTable,
54    /// Invalid alignment
55    #[error("invalid alignment")]
56    InvalidAlignment,
57    /// No string table
58    #[error("no string table")]
59    NoStringTable,
60    /// No dynamic string table
61    #[error("no dynamic string table")]
62    NoDynamicStringTable,
63}
64
65impl Elf64Phdr {
66    /// Returns the byte range the section spans in the file.
67    pub fn file_range(&self) -> Option<Range<usize>> {
68        (self.p_type == PT_LOAD).then(|| {
69            let offset = self.p_offset as usize;
70            offset..offset.saturating_add(self.p_filesz as usize)
71        })
72    }
73
74    /// Returns the segment virtual address range.
75    pub fn vm_range(&self) -> Range<Elf64Addr> {
76        let addr = self.p_vaddr;
77        addr..addr.saturating_add(self.p_memsz)
78    }
79}
80
81impl Elf64Shdr {
82    /// Returns whether the section is writable.
83    pub fn is_writable(&self) -> bool {
84        self.sh_flags & (SHF_ALLOC | SHF_WRITE) == SHF_ALLOC | SHF_WRITE
85    }
86
87    /// Returns the byte range the section spans in the file.
88    pub fn file_range(&self) -> Option<Range<usize>> {
89        (self.sh_type != SHT_NOBITS).then(|| {
90            let offset = self.sh_offset as usize;
91            offset..offset.saturating_add(self.sh_size as usize)
92        })
93    }
94
95    /// Returns the virtual address range.
96    pub fn vm_range(&self) -> Range<Elf64Addr> {
97        self.sh_addr..self.sh_addr.saturating_add(self.sh_size)
98    }
99}
100
101impl Elf64Sym {
102    /// Returns whether the symbol is a function.
103    pub fn is_function(&self) -> bool {
104        (self.st_info & 0xF) == STT_FUNC
105    }
106}
107
108impl Elf64Rel {
109    /// Returns the relocation type.
110    pub fn r_type(&self) -> Elf64Word {
111        (self.r_info & 0xFFFFFFFF) as Elf64Word
112    }
113
114    /// Returns the symbol index.
115    pub fn r_sym(&self) -> Elf64Word {
116        self.r_info.checked_shr(32).unwrap_or(0) as Elf64Word
117    }
118}
119
120fn check_that_there_is_no_overlap(
121    range_a: &Range<usize>,
122    range_b: &Range<usize>,
123) -> Result<(), ElfParserError> {
124    if range_a.end <= range_b.start || range_b.end <= range_a.start {
125        Ok(())
126    } else {
127        Err(ElfParserError::Overlap)
128    }
129}
130
131/// The parsed structure of an ELF file
132pub struct Elf64<'a> {
133    elf_bytes: &'a [u8],
134    file_header: &'a Elf64Ehdr,
135    program_header_table: &'a [Elf64Phdr],
136    section_header_table: &'a [Elf64Shdr],
137    section_names_section_header: Option<&'a Elf64Shdr>,
138    symbol_section_header: Option<&'a Elf64Shdr>,
139    symbol_names_section_header: Option<&'a Elf64Shdr>,
140    dynamic_table: [Elf64Xword; DT_NUM],
141    dynamic_relocations_table: Option<&'a [Elf64Rel]>,
142    dynamic_symbol_table: Option<&'a [Elf64Sym]>,
143    dynamic_symbol_names_section_header: Option<&'a Elf64Shdr>,
144}
145
146impl<'a> Elf64<'a> {
147    /// Parse from the given byte slice
148    pub fn parse(elf_bytes: &'a [u8]) -> Result<Self, ElfParserError> {
149        let (file_header_range, file_header) = Self::parse_file_header(elf_bytes)?;
150
151        if file_header.e_ident.ei_mag != ELFMAG
152            || file_header.e_ident.ei_class != ELFCLASS64
153            || file_header.e_ident.ei_data != ELFDATA2LSB
154            || file_header.e_ident.ei_version != EV_CURRENT as u8
155            || file_header.e_version != EV_CURRENT
156            || file_header.e_ehsize != mem::size_of::<Elf64Ehdr>() as u16
157            || file_header.e_phentsize != mem::size_of::<Elf64Phdr>() as u16
158            || file_header.e_shentsize != mem::size_of::<Elf64Shdr>() as u16
159            || file_header.e_shstrndx >= file_header.e_shnum
160        {
161            return Err(ElfParserError::InvalidFileHeader);
162        }
163
164        let (program_header_table_range, program_header_table) =
165            Self::parse_program_header_table(elf_bytes, file_header_range.clone(), file_header)?;
166
167        let (section_header_table_range, section_header_table) = Self::parse_section_header_table(
168            elf_bytes,
169            file_header_range.clone(),
170            file_header,
171            program_header_table_range.clone(),
172        )?;
173
174        section_header_table
175            .first()
176            .filter(|section_header| section_header.sh_type == SHT_NULL)
177            .ok_or(ElfParserError::InvalidSectionHeader)?;
178
179        let mut vaddr = 0;
180        for program_header in program_header_table {
181            if program_header.p_type != PT_LOAD {
182                continue;
183            }
184            // program headers must be ascending
185            if program_header.p_vaddr < vaddr {
186                return Err(ElfParserError::InvalidProgramHeader);
187            }
188            if program_header
189                .p_offset
190                .err_checked_add(program_header.p_filesz)? as usize
191                > elf_bytes.len()
192            {
193                return Err(ElfParserError::OutOfBounds);
194            }
195            vaddr = program_header.p_vaddr;
196        }
197
198        let mut offset = 0usize;
199        for section_header in section_header_table.iter() {
200            if section_header.sh_type == SHT_NOBITS {
201                continue;
202            }
203            let section_range = section_header.sh_offset as usize
204                ..(section_header.sh_offset as usize)
205                    .err_checked_add(section_header.sh_size as usize)?;
206            check_that_there_is_no_overlap(&section_range, &file_header_range)?;
207            check_that_there_is_no_overlap(&section_range, &program_header_table_range)?;
208            check_that_there_is_no_overlap(&section_range, &section_header_table_range)?;
209            if section_range.start < offset {
210                return Err(ElfParserError::SectionNotInOrder);
211            }
212            offset = section_range.end;
213            if offset > elf_bytes.len() {
214                return Err(ElfParserError::OutOfBounds);
215            }
216        }
217
218        let section_names_section_header = (file_header.e_shstrndx != SHN_UNDEF)
219            .then(|| {
220                section_header_table
221                    .get(file_header.e_shstrndx as usize)
222                    .ok_or(ElfParserError::OutOfBounds)
223            })
224            .transpose()?;
225
226        let mut parser = Self {
227            elf_bytes,
228            file_header,
229            program_header_table,
230            section_header_table,
231            section_names_section_header,
232            symbol_section_header: None,
233            symbol_names_section_header: None,
234            dynamic_table: [0; DT_NUM],
235            dynamic_relocations_table: None,
236            dynamic_symbol_table: None,
237            dynamic_symbol_names_section_header: None,
238        };
239
240        parser.parse_sections()?;
241        parser.parse_dynamic()?;
242
243        Ok(parser)
244    }
245
246    /// Returns the file header.
247    pub fn file_header(&self) -> &Elf64Ehdr {
248        self.file_header
249    }
250
251    /// Returns the program header table.
252    pub fn program_header_table(&self) -> &[Elf64Phdr] {
253        self.program_header_table
254    }
255
256    /// Returns the section header table.
257    pub fn section_header_table(&self) -> &[Elf64Shdr] {
258        self.section_header_table
259    }
260
261    /// Returns the dynamic symbol table.
262    pub fn dynamic_symbol_table(&self) -> Option<&[Elf64Sym]> {
263        self.dynamic_symbol_table
264    }
265
266    /// Returns the dynamic relocations table.
267    pub fn dynamic_relocations_table(&self) -> Option<&[Elf64Rel]> {
268        self.dynamic_relocations_table
269    }
270
271    /// Parses the file header.
272    pub fn parse_file_header(
273        elf_bytes: &'a [u8],
274    ) -> Result<(std::ops::Range<usize>, &'a Elf64Ehdr), ElfParserError> {
275        let file_header_range = 0..mem::size_of::<Elf64Ehdr>();
276        let file_header_bytes = elf_bytes
277            .get(file_header_range.clone())
278            .ok_or(ElfParserError::OutOfBounds)?;
279        let ptr = file_header_bytes.as_ptr();
280        if (ptr as usize)
281            .checked_rem(mem::align_of::<Elf64Ehdr>())
282            .map(|remaining| remaining != 0)
283            .unwrap_or(true)
284        {
285            return Err(ElfParserError::InvalidAlignment);
286        }
287        let file_header = unsafe { &*ptr.cast::<Elf64Ehdr>() };
288        Ok((file_header_range, file_header))
289    }
290
291    /// Parses the program header table.
292    pub fn parse_program_header_table(
293        elf_bytes: &'a [u8],
294        file_header_range: std::ops::Range<usize>,
295        file_header: &Elf64Ehdr,
296    ) -> Result<(std::ops::Range<usize>, &'a [Elf64Phdr]), ElfParserError> {
297        let program_header_table_range = file_header.e_phoff as usize
298            ..mem::size_of::<Elf64Phdr>()
299                .err_checked_mul(file_header.e_phnum as usize)?
300                .err_checked_add(file_header.e_phoff as usize)?;
301        check_that_there_is_no_overlap(&file_header_range, &program_header_table_range)?;
302        let program_header_table =
303            Self::slice_from_bytes::<Elf64Phdr>(elf_bytes, program_header_table_range.clone())?;
304        Ok((program_header_table_range, program_header_table))
305    }
306
307    /// Parses the section header table.
308    pub fn parse_section_header_table(
309        elf_bytes: &'a [u8],
310        file_header_range: std::ops::Range<usize>,
311        file_header: &Elf64Ehdr,
312        program_header_table_range: std::ops::Range<usize>,
313    ) -> Result<(std::ops::Range<usize>, &'a [Elf64Shdr]), ElfParserError> {
314        let section_header_table_range = file_header.e_shoff as usize
315            ..mem::size_of::<Elf64Shdr>()
316                .err_checked_mul(file_header.e_shnum as usize)?
317                .err_checked_add(file_header.e_shoff as usize)?;
318        check_that_there_is_no_overlap(&file_header_range, &section_header_table_range)?;
319        check_that_there_is_no_overlap(&program_header_table_range, &section_header_table_range)?;
320        let section_header_table =
321            Self::slice_from_bytes::<Elf64Shdr>(elf_bytes, section_header_table_range.clone())?;
322        Ok((section_header_table_range, section_header_table))
323    }
324
325    fn parse_sections(&mut self) -> Result<(), ElfParserError> {
326        macro_rules! section_header_by_name {
327            ($self:expr, $section_header:expr, $section_name:expr,
328             $($name:literal => $field:ident,)*) => {
329                match $section_name {
330                    $($name => {
331                        if $self.$field.is_some() {
332                            return Err(ElfParserError::InvalidSectionHeader);
333                        }
334                        $self.$field = Some($section_header);
335                    })*
336                    _ => {}
337                }
338            }
339        }
340        let section_names_section_header = self
341            .section_names_section_header
342            .ok_or(ElfParserError::NoSectionNameStringTable)?;
343        for section_header in self.section_header_table.iter() {
344            let section_name = Self::get_string_in_section(
345                self.elf_bytes,
346                section_names_section_header,
347                section_header.sh_name,
348                SECTION_NAME_LENGTH_MAXIMUM,
349            )?;
350            section_header_by_name!(
351                self, section_header, section_name,
352                b".symtab" => symbol_section_header,
353                b".strtab" => symbol_names_section_header,
354                b".dynstr" => dynamic_symbol_names_section_header,
355            )
356        }
357
358        Ok(())
359    }
360
361    fn parse_dynamic(&mut self) -> Result<(), ElfParserError> {
362        let mut dynamic_table: Option<&[Elf64Dyn]> = None;
363
364        // try to parse PT_DYNAMIC
365        if let Some(dynamic_program_header) = self
366            .program_header_table
367            .iter()
368            .find(|program_header| program_header.p_type == PT_DYNAMIC)
369        {
370            dynamic_table =
371                Self::slice_from_program_header(self.elf_bytes, dynamic_program_header).ok();
372        }
373
374        // if PT_DYNAMIC does not exist or is invalid (some of our tests have this),
375        // fallback to parsing SHT_DYNAMIC
376        if dynamic_table.is_none() {
377            if let Some(dynamic_section_header) = self
378                .section_header_table
379                .iter()
380                .find(|section_header| section_header.sh_type == SHT_DYNAMIC)
381            {
382                dynamic_table = Some(
383                    Self::slice_from_section_header(self.elf_bytes, dynamic_section_header)
384                        .map_err(|_| ElfParserError::InvalidDynamicSectionTable)?,
385                );
386            }
387        }
388
389        // if there are neither PT_DYNAMIC nor SHT_DYNAMIC, this is a static
390        // file
391        let dynamic_table = match dynamic_table {
392            Some(table) => table,
393            None => return Ok(()),
394        };
395
396        // expand Elf64Dyn entries into self.dynamic_table
397        for dyn_info in dynamic_table {
398            if dyn_info.d_tag == DT_NULL {
399                break;
400            }
401
402            if dyn_info.d_tag as usize >= DT_NUM {
403                // we don't parse any reserved tags
404                continue;
405            }
406            self.dynamic_table[dyn_info.d_tag as usize] = dyn_info.d_val;
407        }
408
409        self.dynamic_relocations_table = self.parse_dynamic_relocations()?;
410        self.dynamic_symbol_table = self.parse_dynamic_symbol_table()?;
411
412        Ok(())
413    }
414
415    fn parse_dynamic_relocations(&mut self) -> Result<Option<&'a [Elf64Rel]>, ElfParserError> {
416        let vaddr = self.dynamic_table[DT_REL as usize];
417        if vaddr == 0 {
418            return Ok(None);
419        }
420
421        if self.dynamic_table[DT_RELENT as usize] as usize != mem::size_of::<Elf64Rel>() {
422            return Err(ElfParserError::InvalidDynamicSectionTable);
423        }
424
425        let size = self.dynamic_table[DT_RELSZ as usize] as usize;
426        if size == 0 {
427            return Err(ElfParserError::InvalidDynamicSectionTable);
428        }
429
430        let offset = if let Some(program_header) = self.program_header_for_vaddr(vaddr)? {
431            vaddr
432                .err_checked_sub(program_header.p_vaddr)?
433                .err_checked_add(program_header.p_offset)?
434        } else {
435            // At least until rust-bpf-sysroot v0.13, we used to generate
436            // invalid dynamic sections where the address of DT_REL was not
437            // contained in any program segment. When loading one of those
438            // files, fallback to relying on section headers.
439            self.section_header_table
440                .iter()
441                .find(|section_header| section_header.sh_addr == vaddr)
442                .ok_or(ElfParserError::InvalidDynamicSectionTable)?
443                .sh_offset
444        } as usize;
445
446        Self::slice_from_bytes(self.elf_bytes, offset..offset.err_checked_add(size)?)
447            .map(Some)
448            .map_err(|_| ElfParserError::InvalidDynamicSectionTable)
449    }
450
451    fn parse_dynamic_symbol_table(&mut self) -> Result<Option<&'a [Elf64Sym]>, ElfParserError> {
452        let vaddr = self.dynamic_table[DT_SYMTAB as usize];
453        if vaddr == 0 {
454            return Ok(None);
455        }
456
457        let dynsym_section_header = self
458            .section_header_table
459            .iter()
460            .find(|section_header| section_header.sh_addr == vaddr)
461            .ok_or(ElfParserError::InvalidDynamicSectionTable)?;
462
463        self.get_symbol_table_of_section(dynsym_section_header)
464            .map(Some)
465    }
466
467    /// Query a single string from a section which is marked as SHT_STRTAB
468    pub fn get_string_in_section(
469        elf_bytes: &'a [u8],
470        section_header: &Elf64Shdr,
471        offset_in_section: Elf64Word,
472        maximum_length: usize,
473    ) -> Result<&'a [u8], ElfParserError> {
474        if section_header.sh_type != SHT_STRTAB {
475            return Err(ElfParserError::InvalidSectionHeader);
476        }
477        let offset_in_file =
478            (section_header.sh_offset as usize).err_checked_add(offset_in_section as usize)?;
479        let string_range = offset_in_file
480            ..(section_header.sh_offset as usize)
481                .err_checked_add(section_header.sh_size as usize)?
482                .min(offset_in_file.err_checked_add(maximum_length)?);
483        let unterminated_string_bytes = elf_bytes
484            .get(string_range)
485            .ok_or(ElfParserError::OutOfBounds)?;
486        unterminated_string_bytes
487            .iter()
488            .position(|byte| *byte == 0x00)
489            .and_then(|string_length| unterminated_string_bytes.get(0..string_length))
490            .ok_or_else(|| {
491                ElfParserError::StringTooLong(
492                    String::from_utf8_lossy(unterminated_string_bytes).to_string(),
493                    maximum_length,
494                )
495            })
496    }
497
498    /// Returns the string corresponding to the given `sh_name`
499    pub fn section_name(&self, sh_name: Elf64Word) -> Result<&'a [u8], ElfParserError> {
500        Self::get_string_in_section(
501            self.elf_bytes,
502            self.section_names_section_header
503                .ok_or(ElfParserError::NoSectionNameStringTable)?,
504            sh_name,
505            SECTION_NAME_LENGTH_MAXIMUM,
506        )
507    }
508
509    /// Returns the name of the `st_name` symbol
510    pub fn symbol_name(&self, st_name: Elf64Word) -> Result<&'a [u8], ElfParserError> {
511        Self::get_string_in_section(
512            self.elf_bytes,
513            self.symbol_names_section_header
514                .ok_or(ElfParserError::NoStringTable)?,
515            st_name,
516            SYMBOL_NAME_LENGTH_MAXIMUM,
517        )
518    }
519
520    /// Returns the symbol table
521    pub fn symbol_table(&self) -> Result<Option<&'a [Elf64Sym]>, ElfParserError> {
522        self.symbol_section_header
523            .map(|section_header| self.get_symbol_table_of_section(section_header))
524            .transpose()
525    }
526
527    /// Returns the name of the `st_name` dynamic symbol
528    pub fn dynamic_symbol_name(&self, st_name: Elf64Word) -> Result<&'a [u8], ElfParserError> {
529        Self::get_string_in_section(
530            self.elf_bytes,
531            self.dynamic_symbol_names_section_header
532                .ok_or(ElfParserError::NoDynamicStringTable)?,
533            st_name,
534            SYMBOL_NAME_LENGTH_MAXIMUM,
535        )
536    }
537
538    /// Returns the symbol table of a section which is marked as SHT_SYMTAB
539    pub fn get_symbol_table_of_section(
540        &self,
541        section_header: &Elf64Shdr,
542    ) -> Result<&'a [Elf64Sym], ElfParserError> {
543        if section_header.sh_type != SHT_SYMTAB && section_header.sh_type != SHT_DYNSYM {
544            return Err(ElfParserError::InvalidSectionHeader);
545        }
546
547        Self::slice_from_section_header(self.elf_bytes, section_header)
548    }
549
550    /// Returns the `&[T]` contained in the data described by the given program
551    /// header
552    pub fn slice_from_program_header<T: 'static>(
553        bytes: &'a [u8],
554        &Elf64Phdr {
555            p_offset, p_filesz, ..
556        }: &Elf64Phdr,
557    ) -> Result<&'a [T], ElfParserError> {
558        Self::slice_from_bytes(
559            bytes,
560            (p_offset as usize)..(p_offset as usize).err_checked_add(p_filesz as usize)?,
561        )
562    }
563
564    /// Returns the `&[T]` contained in the section data described by the given
565    /// section header
566    pub fn slice_from_section_header<T: 'static>(
567        bytes: &'a [u8],
568        &Elf64Shdr {
569            sh_offset, sh_size, ..
570        }: &Elf64Shdr,
571    ) -> Result<&'a [T], ElfParserError> {
572        Self::slice_from_bytes(
573            bytes,
574            (sh_offset as usize)..(sh_offset as usize).err_checked_add(sh_size as usize)?,
575        )
576    }
577
578    /// Returns the `&[T]` contained at `bytes[range]`
579    pub fn slice_from_bytes<T: 'static>(
580        bytes: &[u8],
581        range: Range<usize>,
582    ) -> Result<&[T], ElfParserError> {
583        if range
584            .len()
585            .checked_rem(mem::size_of::<T>())
586            .map(|remainder| remainder != 0)
587            .unwrap_or(true)
588        {
589            return Err(ElfParserError::InvalidSize);
590        }
591
592        let bytes = bytes
593            .get(range.clone())
594            .ok_or(ElfParserError::OutOfBounds)?;
595
596        let ptr = bytes.as_ptr();
597        if (ptr as usize)
598            .checked_rem(mem::align_of::<T>())
599            .map(|remaining| remaining != 0)
600            .unwrap_or(true)
601        {
602            return Err(ElfParserError::InvalidAlignment);
603        }
604
605        Ok(unsafe {
606            slice::from_raw_parts(
607                ptr.cast(),
608                range.len().checked_div(mem::size_of::<T>()).unwrap_or(0),
609            )
610        })
611    }
612
613    fn program_header_for_vaddr(
614        &self,
615        vaddr: Elf64Addr,
616    ) -> Result<Option<&'a Elf64Phdr>, ElfParserError> {
617        for program_header in self.program_header_table.iter() {
618            let Elf64Phdr {
619                p_vaddr, p_memsz, ..
620            } = program_header;
621
622            if (*p_vaddr..p_vaddr.err_checked_add(*p_memsz)?).contains(&vaddr) {
623                return Ok(Some(program_header));
624            }
625        }
626        Ok(None)
627    }
628}
629
630impl fmt::Debug for Elf64<'_> {
631    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
632        writeln!(f, "{:#X?}", self.file_header)?;
633        for program_header in self.program_header_table.iter() {
634            writeln!(f, "{program_header:#X?}")?;
635        }
636        for section_header in self.section_header_table.iter() {
637            let section_name = Self::get_string_in_section(
638                self.elf_bytes,
639                self.section_names_section_header.unwrap(),
640                section_header.sh_name,
641                SECTION_NAME_LENGTH_MAXIMUM,
642            )
643            .and_then(|name| std::str::from_utf8(name).map_err(|_| ElfParserError::InvalidString))
644            .unwrap();
645            writeln!(f, "{section_name}")?;
646            writeln!(f, "{section_header:#X?}")?;
647        }
648        if let Some(section_header) = self.symbol_section_header {
649            let symbol_table = self.get_symbol_table_of_section(section_header).unwrap();
650            writeln!(f, "{symbol_table:#X?}")?;
651            for symbol in symbol_table.iter() {
652                if symbol.st_name != 0 {
653                    let symbol_name = Self::get_string_in_section(
654                        self.elf_bytes,
655                        self.symbol_names_section_header.unwrap(),
656                        symbol.st_name,
657                        SYMBOL_NAME_LENGTH_MAXIMUM,
658                    )
659                    .and_then(|name| {
660                        std::str::from_utf8(name).map_err(|_| ElfParserError::InvalidString)
661                    })
662                    .unwrap();
663                    writeln!(f, "{symbol_name}")?;
664                }
665            }
666        }
667        Ok(())
668    }
669}
670
671impl From<ArithmeticOverflow> for ElfParserError {
672    fn from(_: ArithmeticOverflow) -> ElfParserError {
673        ElfParserError::OutOfBounds
674    }
675}