process_builder/
elf_parse.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Parses ELF files per the ELF specification in ulib/musl/include/elf.h
6use 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/// Possible errors that can occur during ELF parsing.
16#[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    /// Returns an appropriate zx::Status code for the given error.
32    pub fn as_zx_status(&self) -> zx::Status {
33        match self {
34            ElfParseError::ReadError(s) => *s,
35            // Not a great status to return for an invalid ELF but there's no great fit, and this
36            // matches elf_load.
37            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/// ELF identity header.
50#[derive(
51    KnownLayout, FromBytes, IntoBytes, Immutable, Debug, Eq, PartialEq, Default, Clone, Copy,
52)]
53#[repr(C)]
54pub struct ElfIdent {
55    /// e_ident[EI_MAG0:EI_MAG3]
56    pub magic: [u8; 4],
57    /// e_ident[EI_CLASS]
58    pub class: u8,
59    /// e_ident[EI_DATA]
60    pub data: u8,
61    /// e_ident[EI_VERSION]
62    pub version: u8,
63    /// e_ident[EI_OSABI]
64    pub osabi: u8,
65    /// e_ident[EI_ABIVERSION]
66    pub abiversion: u8,
67    /// e_ident[EI_PAD]
68    pub pad: [u8; 7],
69}
70
71#[allow(unused)]
72const EI_NIDENT: usize = 16;
73assert_eq_size!(ElfIdent, [u8; EI_NIDENT]);
74
75/// ELF class, from EI_CLASS.
76#[derive(FromPrimitive, Eq, PartialEq)]
77#[repr(u8)]
78pub enum ElfClass {
79    /// ELFCLASSNONE
80    Unknown = 0,
81    /// ELFCLASS32
82    Elf32 = 1,
83    /// ELFCLASS64
84    Elf64 = 2,
85}
86
87/// ELF data encoding, from EI_DATA.
88#[derive(FromPrimitive, Eq, PartialEq)]
89#[repr(u8)]
90pub enum ElfDataEncoding {
91    /// ELFDATANONE
92    Unknown = 0,
93    /// ELFDATA2LSB
94    LittleEndian = 1,
95    /// ELFDATA2MSB
96    BigEndian = 2,
97}
98
99/// ELF version, from EI_VERSION.
100#[derive(FromPrimitive, Eq, PartialEq)]
101#[repr(u8)]
102pub enum ElfVersion {
103    /// EV_NONE
104    Unknown = 0,
105    /// EV_CURRENT
106    Current = 1,
107}
108
109impl ElfIdent {
110    pub fn from_vmo(vmo: &zx::Vmo) -> Result<ElfIdent, ElfParseError> {
111        // Read and parse the ELF file header from the VMO.
112        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, // These three are the only differently sized entries.
164    pub phoff: u32, // <--
165    pub shoff: u32, // <--
166    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    /// ET_NONE
179    Unknown = 0,
180    /// ET_REL
181    Relocatable = 1,
182    /// ET_EXEC
183    Executable = 2,
184    /// ET_DYN
185    SharedObject = 3,
186    /// ET_CORE
187    Core = 4,
188}
189
190#[derive(FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
191#[repr(u32)]
192pub enum ElfArchitecture {
193    /// EM_NONE
194    Unknown = 0,
195    /// EM_386
196    I386 = 3,
197    /// EM_ARM
198    ARM = 40,
199    /// EM_X86_64
200    X86_64 = 62,
201    /// EM_AARCH64
202    AARCH64 = 183,
203    /// EM_RISCV
204    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        // Read and parse the ELF file header from the VMO.
240        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        // Read and parse the ELF file header from the VMO.
258        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 the current arch doesn't support 32-bit ELF, we will fail here
307        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        // The Validate attribute will fail on this header.
341        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        // The dyn tags are all int32 regardless of the format.
459        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    /// PT_NULL
508    Unused = 0,
509    /// PT_LOAD
510    Load = 1,
511    /// PT_DYNAMIC
512    Dynamic = 2,
513    /// PT_INTERP
514    Interp = 3,
515    /// PT_GNU_STACK
516    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        // Ignore bits that don't correspond to one of the flags included in SegmentFlags
547        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        // Ignore bits that don't correspond to one of the flags included in SegmentFlags
558        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                    // Virtual addresses for PT_LOAD segments should not overlap.
591                    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                    // Segment alignment should be a multiple of the system page size.
599                    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                    // Virtual addresses should be at the same page offset as their offset in the
606                    // file.
607                    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                // No specific validation to perform for these.
623                Ok(SegmentType::Unused) | Ok(SegmentType::Interp) | Ok(SegmentType::Dynamic) => {}
624                // Ignore segment types that we don't care about.
625                Err(_) => {}
626            }
627        }
628        Ok(())
629    }
630}
631
632// Ensure we stay in the 32-bit space.
633impl 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                    // Virtual addresses for PT_LOAD segments should not overlap.
647                    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                    // Segment alignment should be a multiple of the system page size.
659                    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                    // Virtual addresses should be at the same page offset as their offset in the
666                    // file.
667                    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                // No specific validation to perform for these.
681                Ok(SegmentType::Unused) | Ok(SegmentType::Interp) | Ok(SegmentType::Dynamic) => {}
682                // Ignore segment types that we don't care about.
683                Err(_) => {}
684            }
685        }
686        Ok(())
687    }
688}
689
690pub struct Elf64Headers {
691    file_header: Box<Elf64FileHeader>,
692    // Use a Box<[_]> instead of a Vec<> to communicate/enforce that the slice is not mutated after
693    // construction.
694    program_headers: Option<Box<[Elf64ProgramHeader]>>,
695    // Section headers are not parsed currently since they aren't needed for the current use case,
696    // but could be added if needed.
697}
698
699impl Elf64Headers {
700    pub fn from_vmo(vmo: &zx::Vmo) -> Result<Elf64Headers, ElfParseError> {
701        // Read and parse the ELF file header from the VMO.
702        let file_header = Elf64FileHeader::from_vmo(vmo)?;
703
704        // Read and parse the ELF program headers from the VMO. Also support the degenerate case
705        // where there are no program headers, which is valid ELF but probably not useful outside
706        // tests.
707        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        // Check the class, then load the right one.
721        let ident = ElfIdent::from_vmo(vmo)?;
722        if !ident.is_arch32() {
723            // This is not arch32, so we can just proceed.
724            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        // Read and parse the ELF program headers from the VMO. Also support the degenerate case
730        // where there are no program headers, which is valid ELF but probably not useful outside
731        // tests.
732        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            // We can't just use the 64-bit validation because we need to keep
738            // the addresses within the u32 space (if not <3Gb).
739            phdrs.validate()?;
740            let phdrs64: Vec<Elf64ProgramHeader> = phdrs.iter().map(|&ph| ph.into()).collect();
741            // TODO(drewry) we should be able to get rid of this extra validation.
742            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    /// Returns an iterator that yields all program headers of the given type.
761    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    /// Returns 0 or 1 headers of the given type, or Err([ElfParseError::MultipleHeaders]) if more
772    /// than 1 such header is present.
773    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    /// Creates an instance of Elf64Headers from in-memory representations of the ELF headers.
786    #[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        // Check the class, then load the right one.
818        let ident = ElfIdent::from_vmo(vmo)?;
819        if !ident.is_arch32() {
820            // This is not arch32, so we can just proceed.
821            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    // These are specially crafted files that just contain a valid ELF64 file header but
857    // nothing else.
858    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    // Returns a vec of file headers for different architectures that do not match the current one.
873    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's try to parse ourselves!
941        // Ideally we'd use std::env::current_exe but that doesn't seem to be implemented (yet?)
942        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        // Parse the statically linked PIE test binary.
966        let file = File::open("/pkg/bin/static_pie_test_util")?;
967        let vmo = fdio::get_vmo_copy_from_file(&file)?;
968
969        // Should have no PT_INTERP header, but should have PT_DYNAMIC and 1+ PT_LOAD.
970        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}