elf/
segment.rs

1//! Parsing the Program Header table aka Segment table aka `Elf_Phdr`
2use crate::endian::EndianParse;
3use crate::file::Class;
4use crate::parse::{ParseAt, ParseError, ParsingTable};
5
6pub type SegmentTable<'data, E> = ParsingTable<'data, E, ProgramHeader>;
7
8/// C-style 32-bit ELF Program Segment Header definition
9///
10/// These C-style definitions are for users who want to implement their own ELF manipulation logic.
11#[derive(Debug)]
12#[repr(C)]
13pub struct Elf32_Phdr {
14    pub p_type: u32,
15    pub p_offset: u32,
16    pub p_vaddr: u32,
17    pub p_paddr: u32,
18    pub p_filesz: u32,
19    pub p_memsz: u32,
20    pub p_flags: u32,
21    pub p_align: u32,
22}
23
24/// C-style 64-bit ELF Program Segment Header definition
25///
26/// These C-style definitions are for users who want to implement their own ELF manipulation logic.
27#[derive(Debug)]
28#[repr(C)]
29pub struct Elf64_Phdr {
30    pub p_type: u32,
31    pub p_flags: u32,
32    pub p_offset: u64,
33    pub p_vaddr: u64,
34    pub p_paddr: u64,
35    pub p_filesz: u64,
36    pub p_memsz: u64,
37    pub p_align: u64,
38}
39
40/// Encapsulates the contents of an ELF Program Header
41///
42/// The program header table is an array of program header structures describing
43/// the various segments for program execution.
44#[derive(Copy, Clone, Debug, PartialEq, Eq)]
45pub struct ProgramHeader {
46    /// Program segment type
47    pub p_type: u32,
48    /// Offset into the ELF file where this segment begins
49    pub p_offset: u64,
50    /// Virtual adress where this segment should be loaded
51    pub p_vaddr: u64,
52    /// Physical address where this segment should be loaded
53    pub p_paddr: u64,
54    /// Size of this segment in the file
55    pub p_filesz: u64,
56    /// Size of this segment in memory
57    pub p_memsz: u64,
58    /// Flags for this segment
59    pub p_flags: u32,
60    /// file and memory alignment
61    pub p_align: u64,
62}
63
64impl ParseAt for ProgramHeader {
65    fn parse_at<E: EndianParse>(
66        endian: E,
67        class: Class,
68        offset: &mut usize,
69        data: &[u8],
70    ) -> Result<Self, ParseError> {
71        if class == Class::ELF32 {
72            return Ok(ProgramHeader {
73                p_type: endian.parse_u32_at(offset, data)?,
74                p_offset: endian.parse_u32_at(offset, data)? as u64,
75                p_vaddr: endian.parse_u32_at(offset, data)? as u64,
76                p_paddr: endian.parse_u32_at(offset, data)? as u64,
77                p_filesz: endian.parse_u32_at(offset, data)? as u64,
78                p_memsz: endian.parse_u32_at(offset, data)? as u64,
79                p_flags: endian.parse_u32_at(offset, data)?,
80                p_align: endian.parse_u32_at(offset, data)? as u64,
81            });
82        }
83
84        // Note: 64-bit fields are in a different order
85        let p_type = endian.parse_u32_at(offset, data)?;
86        let p_flags = endian.parse_u32_at(offset, data)?;
87        let p_offset = endian.parse_u64_at(offset, data)?;
88        let p_vaddr = endian.parse_u64_at(offset, data)?;
89        let p_paddr = endian.parse_u64_at(offset, data)?;
90        let p_filesz = endian.parse_u64_at(offset, data)?;
91        let p_memsz = endian.parse_u64_at(offset, data)?;
92        let p_align = endian.parse_u64_at(offset, data)?;
93        Ok(ProgramHeader {
94            p_type,
95            p_offset,
96            p_vaddr,
97            p_paddr,
98            p_filesz,
99            p_memsz,
100            p_flags,
101            p_align,
102        })
103    }
104
105    #[inline]
106    fn size_for(class: Class) -> usize {
107        match class {
108            Class::ELF32 => 32,
109            Class::ELF64 => 56,
110        }
111    }
112}
113
114impl ProgramHeader {
115    /// Helper method which uses checked integer math to get a tuple of (start, end) for
116    /// the location in bytes for this ProgramHeader's data in the file.
117    /// i.e. (p_offset, p_offset + p_filesz)
118    pub(crate) fn get_file_data_range(&self) -> Result<(usize, usize), ParseError> {
119        let start: usize = self.p_offset.try_into()?;
120        let size: usize = self.p_filesz.try_into()?;
121        let end = start.checked_add(size).ok_or(ParseError::IntegerOverflow)?;
122        Ok((start, end))
123    }
124}
125
126#[cfg(test)]
127mod parse_tests {
128    use super::*;
129    use crate::endian::{BigEndian, LittleEndian};
130    use crate::parse::{test_parse_for, test_parse_fuzz_too_short};
131
132    #[test]
133    fn parse_phdr32_lsb() {
134        test_parse_for(
135            LittleEndian,
136            Class::ELF32,
137            ProgramHeader {
138                p_type: 0x03020100,
139                p_offset: 0x07060504,
140                p_vaddr: 0xB0A0908,
141                p_paddr: 0x0F0E0D0C,
142                p_filesz: 0x13121110,
143                p_memsz: 0x17161514,
144                p_flags: 0x1B1A1918,
145                p_align: 0x1F1E1D1C,
146            },
147        );
148    }
149
150    #[test]
151    fn parse_phdr32_msb() {
152        test_parse_for(
153            BigEndian,
154            Class::ELF32,
155            ProgramHeader {
156                p_type: 0x00010203,
157                p_offset: 0x04050607,
158                p_vaddr: 0x08090A0B,
159                p_paddr: 0x0C0D0E0F,
160                p_filesz: 0x10111213,
161                p_memsz: 0x14151617,
162                p_flags: 0x18191A1B,
163                p_align: 0x1C1D1E1F,
164            },
165        );
166    }
167
168    #[test]
169    fn parse_phdr64_lsb() {
170        test_parse_for(
171            LittleEndian,
172            Class::ELF64,
173            ProgramHeader {
174                p_type: 0x03020100,
175                p_offset: 0x0F0E0D0C0B0A0908,
176                p_vaddr: 0x1716151413121110,
177                p_paddr: 0x1F1E1D1C1B1A1918,
178                p_filesz: 0x2726252423222120,
179                p_memsz: 0x2F2E2D2C2B2A2928,
180                p_flags: 0x07060504,
181                p_align: 0x3736353433323130,
182            },
183        );
184    }
185
186    #[test]
187    fn parse_phdr64_msb() {
188        test_parse_for(
189            BigEndian,
190            Class::ELF64,
191            ProgramHeader {
192                p_type: 0x00010203,
193                p_offset: 0x08090A0B0C0D0E0F,
194                p_vaddr: 0x1011121314151617,
195                p_paddr: 0x18191A1B1C1D1E1F,
196                p_filesz: 0x2021222324252627,
197                p_memsz: 0x28292A2B2C2D2E2F,
198                p_flags: 0x04050607,
199                p_align: 0x3031323334353637,
200            },
201        );
202    }
203
204    #[test]
205    fn parse_phdr32_lsb_fuzz_too_short() {
206        test_parse_fuzz_too_short::<_, ProgramHeader>(LittleEndian, Class::ELF32);
207    }
208
209    #[test]
210    fn parse_phdr32_msb_fuzz_too_short() {
211        test_parse_fuzz_too_short::<_, ProgramHeader>(BigEndian, Class::ELF32);
212    }
213
214    #[test]
215    fn parse_phdr64_lsb_fuzz_too_short() {
216        test_parse_fuzz_too_short::<_, ProgramHeader>(LittleEndian, Class::ELF64);
217    }
218
219    #[test]
220    fn parse_phdr64_msb_fuzz_too_short() {
221        test_parse_fuzz_too_short::<_, ProgramHeader>(BigEndian, Class::ELF64);
222    }
223}