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
54impl<'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 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 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 SystemV,
335 HpUx,
336 NetBSD,
337 Linux,
338 Solaris,
339 Aix,
340 Irix,
341 FreeBSD,
342 OpenBSD,
343 OpenVMS,
344 Other(u8), }
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), }
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), }
425
426pub 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 Ok(())
453}