elflib/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3mod elf_types;
4
5use core::marker::PhantomData;
6
7use binary_serde::{BinaryDeserializerFromBufSafe, Endianness};
8pub use elf_types::*;
9use thiserror_no_std::Error;
10
11const SHN_UNDEF: u16 = 0;
12const SHN_ABS: u16 = 0xfff1;
13
14#[derive(Debug, Clone)]
15pub struct ElfParser<'a> {
16    data: DebugIgnore<&'a [u8]>,
17    file_info: ElfFileInfo,
18}
19impl<'a> ElfParser<'a> {
20    pub fn new(data: &'a [u8]) -> Result<Self> {
21        // first extract the ident array to get some information about the binary and to make sure that it is valid
22        let mut ident_deserializer = BinaryDeserializerFromBufSafe::new(
23            data,
24            // endianness doesn't matter here because we only use this deserialize to parse the elf ident, which is basically just
25            // a byte array.
26            Endianness::Big,
27        );
28        let ident: ElfIdent = ident_deserializer.deserialize()?;
29
30        // verify elf magic
31        if ident.header.magic != ELF_MAGIC {
32            return Err(Error::ElfMagicIsMissing);
33        }
34
35        let mut parser = Self {
36            data: data.into(),
37            file_info: ElfFileInfo {
38                endianness: ident.header.endianness.into(),
39                bit_length: ident.header.bit_size,
40                os_abi: ident.header.os_abi,
41
42                // to know the architechture we must first parse the header, but to parse the header we need to have an instance of
43                // a parser, so we use a placeholder here which will be filled later. the architechture shouldn't affect the way
44                // the header is parsed, so it's ok to parse the header while the parser uses a placeholder architechture value.
45                arch: elf_types::Architechture::None,
46            },
47        };
48
49        // read the real architechture of the elf file
50        parser.file_info.arch = *parser.header()?.arch();
51
52        Ok(parser)
53    }
54
55    pub fn data(&self) -> &'a [u8] {
56        &self.data
57    }
58
59    fn deserializer(&self) -> BinaryDeserializerFromBufSafe<'a> {
60        BinaryDeserializerFromBufSafe::new(self.data.0, self.file_info.endianness)
61    }
62
63    fn deserializer_at_offset(&self, offset: usize) -> BinaryDeserializerFromBufSafe<'a> {
64        let mut deserializer = self.deserializer();
65        deserializer.set_position(offset);
66        deserializer
67    }
68
69    pub fn file_info(&self) -> ElfFileInfo {
70        self.file_info
71    }
72
73    pub fn header(&self) -> Result<ElfHeader> {
74        let mut deserializer = self.deserializer();
75        Ok(ElfHeader::deserialize(&mut deserializer, &self, ())?)
76    }
77
78    fn records_table<T: VariantStructBinarySerde<'a>>(
79        &self,
80        start_offset: usize,
81        specified_record_len: u64,
82        records_amount: usize,
83        record_name: &'static str,
84        context: T::Context,
85    ) -> Result<ElfRecordsTable<'a, T>> {
86        let record_len = T::record_len(&self.file_info);
87        if specified_record_len != record_len as u64 {
88            return Err(Error::UnexpectedEntrySize {
89                record_name,
90                expected_size: record_len as u64,
91                specified_size: specified_record_len,
92            });
93        }
94        Ok(ElfRecordsTable {
95            parser: self.clone(),
96            table_start_offset: start_offset,
97            table_records_amount: records_amount,
98            record_name,
99            record_len,
100            context,
101            phantom: PhantomData,
102        })
103    }
104
105    pub fn program_headers(&self) -> Result<ProgramHeaders<'a>> {
106        let hdr = self.header()?;
107        self.records_table(
108            hdr.program_headers_off() as usize,
109            hdr.program_header_entry_size() as u64,
110            hdr.program_headers_amount() as usize,
111            "program header",
112            (),
113        )
114    }
115
116    pub fn section_headers(&self) -> Result<SectionHeaders<'a>> {
117        let hdr = self.header()?;
118        self.records_table(
119            hdr.section_headers_off() as usize,
120            hdr.section_header_entry_size() as u64,
121            hdr.section_headers_amount() as usize,
122            "section header",
123            (),
124        )
125    }
126
127    fn get_offset_range_content(
128        &self,
129        offset: usize,
130        len: usize,
131        offset_range_of_what: &'static str,
132    ) -> Result<&'a [u8]> {
133        let offset_range = offset..offset + len;
134        self.data
135            .get(offset_range.clone())
136            .ok_or(Error::OffsetRangeOutOfBounds {
137                offset_range,
138                file_len: self.data.len(),
139                offset_range_of_what,
140            })
141    }
142
143    pub fn section_names_string_table(&self) -> Result<StringTable<'a>> {
144        let hdr = self.header()?;
145        let section_names_section_index = hdr.section_names_section_index();
146        if section_names_section_index == SHN_UNDEF {
147            return Err(Error::NoSectionNamesStringTable);
148        }
149        match self
150            .section_headers()?
151            .get(section_names_section_index as usize)?
152            .data()?
153        {
154            SectionData::StringTable(string_table) => Ok(string_table),
155            _ => Err(Error::SectionNamesSectionIsNotAStringTable),
156        }
157    }
158}
159
160impl<'a> SectionHeaderRef<'a> {
161    pub fn content(&self) -> Result<&'a [u8]> {
162        self.parser.get_offset_range_content(
163            self.offset() as usize,
164            self.size() as usize,
165            "section header content",
166        )
167    }
168
169    pub fn name(&self) -> Result<&'a str> {
170        self.parser
171            .section_names_string_table()?
172            .string_at_offset(self.name_offset() as usize, "section name")
173    }
174
175    fn generic_rel_section_build(
176        &self,
177        entries: GenericRelEntries<'a>,
178    ) -> Result<GenericRelSection<'a>> {
179        Ok(GenericRelSection {
180            entries,
181            relocated_section: self.parser.section_headers()?.get(self.info() as usize)?,
182            parser: self.parser.clone(),
183            linked_symbol_table_index: self.link() as usize,
184        })
185    }
186
187    fn parse_as_symbol_table(&self) -> Result<SymbolEntries<'a>> {
188        self.parser.records_table(
189            self.offset() as usize,
190            self.entry_size(),
191            (self.size() / self.entry_size()) as usize,
192            "symbol table entry",
193            SymbolRefContext {
194                string_table: match self
195                    .parser
196                    .section_headers()?
197                    .get(self.link() as usize)?
198                    .data()?
199                {
200                    SectionData::StringTable(string_table) => string_table,
201                    _ => {
202                        return Err(Error::LinkedSectionOfSymbolTableSectionIsNotAStringTable {
203                            linked_section_index: self.link() as usize,
204                        })
205                    }
206                },
207            },
208        )
209    }
210
211    pub fn data(&self) -> Result<SectionData<'a>> {
212        match self.ty() {
213            SectionHeaderType::Strtab => Ok(SectionData::StringTable(StringTable {
214                content: self.content()?.into(),
215            })),
216            SectionHeaderType::Rela => Ok(SectionData::RelocationSection(
217                self.generic_rel_section_build(GenericRelEntries::RelaEntries(
218                    self.parser.records_table(
219                        self.offset() as usize,
220                        self.entry_size(),
221                        (self.size() / self.entry_size()) as usize,
222                        "relocation entry with addend",
223                        (),
224                    )?,
225                ))?,
226            )),
227            SectionHeaderType::Rel => Ok(SectionData::RelocationSection(
228                self.generic_rel_section_build(GenericRelEntries::RelEntries(
229                    self.parser.records_table(
230                        self.offset() as usize,
231                        self.entry_size(),
232                        (self.size() / self.entry_size()) as usize,
233                        "relocation entry",
234                        (),
235                    )?,
236                ))?,
237            )),
238            SectionHeaderType::Symtab => {
239                Ok(SectionData::SymbolTable(self.parse_as_symbol_table()?))
240            }
241            SectionHeaderType::Dynsym => Ok(SectionData::DynamicSymbolTable(
242                self.parse_as_symbol_table()?,
243            )),
244            _ => Ok(SectionData::UnknownSectionType),
245        }
246    }
247}
248
249#[derive(Debug, Clone)]
250pub enum SectionData<'a> {
251    StringTable(StringTable<'a>),
252    SymbolTable(SymbolEntries<'a>),
253    DynamicSymbolTable(SymbolEntries<'a>),
254    RelocationSection(GenericRelSection<'a>),
255    UnknownSectionType,
256}
257
258pub type SymbolEntries<'a> = ElfRecordsTable<'a, SymbolRef<'a>>;
259pub type SymbolEntriesIter<'a> = ElfRecordsTableIter<'a, SymbolRef<'a>>;
260
261impl<'a> SymbolRef<'a> {
262    pub fn name(&self) -> Result<&'a str> {
263        match self.info().ty {
264            elf_types::SymbolType::Section => self
265                .section()?
266                .as_optional_section()
267                .ok_or(Error::SectionSymbolHasNoSectionIndex)?
268                .name(),
269            _ => self
270                .context
271                .string_table
272                .string_at_offset(self.name_index_in_string_table() as usize, "symbol name"),
273        }
274    }
275
276    pub fn section(&self) -> Result<SymbolSection<'a>> {
277        match self.related_section_index() {
278            SHN_UNDEF => Ok(SymbolSection::UndefinedSection),
279            SHN_ABS => Ok(SymbolSection::AbsoluteSymbol),
280            section_index => Ok(SymbolSection::Section(
281                self.parser.section_headers()?.get(section_index as usize)?,
282            )),
283        }
284    }
285}
286
287pub enum SymbolSection<'a> {
288    /// the symbol is not defined relative to any section
289    UndefinedSection,
290
291    /// the symbol is absolute.
292    AbsoluteSymbol,
293
294    Section(SectionHeaderRef<'a>),
295}
296impl<'a> SymbolSection<'a> {
297    pub fn as_optional_section(self) -> Option<SectionHeaderRef<'a>> {
298        match self {
299            SymbolSection::UndefinedSection => None,
300            SymbolSection::AbsoluteSymbol => None,
301            SymbolSection::Section(section) => Some(section),
302        }
303    }
304}
305
306#[derive(Debug, Clone)]
307pub struct GenericRelSection<'a> {
308    parser: ElfParser<'a>,
309    pub entries: GenericRelEntries<'a>,
310    pub linked_symbol_table_index: usize,
311    pub relocated_section: SectionHeaderRef<'a>,
312}
313impl<'a> GenericRelSection<'a> {
314    pub fn linked_symbol_table(&self) -> Result<SymbolEntries<'a>> {
315        match self
316            .parser
317            .section_headers()?
318            .get(self.linked_symbol_table_index)?
319            .data()?
320        {
321            SectionData::SymbolTable(symbols) | SectionData::DynamicSymbolTable(symbols) => {
322                Ok(symbols)
323            }
324            _ => Err(Error::LinkedSectionOfRelocationSectionIsNotASymbolTable {
325                linked_section_index: self.linked_symbol_table_index as usize,
326            }),
327        }
328    }
329}
330
331#[derive(Debug, Clone)]
332pub enum GenericRelEntries<'a> {
333    RelEntries(RelEntries<'a>),
334    RelaEntries(RelaEntries<'a>),
335}
336impl<'a> GenericRelEntries<'a> {
337    pub fn get(&self, index: usize) -> Result<GenericRel> {
338        match self {
339            GenericRelEntries::RelEntries(x) => Ok(x.get(index)?.into()),
340            GenericRelEntries::RelaEntries(x) => Ok(x.get(index)?.into()),
341        }
342    }
343
344    pub fn iter(&self) -> GenericRelEntriesIter<'a> {
345        match self {
346            GenericRelEntries::RelEntries(x) => GenericRelEntriesIter::RelEntriesIter(x.iter()),
347            GenericRelEntries::RelaEntries(x) => GenericRelEntriesIter::RelaEntriesIter(x.iter()),
348        }
349    }
350}
351impl<'a> IntoIterator for GenericRelEntries<'a> {
352    type Item = Result<GenericRel>;
353
354    type IntoIter = GenericRelEntriesIter<'a>;
355
356    fn into_iter(self) -> Self::IntoIter {
357        self.iter()
358    }
359}
360impl<'a, 'r> IntoIterator for &'r GenericRelEntries<'a> {
361    type Item = Result<GenericRel>;
362
363    type IntoIter = GenericRelEntriesIter<'a>;
364
365    fn into_iter(self) -> Self::IntoIter {
366        self.iter()
367    }
368}
369
370#[derive(Debug, Clone)]
371pub enum GenericRelEntriesIter<'a> {
372    RelEntriesIter(RelEntriesIter<'a>),
373    RelaEntriesIter(RelaEntriesIter<'a>),
374}
375impl<'a> Iterator for GenericRelEntriesIter<'a> {
376    type Item = Result<GenericRel>;
377
378    fn next(&mut self) -> Option<Self::Item> {
379        match self {
380            GenericRelEntriesIter::RelEntriesIter(x) => Some(x.next()?.map(|rel| rel.into())),
381            GenericRelEntriesIter::RelaEntriesIter(x) => Some(x.next()?.map(|rel| rel.into())),
382        }
383    }
384}
385
386pub type RelaEntries<'a> = ElfRecordsTable<'a, Rela>;
387pub type RelaEntriesIter<'a> = ElfRecordsTableIter<'a, Rela>;
388
389pub type RelEntries<'a> = ElfRecordsTable<'a, Rel>;
390pub type RelEntriesIter<'a> = ElfRecordsTableIter<'a, Rel>;
391
392#[derive(Debug, Clone)]
393pub struct StringTable<'a> {
394    content: DebugIgnore<&'a [u8]>,
395}
396impl<'a> StringTable<'a> {
397    pub fn string_at_offset(&self, offset: usize, offset_of_what: &'static str) -> Result<&'a str> {
398        let slice = self
399            .content
400            .get(offset..)
401            .ok_or(Error::StringOffsetOutOfBoundsOfStrtab {
402                offset,
403                strtab_len: self.content.len(),
404                offset_of_what,
405            })?;
406        let cstr = core::ffi::CStr::from_bytes_until_nul(slice)
407            .map_err(|_| Error::StringTableNotNullTerminated)?;
408        cstr.to_str().map_err(|_| Error::StringTableInvalidUtf8)
409    }
410}
411
412impl<'a> ProgramHeaderRef<'a> {
413    pub fn content_in_file(&self) -> Result<&'a [u8]> {
414        self.parser.get_offset_range_content(
415            self.offset() as usize,
416            self.size_in_file() as usize,
417            "program header content",
418        )
419    }
420}
421
422pub type ProgramHeaders<'a> = ElfRecordsTable<'a, ProgramHeaderRef<'a>>;
423pub type ProgramHeadersIter<'a> = ElfRecordsTableIter<'a, ProgramHeaderRef<'a>>;
424
425pub type SectionHeaders<'a> = ElfRecordsTable<'a, SectionHeaderRef<'a>>;
426pub type SectionHeadersIter<'a> = ElfRecordsTableIter<'a, SectionHeaderRef<'a>>;
427
428#[derive(Debug, Clone)]
429pub struct ElfRecordsTable<'a, T: VariantStructBinarySerde<'a>> {
430    parser: ElfParser<'a>,
431    table_start_offset: usize,
432    table_records_amount: usize,
433    record_name: &'static str,
434    record_len: usize,
435    phantom: PhantomData<T>,
436    context: T::Context,
437}
438impl<'a, T: VariantStructBinarySerde<'a>> ElfRecordsTable<'a, T> {
439    pub fn len(&self) -> usize {
440        self.table_records_amount
441    }
442    pub fn get(&self, index: usize) -> Result<T> {
443        if index > self.table_records_amount {
444            return Err(Error::RecordIndexOutOfBounds {
445                record_name: self.record_name,
446                index,
447                records_amount: self.table_records_amount,
448            });
449        }
450        let mut deserializer = self
451            .parser
452            .deserializer_at_offset(self.table_start_offset + self.record_len * index);
453        Ok(T::deserialize(
454            &mut deserializer,
455            &self.parser,
456            self.context.clone(),
457        )?)
458    }
459
460    pub fn iter(&self) -> ElfRecordsTableIter<'a, T> {
461        ElfRecordsTableIter {
462            parser: self.parser.clone(),
463            table_records_amount: self.table_records_amount,
464            cur_record_index: 0,
465            deserializer: self
466                .parser
467                .deserializer_at_offset(self.table_start_offset)
468                .into(),
469            context: self.context.clone(),
470            phantom: PhantomData,
471        }
472    }
473}
474impl<'a, T: VariantStructBinarySerde<'a>> IntoIterator for ElfRecordsTable<'a, T> {
475    type Item = Result<T>;
476
477    type IntoIter = ElfRecordsTableIter<'a, T>;
478
479    fn into_iter(self) -> Self::IntoIter {
480        self.iter()
481    }
482}
483
484impl<'r, 'a, T: VariantStructBinarySerde<'a>> IntoIterator for &'r ElfRecordsTable<'a, T> {
485    type Item = Result<T>;
486
487    type IntoIter = ElfRecordsTableIter<'a, T>;
488
489    fn into_iter(self) -> Self::IntoIter {
490        self.iter()
491    }
492}
493
494#[derive(Debug, Clone)]
495pub struct ElfRecordsTableIter<'a, T: VariantStructBinarySerde<'a>> {
496    parser: ElfParser<'a>,
497    table_records_amount: usize,
498    cur_record_index: usize,
499    deserializer: DebugIgnore<BinaryDeserializerFromBufSafe<'a>>,
500    context: T::Context,
501    phantom: PhantomData<T>,
502}
503impl<'a, T: VariantStructBinarySerde<'a>> Iterator for ElfRecordsTableIter<'a, T> {
504    type Item = Result<T>;
505
506    fn next(&mut self) -> Option<Self::Item> {
507        if self.cur_record_index >= self.table_records_amount {
508            return None;
509        }
510        self.cur_record_index += 1;
511        Some(
512            T::deserialize(&mut self.deserializer, &self.parser, self.context.clone())
513                .map_err(|e| e.into()),
514        )
515    }
516}
517
518pub trait VariantStructBinarySerde<'a>: Sized {
519    type Context: Clone;
520    fn deserialize(
521        deserializer: &mut BinaryDeserializerFromBufSafe<'a>,
522        parser: &ElfParser<'a>,
523        context: Self::Context,
524    ) -> core::result::Result<Self, binary_serde::BinarySerdeBufSafeError>;
525    fn serialize(&self, buf: &mut [u8], endianness: Endianness);
526    fn record_len(file_info: &ElfFileInfo) -> usize;
527}
528
529#[derive(Debug, Error)]
530pub enum Error {
531    #[error("failed to deserialize binary data from elf file content")]
532    BinaryDeserializeError(
533        #[from]
534        #[source]
535        binary_serde::BinarySerdeBufSafeError,
536    ),
537
538    #[error("elf magic is missing")]
539    ElfMagicIsMissing,
540
541    #[error(
542        "{record_name} {index} is out of bounds of {record_name} table with {records_amount} {record_name}s"
543    )]
544    RecordIndexOutOfBounds {
545        record_name: &'static str,
546        index: usize,
547        records_amount: usize,
548    },
549
550    #[error(
551        "file offset range {offset_range:?} of {offset_range_of_what} is out of bounds of file with length {file_len}"
552    )]
553    OffsetRangeOutOfBounds {
554        offset_range: core::ops::Range<usize>,
555        file_len: usize,
556        offset_range_of_what: &'static str,
557    },
558
559    #[error("string offset {offset} of {offset_of_what} is out of bounds of string table with length {strtab_len}")]
560    StringOffsetOutOfBoundsOfStrtab {
561        offset: usize,
562        strtab_len: usize,
563        offset_of_what: &'static str,
564    },
565
566    #[error("string table is not null terminated")]
567    StringTableNotNullTerminated,
568
569    #[error("string from string table is not valid utf8")]
570    StringTableInvalidUtf8,
571
572    #[error("elf has no section names string table")]
573    NoSectionNamesStringTable,
574
575    #[error("section names section is not a string table")]
576    SectionNamesSectionIsNotAStringTable,
577
578    #[error("the size of a {record_name} specified in the elf file is {specified_size} but the expected size is {expected_size}")]
579    UnexpectedEntrySize {
580        record_name: &'static str,
581        expected_size: u64,
582        specified_size: u64,
583    },
584
585    #[error("section with index {linked_section_index} is sepcified as the linked section of a symbol table section but it is not a string table")]
586    LinkedSectionOfSymbolTableSectionIsNotAStringTable { linked_section_index: usize },
587
588    #[error("section with index {linked_section_index} is sepcified as the linked section of a relocation section but it is not a symbol table")]
589    LinkedSectionOfRelocationSectionIsNotASymbolTable { linked_section_index: usize },
590
591    #[error("symbol of type section has no section index")]
592    SectionSymbolHasNoSectionIndex,
593}
594
595pub type Result<T> = core::result::Result<T, Error>;