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