xmas_elf/
program.rs

1use {ElfFile, P32, P64};
2use zero::{read, read_array, Pod};
3use header::{Class, Header};
4use dynamic::Dynamic;
5use sections::NoteHeader;
6
7use core::mem;
8use core::fmt;
9
10
11pub fn parse_program_header<'a>(input: &'a [u8],
12                                header: Header<'a>,
13                                index: u16)
14                                -> Result<ProgramHeader<'a>, &'static str> {
15    let pt2 = &header.pt2;
16    if !(index < pt2.ph_count() && pt2.ph_offset() > 0 && pt2.ph_entry_size() > 0) {
17        return Err("There are no program headers in this file")
18    }
19
20    let start = pt2.ph_offset() as usize + index as usize * pt2.ph_entry_size() as usize;
21    let end = start + pt2.ph_entry_size() as usize;
22
23    match header.pt1.class() {
24        Class::ThirtyTwo => {
25            Ok(ProgramHeader::Ph32(read(&input[start..end])))
26        }
27        Class::SixtyFour => {
28            Ok(ProgramHeader::Ph64(read(&input[start..end])))
29        }
30        Class::None | Class::Other(_) => unreachable!(),
31    }
32}
33
34#[derive(Debug, Clone)]
35pub struct ProgramIter<'b, 'a: 'b> {
36    pub file: &'b ElfFile<'a>,
37    pub next_index: u16,
38}
39
40impl<'b, 'a> Iterator for ProgramIter<'b, 'a> {
41    type Item = ProgramHeader<'a>;
42
43    fn next(&mut self) -> Option<Self::Item> {
44        let count = self.file.header.pt2.ph_count();
45        if self.next_index >= count {
46            return None;
47        }
48
49        let result = self.file.program_header(self.next_index);
50        self.next_index += 1;
51        result.ok()
52    }
53}
54
55#[derive(Copy, Clone, Debug)]
56pub enum ProgramHeader<'a> {
57    Ph32(&'a ProgramHeader32),
58    Ph64(&'a ProgramHeader64),
59}
60
61#[derive(Copy, Clone, Debug, Default)]
62#[repr(C)]
63pub struct ProgramHeader32 {
64    pub type_: Type_,
65    pub offset: u32,
66    pub virtual_addr: u32,
67    pub physical_addr: u32,
68    pub file_size: u32,
69    pub mem_size: u32,
70    pub flags: Flags,
71    pub align: u32,
72}
73
74unsafe impl Pod for ProgramHeader32 {}
75
76#[derive(Copy, Clone, Debug, Default)]
77#[repr(C)]
78pub struct ProgramHeader64 {
79    pub type_: Type_,
80    pub flags: Flags,
81    pub offset: u64,
82    pub virtual_addr: u64,
83    pub physical_addr: u64,
84    pub file_size: u64,
85    pub mem_size: u64,
86    pub align: u64,
87}
88
89unsafe impl Pod for ProgramHeader64 {}
90
91macro_rules! getter {
92    ($name: ident, $typ: ident) => {
93        pub fn $name(&self) -> $typ {
94            match *self {
95                ProgramHeader::Ph32(h) => h.$name as $typ,
96                ProgramHeader::Ph64(h) => h.$name as $typ,
97            }
98        }
99    }
100}
101
102impl<'a> ProgramHeader<'a> {
103    pub fn get_type(&self) -> Result<Type, &'static str> {
104        match *self {
105            ProgramHeader::Ph32(ph) => ph.get_type(),
106            ProgramHeader::Ph64(ph) => ph.get_type(),
107        }
108    }
109
110    pub fn get_data(&self, elf_file: &ElfFile<'a>) -> Result<SegmentData<'a>, &'static str> {
111        match *self {
112            ProgramHeader::Ph32(ph) => ph.get_data(elf_file),
113            ProgramHeader::Ph64(ph) => ph.get_data(elf_file),
114        }
115    }
116
117    getter!(align, u64);
118    getter!(file_size, u64);
119    getter!(mem_size, u64);
120    getter!(offset, u64);
121    getter!(physical_addr, u64);
122    getter!(virtual_addr, u64);
123    getter!(flags, Flags);
124}
125
126impl<'a> fmt::Display for ProgramHeader<'a> {
127    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128        match *self {
129            ProgramHeader::Ph32(ph) => ph.fmt(f),
130            ProgramHeader::Ph64(ph) => ph.fmt(f),
131        }
132    }
133}
134macro_rules! ph_impl {
135    ($ph: ident) => {
136        impl $ph {
137            pub fn get_type(&self) -> Result<Type, &'static str> {
138                self.type_.as_type()
139            }
140
141            pub fn get_data<'a>(&self, elf_file: &ElfFile<'a>) -> Result<SegmentData<'a>, &'static str> {
142                self.get_type().map(|typ| match typ {
143                    Type::Null => SegmentData::Empty,
144                    Type::Load | Type::Interp | Type::ShLib | Type::Phdr | Type::Tls |
145                    Type::GnuRelro | Type::OsSpecific(_) | Type::ProcessorSpecific(_) => {
146                        SegmentData::Undefined(self.raw_data(elf_file))
147                    }
148                    Type::Dynamic => {
149                        let data = self.raw_data(elf_file);
150                        match elf_file.header.pt1.class() {
151                            Class::ThirtyTwo => SegmentData::Dynamic32(read_array(data)),
152                            Class::SixtyFour => SegmentData::Dynamic64(read_array(data)),
153                            Class::None | Class::Other(_) => unreachable!(),
154                        }
155                    }
156                    Type::Note => {
157                        let data = self.raw_data(elf_file);
158                        match elf_file.header.pt1.class() {
159                            Class::ThirtyTwo => unimplemented!(),
160                            Class::SixtyFour => {
161                                let header: &'a NoteHeader = read(&data[0..12]);
162                                let index = &data[12..];
163                                SegmentData::Note64(header, index)
164                            }
165                            Class::None | Class::Other(_) => unreachable!(),
166                        }
167                    }
168                })
169            }
170
171            pub fn raw_data<'a>(&self, elf_file: &ElfFile<'a>) -> &'a [u8] {
172                assert!(self.get_type().map(|typ| typ != Type::Null).unwrap_or(false));
173                if self.file_size == 0 {
174                    // When size is 0 it's not guaranteed that offset is not
175                    // outside of elf_file.input range.
176                    &[]
177                } else {
178                    &elf_file.input[self.offset as usize..(self.offset + self.file_size) as usize]
179                }
180            }
181        }
182
183        impl fmt::Display for $ph {
184            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185                writeln!(f, "Program header:")?;
186                writeln!(f, "    type:             {:?}", self.get_type())?;
187                writeln!(f, "    flags:            {}", self.flags)?;
188                writeln!(f, "    offset:           {:#x}", self.offset)?;
189                writeln!(f, "    virtual address:  {:#x}", self.virtual_addr)?;
190                writeln!(f, "    physical address: {:#x}", self.physical_addr)?;
191                writeln!(f, "    file size:        {:#x}", self.file_size)?;
192                writeln!(f, "    memory size:      {:#x}", self.mem_size)?;
193                writeln!(f, "    align:            {:#x}", self.align)?;
194                Ok(())
195            }
196        }
197    }
198}
199
200ph_impl!(ProgramHeader32);
201ph_impl!(ProgramHeader64);
202
203#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
204pub struct Flags(pub u32);
205
206impl Flags {
207    pub fn is_execute(&self) -> bool {
208        self.0 & FLAG_X == FLAG_X
209    }
210
211    pub fn is_write(&self) -> bool {
212        self.0 & FLAG_W == FLAG_W
213    }
214
215    pub fn is_read(&self) -> bool {
216        self.0 & FLAG_R == FLAG_R
217    }
218}
219
220impl fmt::Display for Flags {
221    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222        write!(f, "{}{}{}",
223               if self.0 & FLAG_X == FLAG_X { 'X' } else { ' ' },
224               if self.0 & FLAG_W == FLAG_W { 'W' } else { ' ' },
225               if self.0 & FLAG_R == FLAG_R { 'R' } else { ' ' })
226    }
227}
228
229impl fmt::LowerHex for Flags {
230    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
231        let val = self.0;
232
233        write!(f, "{:#x}", val) // delegate to i32's implementation
234    }
235}
236
237impl fmt::UpperHex for Flags {
238    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
239        let val = self.0;
240
241        write!(f, "{:#X}", val)
242    }
243}
244
245#[derive(Copy, Clone, Default)]
246pub struct Type_(u32);
247
248#[derive(Copy, Clone, Debug, Eq, PartialEq)]
249pub enum Type {
250    Null,
251    Load,
252    Dynamic,
253    Interp,
254    Note,
255    ShLib,
256    Phdr,
257    Tls,
258    GnuRelro,
259    OsSpecific(u32),
260    ProcessorSpecific(u32),
261}
262
263impl Type_ {
264    fn as_type(&self) -> Result<Type, &'static str> {
265        match self.0 {
266            0 => Ok(Type::Null),
267            1 => Ok(Type::Load),
268            2 => Ok(Type::Dynamic),
269            3 => Ok(Type::Interp),
270            4 => Ok(Type::Note),
271            5 => Ok(Type::ShLib),
272            6 => Ok(Type::Phdr),
273            7 => Ok(Type::Tls),
274            TYPE_GNU_RELRO => Ok(Type::GnuRelro),
275            t if (TYPE_LOOS..=TYPE_HIOS).contains(&t) => Ok(Type::OsSpecific(t)),
276            t if (TYPE_LOPROC..=TYPE_HIPROC).contains(&t) => Ok(Type::ProcessorSpecific(t)),
277            _ => Err("Invalid type"),
278        }
279    }
280}
281
282impl fmt::Debug for Type_ {
283    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284        self.as_type().fmt(f)
285    }
286}
287
288#[derive(Debug)]
289pub enum SegmentData<'a> {
290    Empty,
291    Undefined(&'a [u8]),
292    Dynamic32(&'a [Dynamic<P32>]),
293    Dynamic64(&'a [Dynamic<P64>]),
294    // Note32 uses 4-byte words, which I'm not sure how to manage.
295    // The pointer is to the start of the name field in the note.
296    Note64(&'a NoteHeader, &'a [u8]), /* TODO Interp and Phdr should probably be defined some how, but I can't find the details. */
297}
298
299pub const TYPE_LOOS: u32 = 0x60000000;
300pub const TYPE_HIOS: u32 = 0x6fffffff;
301pub const TYPE_LOPROC: u32 = 0x70000000;
302pub const TYPE_HIPROC: u32 = 0x7fffffff;
303pub const TYPE_GNU_RELRO: u32 = TYPE_LOOS + 0x474e552;
304
305pub const FLAG_X: u32 = 0x1;
306pub const FLAG_W: u32 = 0x2;
307pub const FLAG_R: u32 = 0x4;
308pub const FLAG_MASKOS: u32 = 0x0ff00000;
309pub const FLAG_MASKPROC: u32 = 0xf0000000;
310
311pub fn sanity_check<'a>(ph: ProgramHeader<'a>, elf_file: &ElfFile<'a>) -> Result<(), &'static str> {
312    let header = elf_file.header;
313    match ph {
314        ProgramHeader::Ph32(ph) => {
315            check!(mem::size_of_val(ph) == header.pt2.ph_entry_size() as usize,
316                   "program header size mismatch");
317            check!(((ph.offset + ph.file_size) as usize) < elf_file.input.len(),
318                   "entry point out of range");
319            check!(ph.get_type()? != Type::ShLib, "Shouldn't use ShLib");
320            if ph.align > 1 {
321                check!(ph.virtual_addr % ph.align == ph.offset % ph.align,
322                       "Invalid combination of virtual_addr, offset, and align");
323            }
324        }
325        ProgramHeader::Ph64(ph) => {
326            check!(mem::size_of_val(ph) == header.pt2.ph_entry_size() as usize,
327                   "program header size mismatch");
328            check!(((ph.offset + ph.file_size) as usize) < elf_file.input.len(),
329                   "entry point out of range");
330            check!(ph.get_type()? != Type::ShLib, "Shouldn't use ShLib");
331            if ph.align > 1 {
332                // println!("{} {} {}", ph.virtual_addr, ph.offset, ph.align);
333                check!(ph.virtual_addr % ph.align == ph.offset % ph.align,
334                       "Invalid combination of virtual_addr, offset, and align");
335            }
336        }
337    }
338
339    Ok(())
340}