1use bitflags::bitflags;
7
8use num_derive::FromPrimitive;
9use num_traits::cast::FromPrimitive;
10use static_assertions::assert_eq_size;
11use std::{fmt, mem};
12use thiserror::Error;
13use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
14
15#[derive(Error, Debug)]
17pub enum ElfParseError {
18 #[error("Failed to read ELF from VMO: {}", _0)]
19 ReadError(zx::Status),
20 #[error("Parse error: {}", _0)]
21 ParseError(&'static str),
22 #[error("Invalid ELF file header: {}", _0)]
23 InvalidFileHeader(&'static str),
24 #[error("Invalid ELF program header: {}", _0)]
25 InvalidProgramHeader(&'static str),
26 #[error("Multiple ELF program headers of type {} present", _0)]
27 MultipleHeaders(SegmentType),
28}
29
30impl ElfParseError {
31 pub fn as_zx_status(&self) -> zx::Status {
33 match self {
34 ElfParseError::ReadError(s) => *s,
35 ElfParseError::ParseError(_)
38 | ElfParseError::InvalidFileHeader(_)
39 | ElfParseError::InvalidProgramHeader(_) => zx::Status::NOT_FOUND,
40 ElfParseError::MultipleHeaders(_) => zx::Status::NOT_FOUND,
41 }
42 }
43}
44
45trait Validate {
46 fn validate(&self) -> Result<(), ElfParseError>;
47}
48
49#[derive(
51 KnownLayout, FromBytes, IntoBytes, Immutable, Debug, Eq, PartialEq, Default, Clone, Copy,
52)]
53#[repr(C)]
54pub struct ElfIdent {
55 pub magic: [u8; 4],
57 pub class: u8,
59 pub data: u8,
61 pub version: u8,
63 pub osabi: u8,
65 pub abiversion: u8,
67 pub pad: [u8; 7],
69}
70
71#[allow(unused)]
72const EI_NIDENT: usize = 16;
73assert_eq_size!(ElfIdent, [u8; EI_NIDENT]);
74
75#[derive(FromPrimitive, Eq, PartialEq)]
77#[repr(u8)]
78pub enum ElfClass {
79 Unknown = 0,
81 Elf32 = 1,
83 Elf64 = 2,
85}
86
87#[derive(FromPrimitive, Eq, PartialEq)]
89#[repr(u8)]
90pub enum ElfDataEncoding {
91 Unknown = 0,
93 LittleEndian = 1,
95 BigEndian = 2,
97}
98
99#[derive(FromPrimitive, Eq, PartialEq)]
101#[repr(u8)]
102pub enum ElfVersion {
103 Unknown = 0,
105 Current = 1,
107}
108
109impl ElfIdent {
110 pub fn from_vmo(vmo: &zx::Vmo) -> Result<ElfIdent, ElfParseError> {
111 let field = vmo.read_to_object(0).map_err(|s| ElfParseError::ReadError(s))?;
113 Ok(field)
114 }
115
116 pub fn class(&self) -> Result<ElfClass, u8> {
117 ElfClass::from_u8(self.class).ok_or(self.class)
118 }
119
120 pub fn is_arch32(&self) -> bool {
121 self.class() == Ok(ElfClass::Elf32)
122 }
123
124 pub fn data(&self) -> Result<ElfDataEncoding, u8> {
125 ElfDataEncoding::from_u8(self.data).ok_or(self.data)
126 }
127
128 pub fn version(&self) -> Result<ElfVersion, u8> {
129 ElfVersion::from_u8(self.version).ok_or(self.version)
130 }
131}
132
133#[derive(
134 KnownLayout, FromBytes, IntoBytes, Immutable, Debug, Eq, PartialEq, Default, Clone, Copy,
135)]
136#[repr(C)]
137pub struct Elf64FileHeader {
138 pub ident: ElfIdent,
139 pub elf_type: u16,
140 pub machine: u16,
141 pub version: u32,
142 pub entry: usize,
143 pub phoff: usize,
144 pub shoff: usize,
145 pub flags: u32,
146 pub ehsize: u16,
147 pub phentsize: u16,
148 pub phnum: u16,
149 pub shentsize: u16,
150 pub shnum: u16,
151 pub shstrndx: u16,
152}
153
154#[derive(
155 KnownLayout, FromBytes, IntoBytes, Immutable, Debug, Eq, PartialEq, Default, Clone, Copy,
156)]
157#[repr(C)]
158pub struct Elf32FileHeader {
159 pub ident: ElfIdent,
160 pub elf_type: u16,
161 pub machine: u16,
162 pub version: u32,
163 pub entry: u32, pub phoff: u32, pub shoff: u32, pub flags: u32,
167 pub ehsize: u16,
168 pub phentsize: u16,
169 pub phnum: u16,
170 pub shentsize: u16,
171 pub shnum: u16,
172 pub shstrndx: u16,
173}
174
175#[derive(FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
176#[repr(u16)]
177pub enum ElfType {
178 Unknown = 0,
180 Relocatable = 1,
182 Executable = 2,
184 SharedObject = 3,
186 Core = 4,
188}
189
190#[derive(FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
191#[repr(u32)]
192pub enum ElfArchitecture {
193 Unknown = 0,
195 I386 = 3,
197 ARM = 40,
199 X86_64 = 62,
201 AARCH64 = 183,
203 RISCV = 243,
205}
206
207pub const ELF_MAGIC: [u8; 4] = *b"\x7fELF";
208
209#[cfg(target_endian = "little")]
210pub const NATIVE_ENCODING: ElfDataEncoding = ElfDataEncoding::LittleEndian;
211#[cfg(target_endian = "big")]
212pub const NATIVE_ENCODING: ElfDataEncoding = ElfDataEncoding::BigEndian;
213
214#[cfg(target_arch = "x86_64")]
215pub const CURRENT_ARCH: ElfArchitecture = ElfArchitecture::X86_64;
216#[cfg(target_arch = "x86_64")]
217pub const ARCH32_ARCH: ElfArchitecture = ElfArchitecture::Unknown;
218
219#[cfg(target_arch = "aarch64")]
220pub const CURRENT_ARCH: ElfArchitecture = ElfArchitecture::AARCH64;
221#[cfg(target_arch = "aarch64")]
222pub const ARCH32_ARCH: ElfArchitecture = ElfArchitecture::ARM;
223
224#[cfg(target_arch = "riscv64")]
225pub const CURRENT_ARCH: ElfArchitecture = ElfArchitecture::RISCV;
226#[cfg(target_arch = "riscv64")]
227pub const ARCH32_ARCH: ElfArchitecture = ElfArchitecture::Unknown;
228
229impl Elf64FileHeader {
230 pub fn elf_type(&self) -> Result<ElfType, u16> {
231 ElfType::from_u16(self.elf_type).ok_or(self.elf_type)
232 }
233
234 pub fn machine(&self) -> Result<ElfArchitecture, u16> {
235 ElfArchitecture::from_u16(self.machine).ok_or(self.machine)
236 }
237
238 pub fn from_vmo(vmo: &zx::Vmo) -> Result<Box<Elf64FileHeader>, ElfParseError> {
239 let mut header = Box::<Elf64FileHeader>::default();
241 vmo.read(header.as_mut_bytes(), 0).map_err(|s| ElfParseError::ReadError(s))?;
242 header.validate()?;
243 Ok(header)
244 }
245}
246
247impl Elf32FileHeader {
248 pub fn elf_type(&self) -> Result<ElfType, u16> {
249 ElfType::from_u16(self.elf_type).ok_or(self.elf_type)
250 }
251
252 pub fn machine(&self) -> Result<ElfArchitecture, u16> {
253 ElfArchitecture::from_u16(self.machine).ok_or(self.machine)
254 }
255
256 pub fn from_vmo(vmo: &zx::Vmo) -> Result<Box<Elf32FileHeader>, ElfParseError> {
257 let mut header = Box::<Elf32FileHeader>::default();
259 vmo.read(header.as_mut_bytes(), 0).map_err(|s| ElfParseError::ReadError(s))?;
260 header.validate()?;
261 Ok(header)
262 }
263}
264
265impl Validate for Elf64FileHeader {
266 fn validate(&self) -> Result<(), ElfParseError> {
267 if self.ident.magic != ELF_MAGIC {
268 return Err(ElfParseError::InvalidFileHeader("Invalid ELF magic"));
269 }
270 if self.ident.class() != Ok(ElfClass::Elf64) {
271 return Err(ElfParseError::InvalidFileHeader("Invalid ELF class"));
272 }
273 if self.ident.data() != Ok(NATIVE_ENCODING) {
274 return Err(ElfParseError::InvalidFileHeader("Invalid ELF data encoding"));
275 }
276 if self.ident.version() != Ok(ElfVersion::Current) {
277 return Err(ElfParseError::InvalidFileHeader("Invalid ELF version"));
278 }
279 if self.phentsize as usize != mem::size_of::<Elf64ProgramHeader>() {
280 return Err(ElfParseError::InvalidFileHeader("Invalid ELF program header size"));
281 }
282 if self.phnum == std::u16::MAX {
283 return Err(ElfParseError::InvalidFileHeader(
284 "2^16 or more ELF program headers is unsupported",
285 ));
286 }
287 if self.machine() != Ok(CURRENT_ARCH) {
288 return Err(ElfParseError::InvalidFileHeader("Invalid ELF architecture"));
289 }
290 if self.elf_type() != Ok(ElfType::SharedObject)
291 && self.elf_type() != Ok(ElfType::Executable)
292 {
293 return Err(ElfParseError::InvalidFileHeader(
294 "Invalid or unsupported ELF type, only ET_DYN is supported",
295 ));
296 }
297 Ok(())
298 }
299}
300
301impl Validate for Elf32FileHeader {
302 fn validate(&self) -> Result<(), ElfParseError> {
303 if self.ident.magic != ELF_MAGIC {
304 return Err(ElfParseError::InvalidFileHeader("Invalid ELF magic"));
305 }
306 if ARCH32_ARCH == ElfArchitecture::Unknown || !self.ident.is_arch32() {
308 return Err(ElfParseError::InvalidFileHeader("Invalid ELF class"));
309 }
310 if self.ident.data() != Ok(NATIVE_ENCODING) {
311 return Err(ElfParseError::InvalidFileHeader("Invalid ELF data encoding"));
312 }
313 if self.ident.version() != Ok(ElfVersion::Current) {
314 return Err(ElfParseError::InvalidFileHeader("Invalid ELF version"));
315 }
316 if self.phentsize as usize != mem::size_of::<Elf32ProgramHeader>() {
317 return Err(ElfParseError::InvalidFileHeader("Invalid ELF program header size"));
318 }
319 if self.phnum == std::u16::MAX {
320 return Err(ElfParseError::InvalidFileHeader(
321 "2^16 or more ELF program headers is unsupported",
322 ));
323 }
324 if self.machine() != Ok(ARCH32_ARCH) {
325 return Err(ElfParseError::InvalidFileHeader("Invalid ELF architecture"));
326 }
327 if self.elf_type() != Ok(ElfType::SharedObject)
328 && self.elf_type() != Ok(ElfType::Executable)
329 {
330 return Err(ElfParseError::InvalidFileHeader(
331 "Invalid or unsupported ELF type, only ET_DYN is supported",
332 ));
333 }
334 Ok(())
335 }
336}
337
338impl Into<Box<Elf64FileHeader>> for Box<Elf32FileHeader> {
339 fn into(self) -> Box<Elf64FileHeader> {
340 Box::new(Elf64FileHeader {
342 ident: self.ident as ElfIdent,
343 elf_type: self.elf_type as u16,
344 machine: self.machine as u16,
345 version: self.version as u32,
346 entry: self.entry as usize,
347 phoff: self.phoff as usize,
348 shoff: self.shoff as usize,
349 flags: self.flags as u32,
350 ehsize: self.ehsize as u16,
351 phentsize: self.phentsize as u16,
352 phnum: self.phnum as u16,
353 shentsize: self.shentsize as u16,
354 shnum: self.shnum as u16,
355 shstrndx: self.shstrndx as u16,
356 })
357 }
358}
359
360#[derive(
361 KnownLayout, FromBytes, Immutable, IntoBytes, Debug, Eq, PartialEq, Default, Clone, Copy,
362)]
363#[repr(C)]
364pub struct Elf64ProgramHeader {
365 pub segment_type: u32,
366 pub flags: u32,
367 pub offset: usize,
368 pub vaddr: usize,
369 pub paddr: usize,
370 pub filesz: u64,
371 pub memsz: u64,
372 pub align: u64,
373}
374
375#[derive(
376 KnownLayout, FromBytes, Immutable, IntoBytes, Debug, Eq, PartialEq, Default, Clone, Copy,
377)]
378#[repr(C)]
379pub struct Elf32ProgramHeader {
380 pub segment_type: u32,
381 pub offset: u32,
382 pub vaddr: u32,
383 pub paddr: u32,
384 pub filesz: u32,
385 pub memsz: u32,
386 pub flags: u32,
387 pub align: u32,
388}
389
390#[derive(FromPrimitive, Debug, Eq, PartialEq)]
391#[repr(u64)]
392pub enum Elf64DynTag {
393 Null = 0,
394 Needed = 1,
395 Pltrelsz = 2,
396 Pltgot = 3,
397 Hash = 4,
398 Strtab = 5,
399 Symtab = 6,
400 Rela = 7,
401 Relasz = 8,
402 Relaent = 9,
403 Strsz = 10,
404 Syment = 11,
405 Init = 12,
406 Fini = 13,
407 Soname = 14,
408 Rpath = 15,
409 Symbolic = 16,
410 Rel = 17,
411 Relsz = 18,
412 Relent = 19,
413 Pltrel = 20,
414 Debug = 21,
415 Textrel = 22,
416 Jmprel = 23,
417 BindNow = 24,
418 InitArray = 25,
419 FiniArray = 26,
420 InitArraysz = 27,
421 FiniArraysz = 28,
422 Runpath = 29,
423 Flags = 30,
424 PreinitArray = 32,
425 PreinitArraysz = 33,
426 Loos = 0x6000000D,
427 Hios = 0x6ffff000,
428 Loproc = 0x70000000,
429 Hiproc = 0x7fffffff,
430}
431
432#[derive(
433 IntoBytes, Immutable, Copy, Clone, KnownLayout, FromBytes, Default, Debug, Eq, PartialEq,
434)]
435#[repr(C)]
436pub struct Elf64Dyn {
437 pub tag: u64,
438 pub value: u64,
439}
440
441impl Elf64Dyn {
442 pub fn tag(&self) -> Result<Elf64DynTag, u64> {
443 Elf64DynTag::from_u64(self.tag).ok_or(self.tag)
444 }
445}
446
447#[derive(
448 IntoBytes, Immutable, Copy, Clone, KnownLayout, FromBytes, Default, Debug, Eq, PartialEq,
449)]
450#[repr(C)]
451pub struct Elf32Dyn {
452 pub tag: u32,
453 pub value: u32,
454}
455
456impl Elf32Dyn {
457 pub fn tag(&self) -> Result<Elf64DynTag, u32> {
458 Elf64DynTag::from_u32(self.tag).ok_or(self.tag)
460 }
461}
462
463impl Into<Elf64Dyn> for Elf32Dyn {
464 fn into(self) -> Elf64Dyn {
465 Elf64Dyn { tag: self.tag as u64, value: self.value as u64 }
466 }
467}
468
469pub type Elf64Addr = u64;
470pub type Elf64Half = u16;
471pub type Elf64Word = u32;
472pub type Elf64Xword = u64;
473
474pub type Elf32Addr = u32;
475pub type Elf32Half = u16;
476pub type Elf32Word = u32;
477pub type Elf32SWord = i32;
478pub type Elf32Lword = u64;
479
480#[repr(C)]
481#[derive(Debug, Default, Copy, Clone, IntoBytes, KnownLayout, FromBytes, Immutable)]
482pub struct elf64_sym {
483 pub st_name: Elf64Word,
484 pub st_info: u8,
485 pub st_other: u8,
486 pub st_shndx: Elf64Half,
487 pub st_value: Elf64Addr,
488 pub st_size: Elf64Xword,
489}
490pub type Elf64Sym = elf64_sym;
491
492#[repr(C)]
493#[derive(Debug, Default, Copy, Clone, IntoBytes, KnownLayout, FromBytes, Immutable)]
494pub struct elf32_sym {
495 pub st_name: Elf32Word,
496 pub st_value: Elf32Addr,
497 pub st_size: Elf32Word,
498 pub st_info: u8,
499 pub st_other: u8,
500 pub st_shndx: Elf32Half,
501}
502pub type Elf32Sym = elf32_sym;
503
504#[derive(FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
505#[repr(u32)]
506pub enum SegmentType {
507 Unused = 0,
509 Load = 1,
511 Dynamic = 2,
513 Interp = 3,
515 GnuStack = 0x6474e551,
517}
518
519bitflags! {
520 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
521 pub struct SegmentFlags: u32 {
522 const EXECUTE = 0b0001;
523 const WRITE = 0b0010;
524 const READ = 0b0100;
525 }
526}
527
528impl fmt::Display for SegmentType {
529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530 match self {
531 SegmentType::Unused => write!(f, "PT_NULL"),
532 SegmentType::Load => write!(f, "PT_LOAD"),
533 SegmentType::Dynamic => write!(f, "PT_DYNAMIC"),
534 SegmentType::Interp => write!(f, "PT_INTERP"),
535 SegmentType::GnuStack => write!(f, "PT_GNU_STACK"),
536 }
537 }
538}
539
540impl Elf64ProgramHeader {
541 pub fn segment_type(&self) -> Result<SegmentType, u32> {
542 SegmentType::from_u32(self.segment_type).ok_or(self.segment_type)
543 }
544
545 pub fn flags(&self) -> SegmentFlags {
546 SegmentFlags::from_bits_truncate(self.flags)
548 }
549}
550
551impl Elf32ProgramHeader {
552 pub fn segment_type(&self) -> Result<SegmentType, u32> {
553 SegmentType::from_u32(self.segment_type).ok_or(self.segment_type)
554 }
555
556 pub fn flags(&self) -> SegmentFlags {
557 SegmentFlags::from_bits_truncate(self.flags)
559 }
560}
561
562impl Into<Elf64ProgramHeader> for Elf32ProgramHeader {
563 fn into(self) -> Elf64ProgramHeader {
564 Elf64ProgramHeader {
565 segment_type: self.segment_type,
566 flags: self.flags,
567 offset: self.offset as usize,
568 vaddr: self.vaddr as usize,
569 paddr: self.paddr as usize,
570 filesz: self.filesz as u64,
571 memsz: self.memsz as u64,
572 align: self.align as u64,
573 }
574 }
575}
576
577impl Validate for [Elf64ProgramHeader] {
578 fn validate(&self) -> Result<(), ElfParseError> {
579 let page_size = zx::system_get_page_size() as usize;
580 let mut vaddr_high: usize = 0;
581 for hdr in self {
582 match hdr.segment_type() {
583 Ok(SegmentType::Load) => {
584 if hdr.filesz > hdr.memsz {
585 return Err(ElfParseError::InvalidProgramHeader(
586 "filesz > memsz in a PT_LOAD segment",
587 ));
588 }
589
590 if hdr.vaddr < vaddr_high {
592 return Err(ElfParseError::InvalidProgramHeader(
593 "Overlap in virtual addresses",
594 ));
595 }
596 vaddr_high = hdr.vaddr + hdr.memsz as usize;
597
598 if hdr.align % page_size as u64 != 0 {
600 return Err(ElfParseError::InvalidProgramHeader(
601 "Alignment must be multiple of the system page size",
602 ));
603 }
604
605 if hdr.align != 0
608 && (hdr.vaddr % hdr.align as usize) != (hdr.offset % hdr.align as usize)
609 {
610 return Err(ElfParseError::InvalidProgramHeader(
611 "Virtual address and offset in file are not at same offset in page",
612 ));
613 }
614 }
615 Ok(SegmentType::GnuStack) => {
616 if hdr.flags().contains(SegmentFlags::EXECUTE) {
617 return Err(ElfParseError::InvalidProgramHeader(
618 "Fuchsia does not support executable stacks",
619 ));
620 }
621 }
622 Ok(SegmentType::Unused) | Ok(SegmentType::Interp) | Ok(SegmentType::Dynamic) => {}
624 Err(_) => {}
626 }
627 }
628 Ok(())
629 }
630}
631
632impl Validate for [Elf32ProgramHeader] {
634 fn validate(&self) -> Result<(), ElfParseError> {
635 let page_size = zx::system_get_page_size() as u32;
636 let mut vaddr_high: u32 = 0;
637 for hdr in self {
638 match hdr.segment_type() {
639 Ok(SegmentType::Load) => {
640 if hdr.filesz > hdr.memsz {
641 return Err(ElfParseError::InvalidProgramHeader(
642 "filesz > memsz in a PT_LOAD segment",
643 ));
644 }
645
646 if hdr.vaddr < vaddr_high {
648 return Err(ElfParseError::InvalidProgramHeader(
649 "Overlap in virtual addresses",
650 ));
651 }
652 vaddr_high = hdr.vaddr.checked_add(hdr.memsz).ok_or({
653 ElfParseError::InvalidProgramHeader(
654 "load segment overflow the 32-bit memory space",
655 )
656 })?;
657
658 if hdr.align % page_size != 0 {
660 return Err(ElfParseError::InvalidProgramHeader(
661 "Alignment must be multiple of the system page size",
662 ));
663 }
664
665 if hdr.align != 0 && (hdr.vaddr % hdr.align) != (hdr.offset % hdr.align) {
668 return Err(ElfParseError::InvalidProgramHeader(
669 "Virtual address and offset in file are not at same offset in page",
670 ));
671 }
672 }
673 Ok(SegmentType::GnuStack) => {
674 if hdr.flags().contains(SegmentFlags::EXECUTE) {
675 return Err(ElfParseError::InvalidProgramHeader(
676 "Fuchsia does not support executable stacks",
677 ));
678 }
679 }
680 Ok(SegmentType::Unused) | Ok(SegmentType::Interp) | Ok(SegmentType::Dynamic) => {}
682 Err(_) => {}
684 }
685 }
686 Ok(())
687 }
688}
689
690pub struct Elf64Headers {
691 file_header: Box<Elf64FileHeader>,
692 program_headers: Option<Box<[Elf64ProgramHeader]>>,
695 }
698
699impl Elf64Headers {
700 pub fn from_vmo(vmo: &zx::Vmo) -> Result<Elf64Headers, ElfParseError> {
701 let file_header = Elf64FileHeader::from_vmo(vmo)?;
703
704 let mut program_headers = None;
708 if file_header.phnum > 0 {
709 let mut phdrs = vec![Elf64ProgramHeader::default(); file_header.phnum as usize];
710 vmo.read(phdrs.as_mut_bytes(), file_header.phoff as u64)
711 .map_err(|s| ElfParseError::ReadError(s))?;
712 phdrs.validate()?;
713 program_headers = Some(phdrs.into_boxed_slice());
714 }
715
716 Ok(Elf64Headers { file_header, program_headers })
717 }
718
719 pub fn from_vmo_with_arch32(vmo: &zx::Vmo) -> Result<Elf64Headers, ElfParseError> {
720 let ident = ElfIdent::from_vmo(vmo)?;
722 if !ident.is_arch32() {
723 return Elf64Headers::from_vmo(vmo);
725 }
726 let arch32_headers = Elf32FileHeader::from_vmo(vmo)?;
727 let file_header: Box<Elf64FileHeader> = arch32_headers.into();
728
729 let mut program_headers = None;
733 if file_header.phnum > 0 {
734 let mut phdrs = vec![Elf32ProgramHeader::default(); file_header.phnum as usize];
735 vmo.read(phdrs.as_mut_bytes(), file_header.phoff as u64)
736 .map_err(|s| ElfParseError::ReadError(s))?;
737 phdrs.validate()?;
740 let phdrs64: Vec<Elf64ProgramHeader> = phdrs.iter().map(|&ph| ph.into()).collect();
741 phdrs64.validate()?;
743 program_headers = Some(phdrs64.into_boxed_slice());
744 }
745
746 Ok(Elf64Headers { file_header, program_headers })
747 }
748
749 pub fn file_header(&self) -> &Elf64FileHeader {
750 &*self.file_header
751 }
752
753 pub fn program_headers(&self) -> &[Elf64ProgramHeader] {
754 match &self.program_headers {
755 Some(boxed_slice) => &*boxed_slice,
756 None => &[],
757 }
758 }
759
760 pub fn program_headers_with_type(
762 &self,
763 stype: SegmentType,
764 ) -> impl Iterator<Item = &Elf64ProgramHeader> {
765 self.program_headers().iter().filter(move |x| match x.segment_type() {
766 Ok(t) => t == stype,
767 _ => false,
768 })
769 }
770
771 pub fn program_header_with_type(
774 &self,
775 stype: SegmentType,
776 ) -> Result<Option<&Elf64ProgramHeader>, ElfParseError> {
777 let mut headers = self.program_headers_with_type(stype);
778 let header = headers.next();
779 if headers.next().is_some() {
780 return Err(ElfParseError::MultipleHeaders(stype));
781 }
782 return Ok(header);
783 }
784
785 #[cfg(test)]
787 pub fn new_for_test(
788 file_header: &Elf64FileHeader,
789 program_headers: Option<&[Elf64ProgramHeader]>,
790 ) -> Self {
791 Self {
792 file_header: Box::new(*file_header),
793 program_headers: program_headers.map(|headers| headers.into()),
794 }
795 }
796}
797
798pub struct Elf64DynSection {
799 dyn_entries: Box<[Elf64Dyn]>,
800}
801impl Elf64DynSection {
802 pub fn from_vmo(vmo: &zx::Vmo) -> Result<Elf64DynSection, ElfParseError> {
803 let headers = Elf64Headers::from_vmo(vmo)?;
804 const ENTRY_SIZE: usize = std::mem::size_of::<Elf64Dyn>();
805 if let Some(dynamic_header) = headers.program_header_with_type(SegmentType::Dynamic)? {
806 let dyn_entries_size = dynamic_header.filesz as usize / ENTRY_SIZE;
807 let mut entries = vec![Elf64Dyn::default(); dyn_entries_size];
808 vmo.read(entries.as_mut_bytes(), dynamic_header.offset as u64)
809 .map_err(|s| ElfParseError::ReadError(s))?;
810 let dyn_entries = entries.into_boxed_slice();
811 return Ok(Elf64DynSection { dyn_entries });
812 }
813 Err(ElfParseError::ParseError("Dynamic header not found"))
814 }
815
816 pub fn from_vmo_with_arch32(vmo: &zx::Vmo) -> Result<Elf64DynSection, ElfParseError> {
817 let ident = ElfIdent::from_vmo(vmo)?;
819 if !ident.is_arch32() {
820 return Elf64DynSection::from_vmo(vmo);
822 }
823 let headers = Elf64Headers::from_vmo_with_arch32(vmo)?;
824 const ENTRY_SIZE: usize = std::mem::size_of::<Elf32Dyn>();
825 if let Some(dynamic_header) = headers.program_header_with_type(SegmentType::Dynamic)? {
826 let dyn_entries_size = dynamic_header.filesz as usize / ENTRY_SIZE;
827 let mut entries = vec![Elf32Dyn::default(); dyn_entries_size];
828 vmo.read(entries.as_mut_bytes(), dynamic_header.offset as u64)
829 .map_err(|s| ElfParseError::ReadError(s))?;
830 let entries64: Vec<Elf64Dyn> = entries.iter().map(|&dt| dt.into()).collect();
831 let dyn_entries = entries64.into_boxed_slice();
832 return Ok(Elf64DynSection { dyn_entries });
833 }
834 Err(ElfParseError::ParseError("Dynamic header not found"))
835 }
836
837 pub fn dynamic_entries(&self) -> &[Elf64Dyn] {
838 &*self.dyn_entries
839 }
840
841 pub fn dynamic_entry_with_tag(&self, tag: Elf64DynTag) -> Option<&Elf64Dyn> {
842 self.dynamic_entries().iter().find(move |x| match x.tag() {
843 Ok(t) => t == tag,
844 _ => false,
845 })
846 }
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852 use anyhow::Error;
853 use assert_matches::assert_matches;
854 use std::fs::File;
855
856 static HEADER_DATA_X86_64: &'static [u8] =
859 include_bytes!("../test-utils/elf_x86-64_file-header.bin");
860 static HEADER_DATA_AARCH64: &'static [u8] =
861 include_bytes!("../test-utils/elf_aarch64_file-header.bin");
862 static HEADER_DATA_RISCV64: &'static [u8] =
863 include_bytes!("../test-utils/elf_riscv64_file-header.bin");
864
865 #[cfg(target_arch = "x86_64")]
866 static HEADER_DATA: &'static [u8] = HEADER_DATA_X86_64;
867 #[cfg(target_arch = "aarch64")]
868 static HEADER_DATA: &'static [u8] = HEADER_DATA_AARCH64;
869 #[cfg(target_arch = "riscv64")]
870 static HEADER_DATA: &'static [u8] = HEADER_DATA_RISCV64;
871
872 fn wrong_arch_file_headers() -> Vec<&'static [u8]> {
874 if cfg!(target_arch = "x86_64") {
875 vec![HEADER_DATA_AARCH64, HEADER_DATA_RISCV64]
876 } else if cfg!(target_arch = "aarch64") {
877 vec![HEADER_DATA_X86_64, HEADER_DATA_RISCV64]
878 } else if cfg!(target_arch = "riscv64") {
879 vec![HEADER_DATA_AARCH64, HEADER_DATA_X86_64]
880 } else {
881 panic!("Unrecognized arch")
882 }
883 }
884
885 #[test]
886 fn test_parse_file_header() -> Result<(), Error> {
887 let vmo = zx::Vmo::create(HEADER_DATA.len() as u64)?;
888 vmo.write(&HEADER_DATA, 0)?;
889
890 let headers = Elf64Headers::from_vmo(&vmo)?;
891 assert_eq!(
892 headers.file_header(),
893 &Elf64FileHeader {
894 ident: ElfIdent {
895 magic: ELF_MAGIC,
896 class: ElfClass::Elf64 as u8,
897 data: ElfDataEncoding::LittleEndian as u8,
898 version: ElfVersion::Current as u8,
899 osabi: 0,
900 abiversion: 0,
901 pad: [0; 7],
902 },
903 elf_type: ElfType::SharedObject as u16,
904 machine: CURRENT_ARCH as u16,
905 version: 1,
906 entry: 0x10000,
907 phoff: 0,
908 shoff: 0,
909 flags: 0,
910 ehsize: mem::size_of::<Elf64FileHeader>() as u16,
911 phentsize: mem::size_of::<Elf64ProgramHeader>() as u16,
912 phnum: 0,
913 shentsize: 0,
914 shnum: 0,
915 shstrndx: 0,
916 }
917 );
918 assert_eq!(headers.program_headers().len(), 0);
919 Ok(())
920 }
921
922 #[test]
923 fn test_parse_wrong_arch() -> Result<(), Error> {
924 for header_data in wrong_arch_file_headers() {
925 let vmo = zx::Vmo::create(header_data.len() as u64)?;
926 vmo.write(&HEADER_DATA, 0)?;
927
928 match Elf64Headers::from_vmo(&vmo) {
929 Err(ElfParseError::InvalidFileHeader(msg)) => {
930 assert_eq!(msg, "Invalid ELF architecture");
931 }
932 _ => {}
933 }
934 }
935 Ok(())
936 }
937
938 #[test]
939 fn test_parse_program_headers() -> Result<(), Error> {
940 let file = File::open("/pkg/bin/process_builder_lib_test")?;
943 let vmo = fdio::get_vmo_copy_from_file(&file)?;
944
945 let headers = Elf64Headers::from_vmo(&vmo)?;
946 assert!(headers.program_headers().len() > 0);
947 assert!(headers.program_header_with_type(SegmentType::Interp)?.is_some());
948 assert!(headers.program_headers_with_type(SegmentType::Dynamic).count() == 1);
949 assert!(headers.program_headers_with_type(SegmentType::Load).count() > 1);
950 Ok(())
951 }
952
953 #[test]
954 fn test_parse_dynamic_section() -> Result<(), Error> {
955 let file = File::open("/pkg/bin/process_builder_lib_test")?;
956 let vmo = fdio::get_vmo_copy_from_file(&file)?;
957
958 let dynamic_section = Elf64DynSection::from_vmo(&vmo)?;
959 assert!(dynamic_section.dynamic_entries().len() > 0);
960 Ok(())
961 }
962
963 #[test]
964 fn test_parse_static_pie() -> Result<(), Error> {
965 let file = File::open("/pkg/bin/static_pie_test_util")?;
967 let vmo = fdio::get_vmo_copy_from_file(&file)?;
968
969 let headers = Elf64Headers::from_vmo(&vmo)?;
971 assert!(headers.program_headers().len() > 0);
972 assert!(headers.program_header_with_type(SegmentType::Interp)?.is_none());
973 assert!(headers.program_headers_with_type(SegmentType::Dynamic).count() == 1);
974 assert!(headers.program_headers_with_type(SegmentType::Load).count() > 1);
975 Ok(())
976 }
977
978 #[test]
979 fn test_parse_program_header_bad_alignment() {
980 let page_size = zx::system_get_page_size() as usize;
981 let headers = [Elf64ProgramHeader {
982 segment_type: SegmentType::Load as u32,
983 flags: (SegmentFlags::READ | SegmentFlags::EXECUTE).bits(),
984 align: page_size as u64 + 1024,
985 offset: 0x1000,
986 vaddr: 0x1000,
987 paddr: 0x1000,
988 filesz: 0x1000,
989 memsz: 0x1000,
990 }];
991 assert_matches!(
992 headers.validate(),
993 Err(ElfParseError::InvalidProgramHeader(
994 "Alignment must be multiple of the system page size"
995 ))
996 );
997 }
998
999 #[test]
1000 fn test_parse_program_header_bad_offset_and_vaddr() {
1001 let page_size = zx::system_get_page_size() as usize;
1002 let headers = [Elf64ProgramHeader {
1003 segment_type: SegmentType::Load as u32,
1004 flags: (SegmentFlags::READ | SegmentFlags::EXECUTE).bits(),
1005 align: page_size as u64,
1006 offset: 0x1001,
1007 vaddr: 0x1002,
1008 paddr: 0x1000,
1009 filesz: 0x1000,
1010 memsz: 0x1000,
1011 }];
1012 assert_matches!(
1013 headers.validate(),
1014 Err(ElfParseError::InvalidProgramHeader(
1015 "Virtual address and offset in file are not at same offset in page"
1016 ))
1017 );
1018 }
1019}