elf/
file.rs

1//! Parsing the ELF File Header
2use crate::abi;
3use crate::endian::EndianParse;
4use crate::parse::ParseError;
5
6/// Represents the ELF file word size (32-bit vs 64-bit)
7#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8pub enum Class {
9    ELF32,
10    ELF64,
11}
12
13/// C-style 32-bit ELF File Header definition
14///
15/// These C-style definitions are for users who want to implement their own ELF manipulation logic.
16#[derive(Debug)]
17#[repr(C)]
18pub struct Elf32_Ehdr {
19    pub e_ident: [u8; abi::EI_NIDENT],
20    pub e_type: u16,
21    pub e_machine: u16,
22    pub e_version: u32,
23    pub e_entry: u32,
24    pub e_phoff: u32,
25    pub e_shoff: u32,
26    pub e_flags: u32,
27    pub e_ehsize: u16,
28    pub e_phentsize: u16,
29    pub e_phnum: u16,
30    pub e_shentsize: u16,
31    pub e_shnum: u16,
32    pub e_shstrndx: u16,
33}
34
35/// C-style 64-bit ELF File Header definition
36///
37/// These C-style definitions are for users who want to implement their own ELF manipulation logic.
38#[derive(Debug)]
39#[repr(C)]
40pub struct Elf64_Ehdr {
41    pub e_ident: [u8; abi::EI_NIDENT],
42    pub e_type: u16,
43    pub e_machine: u16,
44    pub e_version: u32,
45    pub e_entry: u64,
46    pub e_phoff: u64,
47    pub e_shoff: u64,
48    pub e_flags: u32,
49    pub e_ehsize: u16,
50    pub e_phentsize: u16,
51    pub e_phnum: u16,
52    pub e_shentsize: u16,
53    pub e_shnum: u16,
54    pub e_shstrndx: u16,
55}
56
57/// Encapsulates the contents of the ELF File Header
58///
59/// The ELF File Header starts off every ELF file and both identifies the
60/// file contents and informs how to interpret said contents. This includes
61/// the width of certain fields (32-bit vs 64-bit), the data endianness, the
62/// file type, and more.
63#[derive(Copy, Clone, Debug, PartialEq, Eq)]
64pub struct FileHeader<E: EndianParse> {
65    /// 32-bit vs 64-bit
66    pub class: Class,
67    // file byte order
68    pub endianness: E,
69    /// elf version
70    pub version: u32,
71    /// OS ABI
72    pub osabi: u8,
73    /// Version of the OS ABI
74    pub abiversion: u8,
75    /// ELF file type
76    pub e_type: u16,
77    /// Target machine architecture
78    pub e_machine: u16,
79    /// Virtual address of program entry point
80    /// This member gives the virtual address to which the system first transfers control,
81    /// thus starting the process. If the file has no associated entry point, this member holds zero.
82    ///
83    /// Note: Type is Elf32_Addr or Elf64_Addr which are either 4 or 8 bytes. We aren't trying to zero-copy
84    /// parse the FileHeader since there's only one per file and its only ~45 bytes anyway, so we use
85    /// u64 for the three Elf*_Addr and Elf*_Off fields here.
86    pub e_entry: u64,
87    /// This member holds the program header table's file offset in bytes. If the file has no program header
88    /// table, this member holds zero.
89    pub e_phoff: u64,
90    /// This member holds the section header table's file offset in bytes. If the file has no section header
91    /// table, this member holds zero.
92    pub e_shoff: u64,
93    /// This member holds processor-specific flags associated with the file. Flag names take the form EF_machine_flag.
94    pub e_flags: u32,
95    /// This member holds the ELF header's size in bytes.
96    pub e_ehsize: u16,
97    /// This member holds the size in bytes of one entry in the file's program header table; all entries are the same size.
98    pub e_phentsize: u16,
99    /// This member holds the number of entries in the program header table. Thus the product of e_phentsize and e_phnum
100    /// gives the table's size in bytes. If a file has no program header table, e_phnum holds the value zero.
101    pub e_phnum: u16,
102    /// This member holds a section header's size in bytes. A section header is one entry in the section header table;
103    /// all entries are the same size.
104    pub e_shentsize: u16,
105    /// This member holds the number of entries in the section header table. Thus the product of e_shentsize and e_shnum
106    /// gives the section header table's size in bytes. If a file has no section header table, e_shnum holds the value zero.
107    ///
108    /// If the number of sections is greater than or equal to SHN_LORESERVE (0xff00), this member has the value zero and
109    /// the actual number of section header table entries is contained in the sh_size field of the section header at index 0.
110    /// (Otherwise, the sh_size member of the initial entry contains 0.)
111    pub e_shnum: u16,
112    /// This member holds the section header table index of the entry associated with the section name string table. If the
113    /// file has no section name string table, this member holds the value SHN_UNDEF.
114    ///
115    /// If the section name string table section index is greater than or equal to SHN_LORESERVE (0xff00), this member has
116    /// the value SHN_XINDEX (0xffff) and the actual index of the section name string table section is contained in the
117    /// sh_link field of the section header at index 0. (Otherwise, the sh_link member of the initial entry contains 0.)
118    pub e_shstrndx: u16,
119}
120
121pub const ELF32_EHDR_TAILSIZE: usize = 36;
122pub const ELF64_EHDR_TAILSIZE: usize = 48;
123
124fn verify_ident(buf: &[u8]) -> Result<(), ParseError> {
125    // Verify the magic number
126    let magic = buf.split_at(abi::EI_CLASS).0;
127    if magic != abi::ELFMAGIC {
128        return Err(ParseError::BadMagic([
129            magic[0], magic[1], magic[2], magic[3],
130        ]));
131    }
132
133    // Verify ELF Version
134    let version = buf[abi::EI_VERSION];
135    if version != abi::EV_CURRENT {
136        return Err(ParseError::UnsupportedVersion((
137            version as u64,
138            abi::EV_CURRENT as u64,
139        )));
140    }
141
142    Ok(())
143}
144
145pub fn parse_ident<E: EndianParse>(data: &[u8]) -> Result<(E, Class, u8, u8), ParseError> {
146    verify_ident(data)?;
147
148    let e_class = data[abi::EI_CLASS];
149    let class = match e_class {
150        abi::ELFCLASS32 => Class::ELF32,
151        abi::ELFCLASS64 => Class::ELF64,
152        _ => {
153            return Err(ParseError::UnsupportedElfClass(e_class));
154        }
155    };
156
157    // Verify endianness is something we know how to parse
158    let file_endian = E::from_ei_data(data[abi::EI_DATA])?;
159
160    Ok((
161        file_endian,
162        class,
163        data[abi::EI_OSABI],
164        data[abi::EI_ABIVERSION],
165    ))
166}
167
168impl<E: EndianParse> FileHeader<E> {
169    pub fn parse_tail(ident: (E, Class, u8, u8), data: &[u8]) -> Result<FileHeader<E>, ParseError> {
170        let (file_endian, class, osabi, abiversion) = ident;
171
172        let mut offset = 0;
173        let e_type = file_endian.parse_u16_at(&mut offset, data)?;
174        let e_machine = file_endian.parse_u16_at(&mut offset, data)?;
175        let version = file_endian.parse_u32_at(&mut offset, data)?;
176
177        let e_entry: u64;
178        let e_phoff: u64;
179        let e_shoff: u64;
180
181        if class == Class::ELF32 {
182            e_entry = file_endian.parse_u32_at(&mut offset, data)? as u64;
183            e_phoff = file_endian.parse_u32_at(&mut offset, data)? as u64;
184            e_shoff = file_endian.parse_u32_at(&mut offset, data)? as u64;
185        } else {
186            e_entry = file_endian.parse_u64_at(&mut offset, data)?;
187            e_phoff = file_endian.parse_u64_at(&mut offset, data)?;
188            e_shoff = file_endian.parse_u64_at(&mut offset, data)?;
189        }
190
191        let e_flags = file_endian.parse_u32_at(&mut offset, data)?;
192        let e_ehsize = file_endian.parse_u16_at(&mut offset, data)?;
193        let e_phentsize = file_endian.parse_u16_at(&mut offset, data)?;
194        let e_phnum = file_endian.parse_u16_at(&mut offset, data)?;
195        let e_shentsize = file_endian.parse_u16_at(&mut offset, data)?;
196        let e_shnum = file_endian.parse_u16_at(&mut offset, data)?;
197        let e_shstrndx = file_endian.parse_u16_at(&mut offset, data)?;
198
199        Ok(FileHeader {
200            class,
201            endianness: file_endian,
202            version,
203            e_type,
204            e_machine,
205            osabi,
206            abiversion,
207            e_entry,
208            e_phoff,
209            e_shoff,
210            e_flags,
211            e_ehsize,
212            e_phentsize,
213            e_phnum,
214            e_shentsize,
215            e_shnum,
216            e_shstrndx,
217        })
218    }
219}
220
221#[cfg(test)]
222mod parse_tests {
223    use super::*;
224    use crate::endian::AnyEndian;
225
226    #[test]
227    fn test_verify_ident_valid() {
228        let data: [u8; abi::EI_NIDENT] = [
229            abi::ELFMAG0,
230            abi::ELFMAG1,
231            abi::ELFMAG2,
232            abi::ELFMAG3,
233            abi::ELFCLASS32,
234            abi::ELFDATA2LSB,
235            abi::EV_CURRENT,
236            abi::ELFOSABI_LINUX,
237            0,
238            0,
239            0,
240            0,
241            0,
242            0,
243            0,
244            0,
245        ];
246        verify_ident(data.as_ref()).expect("Expected Ok result");
247    }
248
249    #[test]
250    fn test_verify_ident_invalid_mag0() {
251        let data: [u8; abi::EI_NIDENT] = [
252            0xFF,
253            abi::ELFMAG1,
254            abi::ELFMAG2,
255            abi::ELFMAG3,
256            abi::ELFCLASS32,
257            abi::ELFDATA2LSB,
258            abi::EV_CURRENT,
259            abi::ELFOSABI_LINUX,
260            0,
261            0,
262            0,
263            0,
264            0,
265            0,
266            0,
267            0,
268        ];
269        let result = verify_ident(data.as_ref()).expect_err("Expected an error");
270        assert!(
271            matches!(result, ParseError::BadMagic(_)),
272            "Unexpected Error type found: {result}"
273        );
274    }
275
276    #[test]
277    fn test_verify_ident_invalid_mag1() {
278        let data: [u8; abi::EI_NIDENT] = [
279            abi::ELFMAG0,
280            0xFF,
281            abi::ELFMAG2,
282            abi::ELFMAG3,
283            abi::ELFCLASS32,
284            abi::ELFDATA2LSB,
285            abi::EV_CURRENT,
286            abi::ELFOSABI_LINUX,
287            0,
288            0,
289            0,
290            0,
291            0,
292            0,
293            0,
294            0,
295        ];
296        let result = verify_ident(data.as_ref()).expect_err("Expected an error");
297        assert!(
298            matches!(result, ParseError::BadMagic(_)),
299            "Unexpected Error type found: {result}"
300        );
301    }
302
303    #[test]
304    fn test_verify_ident_invalid_mag2() {
305        let data: [u8; abi::EI_NIDENT] = [
306            abi::ELFMAG0,
307            abi::ELFMAG1,
308            0xFF,
309            abi::ELFMAG3,
310            abi::ELFCLASS32,
311            abi::ELFDATA2LSB,
312            abi::EV_CURRENT,
313            abi::ELFOSABI_LINUX,
314            0,
315            0,
316            0,
317            0,
318            0,
319            0,
320            0,
321            0,
322        ];
323        let result = verify_ident(data.as_ref()).expect_err("Expected an error");
324        assert!(
325            matches!(result, ParseError::BadMagic(_)),
326            "Unexpected Error type found: {result}"
327        );
328    }
329
330    #[test]
331    fn test_verify_ident_invalid_mag3() {
332        let data: [u8; abi::EI_NIDENT] = [
333            abi::ELFMAG0,
334            abi::ELFMAG1,
335            abi::ELFMAG2,
336            0xFF,
337            abi::ELFCLASS32,
338            abi::ELFDATA2LSB,
339            abi::EV_CURRENT,
340            abi::ELFOSABI_LINUX,
341            0,
342            0,
343            0,
344            0,
345            0,
346            0,
347            0,
348            0,
349        ];
350        let result = verify_ident(data.as_ref()).expect_err("Expected an error");
351        assert!(
352            matches!(result, ParseError::BadMagic(_)),
353            "Unexpected Error type found: {result}"
354        );
355    }
356
357    #[allow(deprecated)]
358    #[test]
359    fn test_verify_ident_invalid_version() {
360        let data: [u8; abi::EI_NIDENT] = [
361            abi::ELFMAG0,
362            abi::ELFMAG1,
363            abi::ELFMAG2,
364            abi::ELFMAG3,
365            abi::ELFCLASS32,
366            abi::ELFDATA2LSB,
367            42,
368            abi::ELFOSABI_LINUX,
369            0,
370            0,
371            0,
372            0,
373            0,
374            0,
375            0,
376            0,
377        ];
378        let result = verify_ident(data.as_ref()).expect_err("Expected an error");
379        assert!(
380            matches!(result, ParseError::UnsupportedVersion((42, 1))),
381            "Unexpected Error type found: {result}"
382        );
383    }
384
385    #[test]
386    fn test_parse_ehdr32_works() {
387        let ident = (AnyEndian::Little, Class::ELF32, abi::ELFOSABI_LINUX, 7u8);
388        let mut tail = [0u8; ELF64_EHDR_TAILSIZE];
389        for (n, elem) in tail.iter_mut().enumerate().take(ELF64_EHDR_TAILSIZE) {
390            *elem = n as u8;
391        }
392
393        assert_eq!(
394            FileHeader::parse_tail(ident, &tail).unwrap(),
395            FileHeader {
396                class: Class::ELF32,
397                endianness: AnyEndian::Little,
398                version: 0x7060504,
399                osabi: abi::ELFOSABI_LINUX,
400                abiversion: 7,
401                e_type: 0x100,
402                e_machine: 0x302,
403                e_entry: 0x0B0A0908,
404                e_phoff: 0x0F0E0D0C,
405                e_shoff: 0x13121110,
406                e_flags: 0x17161514,
407                e_ehsize: 0x1918,
408                e_phentsize: 0x1B1A,
409                e_phnum: 0x1D1C,
410                e_shentsize: 0x1F1E,
411                e_shnum: 0x2120,
412                e_shstrndx: 0x2322,
413            }
414        );
415    }
416
417    #[test]
418    fn test_parse_ehdr32_fuzz_too_short() {
419        let ident = (AnyEndian::Little, Class::ELF32, abi::ELFOSABI_LINUX, 7u8);
420        let tail = [0u8; ELF32_EHDR_TAILSIZE];
421
422        for n in 0..ELF32_EHDR_TAILSIZE {
423            let buf = tail.split_at(n).0;
424            let result = FileHeader::parse_tail(ident, buf).expect_err("Expected an error");
425            assert!(
426                matches!(result, ParseError::SliceReadError(_)),
427                "Unexpected Error type found: {result:?}"
428            );
429        }
430    }
431
432    #[test]
433    fn test_parse_ehdr64_works() {
434        let ident = (AnyEndian::Big, Class::ELF64, abi::ELFOSABI_LINUX, 7u8);
435        let mut tail = [0u8; ELF64_EHDR_TAILSIZE];
436        for (n, elem) in tail.iter_mut().enumerate().take(ELF64_EHDR_TAILSIZE) {
437            *elem = n as u8;
438        }
439
440        assert_eq!(
441            FileHeader::parse_tail(ident, &tail).unwrap(),
442            FileHeader {
443                class: Class::ELF64,
444                endianness: AnyEndian::Big,
445                version: 0x04050607,
446                osabi: abi::ELFOSABI_LINUX,
447                abiversion: 7,
448                e_type: 0x0001,
449                e_machine: 0x0203,
450                e_entry: 0x08090A0B0C0D0E0F,
451                e_phoff: 0x1011121314151617,
452                e_shoff: 0x18191A1B1C1D1E1F,
453                e_flags: 0x20212223,
454                e_ehsize: 0x2425,
455                e_phentsize: 0x2627,
456                e_phnum: 0x2829,
457                e_shentsize: 0x2A2B,
458                e_shnum: 0x2C2D,
459                e_shstrndx: 0x2E2F,
460            }
461        );
462    }
463
464    #[test]
465    fn test_parse_ehdr64_fuzz_too_short() {
466        let ident = (AnyEndian::Little, Class::ELF64, abi::ELFOSABI_LINUX, 7u8);
467        let tail = [0u8; ELF64_EHDR_TAILSIZE];
468
469        for n in 0..ELF64_EHDR_TAILSIZE {
470            let buf = tail.split_at(n).0;
471            let result = FileHeader::parse_tail(ident, buf).expect_err("Expected an error");
472            assert!(
473                matches!(result, ParseError::SliceReadError(_)),
474                "Unexpected Error type found: {result:?}"
475            );
476        }
477    }
478}