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(KnownLayout, FromBytes, IntoBytes, Immutable, Eq, PartialEq, Default, Clone, Copy)]
51#[repr(C)]
52pub struct ElfIdent {
53    /// e_ident[EI_MAG0:EI_MAG3]
54    pub magic: [u8; 4],
55    /// e_ident[EI_CLASS]
56    pub class: u8,
57    /// e_ident[EI_DATA]
58    pub data: u8,
59    /// e_ident[EI_VERSION]
60    pub version: u8,
61    /// e_ident[EI_OSABI]
62    pub osabi: u8,
63    /// e_ident[EI_ABIVERSION]
64    pub abiversion: u8,
65    /// e_ident[EI_PAD]
66    pub pad: [u8; 7],
67}
68
69#[allow(unused)]
70const EI_NIDENT: usize = 16;
71assert_eq_size!(ElfIdent, [u8; EI_NIDENT]);
72
73/// ELF class, from EI_CLASS.
74#[derive(Debug, FromPrimitive, Eq, PartialEq)]
75#[repr(u8)]
76pub enum ElfClass {
77    /// ELFCLASSNONE
78    Unknown = 0,
79    /// ELFCLASS32
80    Elf32 = 1,
81    /// ELFCLASS64
82    Elf64 = 2,
83}
84
85/// ELF data encoding, from EI_DATA.
86#[derive(Debug, FromPrimitive, Eq, PartialEq)]
87#[repr(u8)]
88pub enum ElfDataEncoding {
89    /// ELFDATANONE
90    Unknown = 0,
91    /// ELFDATA2LSB
92    LittleEndian = 1,
93    /// ELFDATA2MSB
94    BigEndian = 2,
95}
96
97/// ELF version, from EI_VERSION.
98#[derive(Debug, FromPrimitive, Eq, PartialEq)]
99#[repr(u8)]
100pub enum ElfVersion {
101    /// EV_NONE
102    Unknown = 0,
103    /// EV_CURRENT
104    Current = 1,
105}
106
107impl ElfIdent {
108    pub fn from_vmo(vmo: &zx::Vmo) -> Result<ElfIdent, ElfParseError> {
109        // Read and parse the ELF file header from the VMO.
110        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, // These three are the only differently sized entries.
180    pub phoff: u32, // <--
181    pub shoff: u32, // <--
182    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    /// ET_NONE
195    Unknown = 0,
196    /// ET_REL
197    Relocatable = 1,
198    /// ET_EXEC
199    Executable = 2,
200    /// ET_DYN
201    SharedObject = 3,
202    /// ET_CORE
203    Core = 4,
204}
205
206#[derive(FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
207#[repr(u32)]
208pub enum ElfArchitecture {
209    /// EM_NONE
210    Unknown = 0,
211    /// EM_386
212    I386 = 3,
213    /// EM_ARM
214    ARM = 40,
215    /// EM_X86_64
216    X86_64 = 62,
217    /// EM_AARCH64
218    AARCH64 = 183,
219    /// EM_RISCV
220    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        // Read and parse the ELF file header from the VMO.
256        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        // Read and parse the ELF file header from the VMO.
303        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 the current arch doesn't support 32-bit ELF, we will fail here
381        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        // The Validate attribute will fail on this header.
415        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        // The dyn tags are all int32 regardless of the format.
529        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    /// PT_NULL
578    Unused = 0,
579    /// PT_LOAD
580    Load = 1,
581    /// PT_DYNAMIC
582    Dynamic = 2,
583    /// PT_INTERP
584    Interp = 3,
585    /// PT_GNU_STACK
586    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        // Ignore bits that don't correspond to one of the flags included in SegmentFlags
617        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        // Ignore bits that don't correspond to one of the flags included in SegmentFlags
648        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                    // Virtual addresses for PT_LOAD segments should not overlap.
701                    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                    // Segment alignment should be a multiple of the system page size.
709                    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                    // Virtual addresses should be at the same page offset as their offset in the
716                    // file.
717                    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                // No specific validation to perform for these.
733                Ok(SegmentType::Unused) | Ok(SegmentType::Interp) | Ok(SegmentType::Dynamic) => {}
734                // Ignore segment types that we don't care about.
735                Err(_) => {}
736            }
737        }
738        Ok(())
739    }
740}
741
742// Ensure we stay in the 32-bit space.
743impl 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                    // Virtual addresses for PT_LOAD segments should not overlap.
757                    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                    // Segment alignment should be a multiple of the system page size.
769                    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                    // Virtual addresses should be at the same page offset as their offset in the
776                    // file.
777                    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                // No specific validation to perform for these.
791                Ok(SegmentType::Unused) | Ok(SegmentType::Interp) | Ok(SegmentType::Dynamic) => {}
792                // Ignore segment types that we don't care about.
793                Err(_) => {}
794            }
795        }
796        Ok(())
797    }
798}
799
800pub struct Elf64Headers {
801    file_header: Box<Elf64FileHeader>,
802    // Use a Box<[_]> instead of a Vec<> to communicate/enforce that the slice is not mutated after
803    // construction.
804    program_headers: Option<Box<[Elf64ProgramHeader]>>,
805    // Section headers are not parsed currently since they aren't needed for the current use case,
806    // but could be added if needed.
807}
808
809impl Elf64Headers {
810    pub fn from_vmo(vmo: &zx::Vmo) -> Result<Elf64Headers, ElfParseError> {
811        // Read and parse the ELF file header from the VMO.
812        let file_header = Elf64FileHeader::from_vmo(vmo)?;
813
814        // Read and parse the ELF program headers from the VMO. Also support the degenerate case
815        // where there are no program headers, which is valid ELF but probably not useful outside
816        // tests.
817        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        // Check the class, then load the right one.
831        let ident = ElfIdent::from_vmo(vmo)?;
832        if !ident.is_arch32() {
833            // This is not arch32, so we can just proceed.
834            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        // Read and parse the ELF program headers from the VMO. Also support the degenerate case
840        // where there are no program headers, which is valid ELF but probably not useful outside
841        // tests.
842        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            // We can't just use the 64-bit validation because we need to keep
848            // the addresses within the u32 space (if not <3Gb).
849            phdrs.validate()?;
850            let phdrs64: Vec<Elf64ProgramHeader> = phdrs.iter().map(|&ph| ph.into()).collect();
851            // TODO(drewry) we should be able to get rid of this extra validation.
852            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    /// Returns an iterator that yields all program headers of the given type.
871    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    /// Returns 0 or 1 headers of the given type, or Err([ElfParseError::MultipleHeaders]) if more
882    /// than 1 such header is present.
883    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    /// Creates an instance of Elf64Headers from in-memory representations of the ELF headers.
896    #[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        // Check the class, then load the right one.
928        let ident = ElfIdent::from_vmo(vmo)?;
929        if !ident.is_arch32() {
930            // This is not arch32, so we can just proceed.
931            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    // These are specially crafted files that just contain a valid ELF64 file header but
967    // nothing else.
968    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    // Returns a vec of file headers for different architectures that do not match the current one.
983    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's try to parse ourselves!
1051        // Ideally we'd use std::env::current_exe but that doesn't seem to be implemented (yet?)
1052        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        // Parse the statically linked PIE test binary.
1076        let file = File::open("/pkg/bin/static_pie_test_util")?;
1077        let vmo = fdio::get_vmo_copy_from_file(&file)?;
1078
1079        // Should have no PT_INTERP header, but should have PT_DYNAMIC and 1+ PT_LOAD.
1080        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}