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 &[]
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) }
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 Note64(&'a NoteHeader, &'a [u8]), }
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 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}