Skip to main content

elf_parse/
lib.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.checked_add(hdr.memsz as usize).ok_or({
707                        ElfParseError::InvalidProgramHeader(
708                            "load segment overflow the 64-bit memory space",
709                        )
710                    })?;
711
712                    // Segment alignment should be a multiple of the system page size.
713                    if hdr.align % page_size as u64 != 0 {
714                        return Err(ElfParseError::InvalidProgramHeader(
715                            "Alignment must be multiple of the system page size",
716                        ));
717                    }
718
719                    // Virtual addresses should be at the same page offset as their offset in the
720                    // file.
721                    if hdr.align != 0
722                        && (hdr.vaddr % hdr.align as usize) != (hdr.offset % hdr.align as usize)
723                    {
724                        return Err(ElfParseError::InvalidProgramHeader(
725                            "Virtual address and offset in file are not at same offset in page",
726                        ));
727                    }
728                }
729                Ok(SegmentType::GnuStack) => {
730                    if hdr.flags().contains(SegmentFlags::EXECUTE) {
731                        return Err(ElfParseError::InvalidProgramHeader(
732                            "Fuchsia does not support executable stacks",
733                        ));
734                    }
735                }
736                // No specific validation to perform for these.
737                Ok(SegmentType::Unused) | Ok(SegmentType::Interp) | Ok(SegmentType::Dynamic) => {}
738                // Ignore segment types that we don't care about.
739                Err(_) => {}
740            }
741        }
742        Ok(())
743    }
744}
745
746// Ensure we stay in the 32-bit space.
747impl Validate for [Elf32ProgramHeader] {
748    fn validate(&self) -> Result<(), ElfParseError> {
749        let page_size = zx::system_get_page_size() as u32;
750        let mut vaddr_high: u32 = 0;
751        for hdr in self {
752            match hdr.segment_type() {
753                Ok(SegmentType::Load) => {
754                    if hdr.filesz > hdr.memsz {
755                        return Err(ElfParseError::InvalidProgramHeader(
756                            "filesz > memsz in a PT_LOAD segment",
757                        ));
758                    }
759
760                    // Virtual addresses for PT_LOAD segments should not overlap.
761                    if hdr.vaddr < vaddr_high {
762                        return Err(ElfParseError::InvalidProgramHeader(
763                            "Overlap in virtual addresses",
764                        ));
765                    }
766                    vaddr_high = hdr.vaddr.checked_add(hdr.memsz).ok_or({
767                        ElfParseError::InvalidProgramHeader(
768                            "load segment overflow the 32-bit memory space",
769                        )
770                    })?;
771
772                    // Segment alignment should be a multiple of the system page size.
773                    if hdr.align % page_size != 0 {
774                        return Err(ElfParseError::InvalidProgramHeader(
775                            "Alignment must be multiple of the system page size",
776                        ));
777                    }
778
779                    // Virtual addresses should be at the same page offset as their offset in the
780                    // file.
781                    if hdr.align != 0 && (hdr.vaddr % hdr.align) != (hdr.offset % hdr.align) {
782                        return Err(ElfParseError::InvalidProgramHeader(
783                            "Virtual address and offset in file are not at same offset in page",
784                        ));
785                    }
786                }
787                Ok(SegmentType::GnuStack) => {
788                    if hdr.flags().contains(SegmentFlags::EXECUTE) {
789                        return Err(ElfParseError::InvalidProgramHeader(
790                            "Fuchsia does not support executable stacks",
791                        ));
792                    }
793                }
794                // No specific validation to perform for these.
795                Ok(SegmentType::Unused) | Ok(SegmentType::Interp) | Ok(SegmentType::Dynamic) => {}
796                // Ignore segment types that we don't care about.
797                Err(_) => {}
798            }
799        }
800        Ok(())
801    }
802}
803
804pub struct Elf64Headers {
805    file_header: Box<Elf64FileHeader>,
806    // Use a Box<[_]> instead of a Vec<> to communicate/enforce that the slice is not mutated after
807    // construction.
808    program_headers: Option<Box<[Elf64ProgramHeader]>>,
809    // Section headers are not parsed currently since they aren't needed for the current use case,
810    // but could be added if needed.
811}
812
813impl Elf64Headers {
814    pub fn from_vmo(vmo: &zx::Vmo) -> Result<Elf64Headers, ElfParseError> {
815        // Read and parse the ELF file header from the VMO.
816        let file_header = Elf64FileHeader::from_vmo(vmo)?;
817
818        // Read and parse the ELF program headers from the VMO. Also support the degenerate case
819        // where there are no program headers, which is valid ELF but probably not useful outside
820        // tests.
821        let mut program_headers = None;
822        if file_header.phnum > 0 {
823            let mut phdrs = vec![Elf64ProgramHeader::default(); file_header.phnum as usize];
824            vmo.read(phdrs.as_mut_bytes(), file_header.phoff as u64)
825                .map_err(|s| ElfParseError::ReadError(s))?;
826            phdrs.validate()?;
827            program_headers = Some(phdrs.into_boxed_slice());
828        }
829
830        Ok(Elf64Headers { file_header, program_headers })
831    }
832
833    pub fn from_vmo_with_arch32(vmo: &zx::Vmo) -> Result<Elf64Headers, ElfParseError> {
834        // Check the class, then load the right one.
835        let ident = ElfIdent::from_vmo(vmo)?;
836        if !ident.is_arch32() {
837            // This is not arch32, so we can just proceed.
838            return Elf64Headers::from_vmo(vmo);
839        }
840        let arch32_headers = Elf32FileHeader::from_vmo(vmo)?;
841        let file_header: Box<Elf64FileHeader> = arch32_headers.into();
842
843        // Read and parse the ELF program headers from the VMO. Also support the degenerate case
844        // where there are no program headers, which is valid ELF but probably not useful outside
845        // tests.
846        let mut program_headers = None;
847        if file_header.phnum > 0 {
848            let mut phdrs = vec![Elf32ProgramHeader::default(); file_header.phnum as usize];
849            vmo.read(phdrs.as_mut_bytes(), file_header.phoff as u64)
850                .map_err(|s| ElfParseError::ReadError(s))?;
851            // We can't just use the 64-bit validation because we need to keep
852            // the addresses within the u32 space (if not <3Gb).
853            phdrs.validate()?;
854            let phdrs64: Vec<Elf64ProgramHeader> = phdrs.iter().map(|&ph| ph.into()).collect();
855            // TODO(drewry) we should be able to get rid of this extra validation.
856            phdrs64.validate()?;
857            program_headers = Some(phdrs64.into_boxed_slice());
858        }
859
860        Ok(Elf64Headers { file_header, program_headers })
861    }
862
863    pub fn file_header(&self) -> &Elf64FileHeader {
864        &*self.file_header
865    }
866
867    pub fn program_headers(&self) -> &[Elf64ProgramHeader] {
868        match &self.program_headers {
869            Some(boxed_slice) => &*boxed_slice,
870            None => &[],
871        }
872    }
873
874    /// Returns an iterator that yields all program headers of the given type.
875    pub fn program_headers_with_type(
876        &self,
877        stype: SegmentType,
878    ) -> impl Iterator<Item = &Elf64ProgramHeader> {
879        self.program_headers().iter().filter(move |x| match x.segment_type() {
880            Ok(t) => t == stype,
881            _ => false,
882        })
883    }
884
885    /// Returns 0 or 1 headers of the given type, or Err([ElfParseError::MultipleHeaders]) if more
886    /// than 1 such header is present.
887    pub fn program_header_with_type(
888        &self,
889        stype: SegmentType,
890    ) -> Result<Option<&Elf64ProgramHeader>, ElfParseError> {
891        let mut headers = self.program_headers_with_type(stype);
892        let header = headers.next();
893        if headers.next().is_some() {
894            return Err(ElfParseError::MultipleHeaders(stype));
895        }
896        return Ok(header);
897    }
898
899    /// Creates an instance of Elf64Headers from in-memory representations of the ELF headers.
900
901    pub fn new_for_test(
902        file_header: &Elf64FileHeader,
903        program_headers: Option<&[Elf64ProgramHeader]>,
904    ) -> Self {
905        Self {
906            file_header: Box::new(*file_header),
907            program_headers: program_headers.map(|headers| headers.into()),
908        }
909    }
910}
911
912pub struct Elf64DynSection {
913    dyn_entries: Box<[Elf64Dyn]>,
914}
915impl Elf64DynSection {
916    pub fn from_vmo(vmo: &zx::Vmo) -> Result<Elf64DynSection, ElfParseError> {
917        let headers = Elf64Headers::from_vmo(vmo)?;
918        const ENTRY_SIZE: usize = std::mem::size_of::<Elf64Dyn>();
919        if let Some(dynamic_header) = headers.program_header_with_type(SegmentType::Dynamic)? {
920            let dyn_entries_size = dynamic_header.filesz as usize / ENTRY_SIZE;
921            let mut entries = vec![Elf64Dyn::default(); dyn_entries_size];
922            vmo.read(entries.as_mut_bytes(), dynamic_header.offset as u64)
923                .map_err(|s| ElfParseError::ReadError(s))?;
924            let dyn_entries = entries.into_boxed_slice();
925            return Ok(Elf64DynSection { dyn_entries });
926        }
927        Err(ElfParseError::ParseError("Dynamic header not found"))
928    }
929
930    pub fn from_vmo_with_arch32(vmo: &zx::Vmo) -> Result<Elf64DynSection, ElfParseError> {
931        // Check the class, then load the right one.
932        let ident = ElfIdent::from_vmo(vmo)?;
933        if !ident.is_arch32() {
934            // This is not arch32, so we can just proceed.
935            return Elf64DynSection::from_vmo(vmo);
936        }
937        let headers = Elf64Headers::from_vmo_with_arch32(vmo)?;
938        const ENTRY_SIZE: usize = std::mem::size_of::<Elf32Dyn>();
939        if let Some(dynamic_header) = headers.program_header_with_type(SegmentType::Dynamic)? {
940            let dyn_entries_size = dynamic_header.filesz as usize / ENTRY_SIZE;
941            let mut entries = vec![Elf32Dyn::default(); dyn_entries_size];
942            vmo.read(entries.as_mut_bytes(), dynamic_header.offset as u64)
943                .map_err(|s| ElfParseError::ReadError(s))?;
944            let entries64: Vec<Elf64Dyn> = entries.iter().map(|&dt| dt.into()).collect();
945            let dyn_entries = entries64.into_boxed_slice();
946            return Ok(Elf64DynSection { dyn_entries });
947        }
948        Err(ElfParseError::ParseError("Dynamic header not found"))
949    }
950
951    pub fn dynamic_entries(&self) -> &[Elf64Dyn] {
952        &*self.dyn_entries
953    }
954
955    pub fn dynamic_entry_with_tag(&self, tag: Elf64DynTag) -> Option<&Elf64Dyn> {
956        self.dynamic_entries().iter().find(move |x| match x.tag() {
957            Ok(t) => t == tag,
958            _ => false,
959        })
960    }
961}
962
963#[cfg(test)]
964mod tests {
965    use super::*;
966    use anyhow::Error;
967    use assert_matches::assert_matches;
968    use std::fs::File;
969
970    // These are specially crafted files that just contain a valid ELF64 file header but
971    // nothing else.
972    static HEADER_DATA_X86_64: &'static [u8] =
973        include_bytes!("../test-utils/elf_x86-64_file-header.bin");
974    static HEADER_DATA_AARCH64: &'static [u8] =
975        include_bytes!("../test-utils/elf_aarch64_file-header.bin");
976    static HEADER_DATA_RISCV64: &'static [u8] =
977        include_bytes!("../test-utils/elf_riscv64_file-header.bin");
978
979    #[cfg(target_arch = "x86_64")]
980    static HEADER_DATA: &'static [u8] = HEADER_DATA_X86_64;
981    #[cfg(target_arch = "aarch64")]
982    static HEADER_DATA: &'static [u8] = HEADER_DATA_AARCH64;
983    #[cfg(target_arch = "riscv64")]
984    static HEADER_DATA: &'static [u8] = HEADER_DATA_RISCV64;
985
986    // Returns a vec of file headers for different architectures that do not match the current one.
987    fn wrong_arch_file_headers() -> Vec<&'static [u8]> {
988        if cfg!(target_arch = "x86_64") {
989            vec![HEADER_DATA_AARCH64, HEADER_DATA_RISCV64]
990        } else if cfg!(target_arch = "aarch64") {
991            vec![HEADER_DATA_X86_64, HEADER_DATA_RISCV64]
992        } else if cfg!(target_arch = "riscv64") {
993            vec![HEADER_DATA_AARCH64, HEADER_DATA_X86_64]
994        } else {
995            panic!("Unrecognized arch")
996        }
997    }
998
999    #[test]
1000    fn test_parse_file_header() -> Result<(), Error> {
1001        let vmo = zx::Vmo::create(HEADER_DATA.len() as u64)?;
1002        vmo.write(&HEADER_DATA, 0)?;
1003
1004        let headers = Elf64Headers::from_vmo(&vmo)?;
1005        assert_eq!(
1006            headers.file_header(),
1007            &Elf64FileHeader {
1008                ident: ElfIdent {
1009                    magic: ELF_MAGIC,
1010                    class: ElfClass::Elf64 as u8,
1011                    data: ElfDataEncoding::LittleEndian as u8,
1012                    version: ElfVersion::Current as u8,
1013                    osabi: 0,
1014                    abiversion: 0,
1015                    pad: [0; 7],
1016                },
1017                elf_type: ElfType::SharedObject as u16,
1018                machine: CURRENT_ARCH as u16,
1019                version: 1,
1020                entry: 0x10000,
1021                phoff: 0,
1022                shoff: 0,
1023                flags: 0,
1024                ehsize: mem::size_of::<Elf64FileHeader>() as u16,
1025                phentsize: mem::size_of::<Elf64ProgramHeader>() as u16,
1026                phnum: 0,
1027                shentsize: 0,
1028                shnum: 0,
1029                shstrndx: 0,
1030            }
1031        );
1032        assert_eq!(headers.program_headers().len(), 0);
1033        Ok(())
1034    }
1035
1036    #[test]
1037    fn test_parse_wrong_arch() -> Result<(), Error> {
1038        for header_data in wrong_arch_file_headers() {
1039            let vmo = zx::Vmo::create(header_data.len() as u64)?;
1040            vmo.write(&HEADER_DATA, 0)?;
1041
1042            match Elf64Headers::from_vmo(&vmo) {
1043                Err(ElfParseError::InvalidFileHeader(msg)) => {
1044                    assert_eq!(msg, "Invalid ELF architecture");
1045                }
1046                _ => {}
1047            }
1048        }
1049        Ok(())
1050    }
1051
1052    #[test]
1053    fn test_parse_program_headers() -> Result<(), Error> {
1054        // Let's try to parse ourselves!
1055        // Ideally we'd use std::env::current_exe but that doesn't seem to be implemented (yet?)
1056        let file = File::open("/pkg/bin/elf_parse_lib_test")?;
1057        let vmo = fdio::get_vmo_copy_from_file(&file)?;
1058
1059        let headers = Elf64Headers::from_vmo(&vmo)?;
1060        assert!(headers.program_headers().len() > 0);
1061        assert!(headers.program_header_with_type(SegmentType::Interp)?.is_some());
1062        assert!(headers.program_headers_with_type(SegmentType::Dynamic).count() == 1);
1063        assert!(headers.program_headers_with_type(SegmentType::Load).count() > 1);
1064        Ok(())
1065    }
1066
1067    #[test]
1068    fn test_parse_dynamic_section() -> Result<(), Error> {
1069        let file = File::open("/pkg/bin/elf_parse_lib_test")?;
1070        let vmo = fdio::get_vmo_copy_from_file(&file)?;
1071
1072        let dynamic_section = Elf64DynSection::from_vmo(&vmo)?;
1073        assert!(dynamic_section.dynamic_entries().len() > 0);
1074        Ok(())
1075    }
1076
1077    #[test]
1078    fn test_parse_static_pie() -> Result<(), Error> {
1079        // Parse the statically linked PIE test binary.
1080        let file = File::open("/pkg/bin/static_pie_test_util")?;
1081        let vmo = fdio::get_vmo_copy_from_file(&file)?;
1082
1083        // Should have no PT_INTERP header, but should have PT_DYNAMIC and 1+ PT_LOAD.
1084        let headers = Elf64Headers::from_vmo(&vmo)?;
1085        assert!(headers.program_headers().len() > 0);
1086        assert!(headers.program_header_with_type(SegmentType::Interp)?.is_none());
1087        assert!(headers.program_headers_with_type(SegmentType::Dynamic).count() == 1);
1088        assert!(headers.program_headers_with_type(SegmentType::Load).count() > 1);
1089        Ok(())
1090    }
1091
1092    #[test]
1093    fn test_parse_program_header_bad_alignment() {
1094        let page_size = zx::system_get_page_size() as usize;
1095        let headers = [Elf64ProgramHeader {
1096            segment_type: SegmentType::Load as u32,
1097            flags: (SegmentFlags::READ | SegmentFlags::EXECUTE).bits(),
1098            align: page_size as u64 + 1024,
1099            offset: 0x1000,
1100            vaddr: 0x1000,
1101            paddr: 0x1000,
1102            filesz: 0x1000,
1103            memsz: 0x1000,
1104        }];
1105        assert_matches!(
1106            headers.validate(),
1107            Err(ElfParseError::InvalidProgramHeader(
1108                "Alignment must be multiple of the system page size"
1109            ))
1110        );
1111    }
1112
1113    #[test]
1114    fn test_parse_program_header_bad_offset_and_vaddr() {
1115        let page_size = zx::system_get_page_size() as usize;
1116        let headers = [Elf64ProgramHeader {
1117            segment_type: SegmentType::Load as u32,
1118            flags: (SegmentFlags::READ | SegmentFlags::EXECUTE).bits(),
1119            align: page_size as u64,
1120            offset: 0x1001,
1121            vaddr: 0x1002,
1122            paddr: 0x1000,
1123            filesz: 0x1000,
1124            memsz: 0x1000,
1125        }];
1126        assert_matches!(
1127            headers.validate(),
1128            Err(ElfParseError::InvalidProgramHeader(
1129                "Virtual address and offset in file are not at same offset in page"
1130            ))
1131        );
1132    }
1133
1134    #[test]
1135    fn test_parse_program_header_overflow() {
1136        let page_size = zx::system_get_page_size() as usize;
1137        let headers = [
1138            Elf64ProgramHeader {
1139                segment_type: SegmentType::Load as u32,
1140                flags: (SegmentFlags::READ | SegmentFlags::EXECUTE).bits(),
1141                align: page_size as u64,
1142                offset: 0x1000,
1143                // Near the end of the address space, will overflow when adding memsz
1144                vaddr: usize::MAX - 0xfff,
1145                paddr: usize::MAX - 0xfff,
1146                filesz: 0x2000,
1147                memsz: 0x2000,
1148            },
1149            Elf64ProgramHeader {
1150                segment_type: SegmentType::Load as u32,
1151                flags: (SegmentFlags::READ | SegmentFlags::EXECUTE).bits(),
1152                align: page_size as u64,
1153                offset: 0x3000,
1154                vaddr: 0x1000,
1155                paddr: 0x1000,
1156                filesz: 0x1000,
1157                memsz: 0x1000,
1158            },
1159        ];
1160
1161        assert_matches!(
1162            headers.validate(),
1163            Err(ElfParseError::InvalidProgramHeader(
1164                "load segment overflow the 64-bit memory space"
1165            ))
1166        );
1167    }
1168
1169    #[test]
1170    fn test_parse_program_header_overflow_32() {
1171        let page_size = zx::system_get_page_size() as u32;
1172        let headers = [
1173            Elf32ProgramHeader {
1174                segment_type: SegmentType::Load as u32,
1175                flags: (SegmentFlags::READ | SegmentFlags::EXECUTE).bits(),
1176                align: page_size,
1177                offset: 0x1000,
1178                // Near the end of the address space, will overflow when adding memsz
1179                vaddr: u32::MAX - 0xfff,
1180                paddr: u32::MAX - 0xfff,
1181                filesz: 0x2000,
1182                memsz: 0x2000,
1183            },
1184            Elf32ProgramHeader {
1185                segment_type: SegmentType::Load as u32,
1186                flags: (SegmentFlags::READ | SegmentFlags::EXECUTE).bits(),
1187                align: page_size,
1188                offset: 0x3000,
1189                vaddr: 0x1000,
1190                paddr: 0x1000,
1191                filesz: 0x1000,
1192                memsz: 0x1000,
1193            },
1194        ];
1195
1196        assert_matches!(
1197            headers.validate(),
1198            Err(ElfParseError::InvalidProgramHeader(
1199                "load segment overflow the 32-bit memory space"
1200            ))
1201        );
1202    }
1203}