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