xmas_elf/
header.rs

1use core::fmt;
2use core::mem;
3
4use {P32, P64, ElfFile};
5use zero::{read, Pod};
6
7
8pub fn parse_header<'a>(input: &'a [u8]) -> Result<Header<'a>, &'static str> {
9    let size_pt1 = mem::size_of::<HeaderPt1>();
10    if input.len() < size_pt1 {
11        return Err("File is shorter than the first ELF header part");
12    }
13
14    let header_1: &'a HeaderPt1 = read(&input[..size_pt1]);
15    if header_1.magic != MAGIC {
16        return Err("Did not find ELF magic number");
17    }
18
19    let header_2 = match header_1.class() {
20        Class::None | Class::Other(_) => return Err("Invalid ELF class"),
21        Class::ThirtyTwo => {
22            let size_pt2 = mem::size_of::<HeaderPt2_<P32>>();
23            if input.len() < size_pt1 + size_pt2 {
24                return Err("File is shorter than ELF headers");
25            }
26            let header_2: &'a HeaderPt2_<P32> =
27                read(&input[size_pt1..size_pt1 + mem::size_of::<HeaderPt2_<P32>>()]);
28            HeaderPt2::Header32(header_2)
29        }
30        Class::SixtyFour => {
31            let size_pt2 = mem::size_of::<HeaderPt2_<P64>>();
32            if input.len() < size_pt1 + size_pt2 {
33                return Err("File is shorter than ELF headers");
34            }
35            let header_2: &'a HeaderPt2_<P64> =
36                read(&input[size_pt1..size_pt1 + mem::size_of::<HeaderPt2_<P64>>()]);
37            HeaderPt2::Header64(header_2)
38        }
39    };
40    Ok(Header {
41        pt1: header_1,
42        pt2: header_2,
43    })
44}
45
46pub const MAGIC: [u8; 4] = [0x7f, b'E', b'L', b'F'];
47
48#[derive(Clone, Copy, Debug)]
49pub struct Header<'a> {
50    pub pt1: &'a HeaderPt1,
51    pub pt2: HeaderPt2<'a>,
52}
53
54// TODO add Header::section_count, because if sh_count = 0, then the real count is in the first section.
55
56impl<'a> fmt::Display for Header<'a> {
57    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58        writeln!(f, "ELF header:")?;
59        writeln!(f, "    magic:            {:?}", self.pt1.magic)?;
60        writeln!(f, "    class:            {:?}", self.pt1.class)?;
61        writeln!(f, "    data:             {:?}", self.pt1.data)?;
62        writeln!(f, "    version:          {:?}", self.pt1.version)?;
63        writeln!(f, "    os abi:           {:?}", self.pt1.os_abi)?;
64        writeln!(f, "    abi version:      {:?}", self.pt1.abi_version)?;
65        writeln!(f, "    padding:          {:?}", self.pt1.padding)?;
66        write!(f, "{}", self.pt2)?;
67        Ok(())
68    }
69}
70
71#[derive(Copy, Clone, Debug)]
72#[repr(C)]
73pub struct HeaderPt1 {
74    pub magic: [u8; 4],
75    pub class: Class_,
76    pub data: Data_,
77    pub version: Version_,
78    pub os_abi: OsAbi_,
79    // Often also just padding.
80    pub abi_version: u8,
81    pub padding: [u8; 7],
82}
83
84unsafe impl Pod for HeaderPt1 {}
85
86impl HeaderPt1 {
87    pub fn class(&self) -> Class {
88        self.class.as_class()
89    }
90
91    pub fn data(&self) -> Data {
92        self.data.as_data()
93    }
94
95    pub fn version(&self) -> Version {
96        self.version.as_version()
97    }
98
99    pub fn os_abi(&self) -> OsAbi {
100        self.os_abi.as_os_abi()
101    }
102}
103
104#[derive(Clone, Copy, Debug)]
105pub enum HeaderPt2<'a> {
106    Header32(&'a HeaderPt2_<P32>),
107    Header64(&'a HeaderPt2_<P64>),
108}
109
110macro_rules! getter {
111    ($name: ident, $typ: ident) => {
112        pub fn $name(&self) -> $typ {
113            match *self {
114                HeaderPt2::Header32(h) => h.$name as $typ,
115                HeaderPt2::Header64(h) => h.$name as $typ,
116            }
117        }
118    }
119}
120
121impl<'a> HeaderPt2<'a> {
122    pub fn size(&self) -> usize {
123        match *self {
124            HeaderPt2::Header32(_) => mem::size_of::<HeaderPt2_<P32>>(),
125            HeaderPt2::Header64(_) => mem::size_of::<HeaderPt2_<P64>>(),
126        }
127    }
128
129    // TODO move to impl Header
130    getter!(type_, Type_);
131    getter!(machine, Machine_);
132    getter!(version, u32);
133    getter!(header_size, u16);
134    getter!(entry_point, u64);
135    getter!(ph_offset, u64);
136    getter!(sh_offset, u64);
137    getter!(ph_entry_size, u16);
138    getter!(ph_count, u16);
139    getter!(sh_entry_size, u16);
140    getter!(sh_count, u16);
141    getter!(sh_str_index, u16);
142}
143
144impl<'a> fmt::Display for HeaderPt2<'a> {
145    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146        match *self {
147            HeaderPt2::Header32(h) => write!(f, "{}", h),
148            HeaderPt2::Header64(h) => write!(f, "{}", h),
149        }
150    }
151}
152
153#[derive(Debug)]
154#[repr(C)]
155pub struct HeaderPt2_<P> {
156    pub type_: Type_,
157    pub machine: Machine_,
158    pub version: u32,
159    pub entry_point: P,
160    pub ph_offset: P,
161    pub sh_offset: P,
162    pub flags: u32,
163    pub header_size: u16,
164    pub ph_entry_size: u16,
165    pub ph_count: u16,
166    pub sh_entry_size: u16,
167    pub sh_count: u16,
168    pub sh_str_index: u16,
169}
170
171unsafe impl<P> Pod for HeaderPt2_<P> {}
172
173impl<P: fmt::Display> fmt::Display for HeaderPt2_<P> {
174    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175        writeln!(f, "    type:             {:?}", self.type_)?;
176        writeln!(f, "    machine:          {:?}", self.machine)?;
177        writeln!(f, "    version:          {}", self.version)?;
178        writeln!(f, "    entry_point:      {}", self.entry_point)?;
179        writeln!(f, "    ph_offset:        {}", self.ph_offset)?;
180        writeln!(f, "    sh_offset:        {}", self.sh_offset)?;
181        writeln!(f, "    flags:            {}", self.flags)?;
182        writeln!(f, "    header_size:      {}", self.header_size)?;
183        writeln!(f, "    ph_entry_size:    {}", self.ph_entry_size)?;
184        writeln!(f, "    ph_count:         {}", self.ph_count)?;
185        writeln!(f, "    sh_entry_size:    {}", self.sh_entry_size)?;
186        writeln!(f, "    sh_count:         {}", self.sh_count)?;
187        writeln!(f, "    sh_str_index:     {}", self.sh_str_index)?;
188        Ok(())
189    }
190}
191
192#[derive(Clone, Copy, Eq, PartialEq)]
193pub struct Class_(u8);
194
195impl Class_ {
196    pub fn as_class(self) -> Class {
197        match self.0 {
198            0 => Class::None,
199            1 => Class::ThirtyTwo,
200            2 => Class::SixtyFour,
201            other => Class::Other(other),
202        }
203    }
204
205    pub fn is_none(self) -> bool {
206        self.0 == 0
207    }
208}
209
210impl fmt::Debug for Class_ {
211    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        self.as_class().fmt(f)
213    }
214}
215
216#[derive(Copy, Clone, Debug, Eq, PartialEq)]
217pub enum Class {
218    None,
219    ThirtyTwo,
220    SixtyFour,
221    Other(u8),
222}
223
224impl Class {
225    pub fn is_none(&self) -> bool {
226        matches!(*self, Class::None)
227    }
228}
229
230#[derive(Clone, Copy)]
231pub struct Data_(u8);
232
233impl Data_ {
234    pub fn as_data(self) -> Data {
235        match self.0 {
236            0 => Data::None,
237            1 => Data::LittleEndian,
238            2 => Data::BigEndian,
239            other => Data::Other(other),
240        }
241    }
242
243    pub fn is_none(self) -> bool {
244        self.0 == 0
245    }
246}
247
248impl fmt::Debug for Data_ {
249    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250        self.as_data().fmt(f)
251    }
252}
253
254#[derive(Copy, Clone, Debug, Eq, PartialEq)]
255pub enum Data {
256    None,
257    LittleEndian,
258    BigEndian,
259    Other(u8),
260}
261
262impl Data {
263    pub fn is_none(&self) -> bool {
264        matches!(*self, Data::None)
265    }
266}
267
268#[derive(Clone, Copy)]
269pub struct Version_(u8);
270
271impl Version_ {
272    pub fn as_version(self) -> Version {
273        match self.0 {
274            0 => Version::None,
275            1 => Version::Current,
276            other => Version::Other(other),
277        }
278    }
279
280    pub fn is_none(self) -> bool {
281        self.0 == 0
282    }
283}
284
285impl fmt::Debug for Version_ {
286    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287        self.as_version().fmt(f)
288    }
289}
290
291#[derive(Copy, Clone, Debug, Eq, PartialEq)]
292pub enum Version {
293    None,
294    Current,
295    Other(u8),
296}
297
298impl Version {
299    pub fn is_none(&self) -> bool {
300        matches!(*self, Version::None)
301    }
302}
303
304#[derive(Clone, Copy)]
305pub struct OsAbi_(u8);
306
307impl OsAbi_ {
308    pub fn as_os_abi(self) -> OsAbi {
309        match self.0 {
310            0x00 => OsAbi::SystemV,
311            0x01 => OsAbi::HpUx,
312            0x02 => OsAbi::NetBSD,
313            0x03 => OsAbi::Linux,
314            0x06 => OsAbi::Solaris,
315            0x07 => OsAbi::Aix,
316            0x08 => OsAbi::Irix,
317            0x09 => OsAbi::FreeBSD,
318            0x0C => OsAbi::OpenBSD,
319            0x0D => OsAbi::OpenVMS,
320            other => OsAbi::Other(other),
321        }
322    }
323}
324
325impl fmt::Debug for OsAbi_ {
326    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
327        self.as_os_abi().fmt(f)
328    }
329}
330
331#[derive(Copy, Clone, Debug, Eq, PartialEq)]
332pub enum OsAbi {
333    // or None
334    SystemV,
335    HpUx,
336    NetBSD,
337    Linux,
338    Solaris,
339    Aix,
340    Irix,
341    FreeBSD,
342    OpenBSD,
343    OpenVMS,
344    Other(u8), // FIXME there are many, many more of these
345}
346
347#[derive(Clone, Copy)]
348pub struct Type_(pub u16);
349
350impl Type_ {
351    pub fn as_type(self) -> Type {
352        match self.0 {
353            0 => Type::None,
354            1 => Type::Relocatable,
355            2 => Type::Executable,
356            3 => Type::SharedObject,
357            4 => Type::Core,
358            x => Type::ProcessorSpecific(x),
359        }
360    }
361}
362
363impl fmt::Debug for Type_ {
364    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
365        self.as_type().fmt(f)
366    }
367}
368
369#[derive(Copy, Clone, Debug, PartialEq, Eq)]
370pub enum Type {
371    None,
372    Relocatable,
373    Executable,
374    SharedObject,
375    Core,
376    ProcessorSpecific(u16), // TODO OsSpecific
377}
378
379#[derive(Clone, Copy)]
380pub struct Machine_(u16);
381
382impl Machine_ {
383    pub fn as_machine(self) -> Machine {
384        match self.0 {
385            0x00 => Machine::None,
386            0x02 => Machine::Sparc,
387            0x03 => Machine::X86,
388            0x08 => Machine::Mips,
389            0x14 => Machine::PowerPC,
390            0x28 => Machine::Arm,
391            0x2A => Machine::SuperH,
392            0x32 => Machine::Ia64,
393            0x3E => Machine::X86_64,
394            0xB7 => Machine::AArch64,
395            0xF3 => Machine::RISC_V,
396            0xF7 => Machine::BPF,
397            other => Machine::Other(other),
398        }
399    }
400}
401
402impl fmt::Debug for Machine_ {
403    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
404        self.as_machine().fmt(f)
405    }
406}
407
408#[allow(non_camel_case_types)]
409#[derive(Debug, Clone, Copy, Eq, PartialEq)]
410pub enum Machine {
411    None,
412    Sparc,
413    X86,
414    Mips,
415    PowerPC,
416    Arm,
417    SuperH,
418    Ia64,
419    X86_64,
420    AArch64,
421    RISC_V,
422    BPF,
423    Other(u16), // FIXME there are many, many more of these
424}
425
426// TODO any more constants that need to go in here?
427
428pub fn sanity_check(file: &ElfFile) -> Result<(), &'static str> {
429    check!(mem::size_of::<HeaderPt1>() == 16);
430    check!(file.header.pt1.magic == MAGIC, "bad magic number");
431    let pt2 = &file.header.pt2;
432    check!(mem::size_of::<HeaderPt1>() + pt2.size() == pt2.header_size() as usize,
433           "header_size does not match size of header");
434    match (&file.header.pt1.class(), &file.header.pt2) {
435        (&Class::None, _) => return Err("No class"),
436        (&Class::ThirtyTwo, &HeaderPt2::Header32(_)) |
437        (&Class::SixtyFour, &HeaderPt2::Header64(_)) => {}
438        _ => return Err("Mismatch between specified and actual class"),
439    }
440    check!(!file.header.pt1.version.is_none(), "no version");
441    check!(!file.header.pt1.data.is_none(), "no data format");
442
443    check!(pt2.ph_offset() + (pt2.ph_entry_size() as u64) * (pt2.ph_count() as u64) <=
444           file.input.len() as u64,
445           "program header table out of range");
446    check!(pt2.sh_offset() + (pt2.sh_entry_size() as u64) * (pt2.sh_count() as u64) <=
447           file.input.len() as u64,
448           "section header table out of range");
449
450    // TODO check that SectionHeader_ is the same size as sh_entry_size, depending on class
451
452    Ok(())
453}