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 let mut ident_deserializer = BinaryDeserializerFromBufSafe::new(
23 data,
24 Endianness::Big,
27 );
28 let ident: ElfIdent = ident_deserializer.deserialize()?;
29
30 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 arch: elf_types::Architechture::None,
46 },
47 };
48
49 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 UndefinedSection,
290
291 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>;