Skip to main content

sbpf_disassembler/
program_header.rs

1use {
2    crate::errors::DisassemblerError,
3    object::{Endianness, read::elf::ElfFile64},
4    serde::{Deserialize, Serialize},
5    std::fmt::Debug,
6};
7
8// Program Segment Flags
9pub const PF_X: u8 = 0x01;
10pub const PF_W: u8 = 0x02;
11pub const PF_R: u8 = 0x04;
12
13#[allow(non_camel_case_types)]
14#[derive(Debug, Clone, Serialize, Deserialize)]
15#[repr(u32)]
16pub enum ProgramType {
17    PT_NULL = 0x00,    // Program header table entry unused.
18    PT_LOAD = 0x01,    // Loadable segment.
19    PT_DYNAMIC = 0x02, // Dynamic linking information.
20    PT_INTERP = 0x03,  // Interpreter information.
21    PT_NOTE = 0x04,    // Auxiliary information.
22    PT_SHLIB = 0x05,   // Reserved.
23    PT_PHDR = 0x06,    // Segment containing program header table itself.
24    PT_TLS = 0x07,     // Thread-Local Storage template.
25}
26
27impl TryFrom<u32> for ProgramType {
28    type Error = DisassemblerError;
29
30    fn try_from(value: u32) -> Result<Self, Self::Error> {
31        Ok(match value {
32            0 => Self::PT_NULL,
33            1 => Self::PT_LOAD,
34            2 => Self::PT_DYNAMIC,
35            3 => Self::PT_INTERP,
36            4 => Self::PT_NOTE,
37            5 => Self::PT_SHLIB,
38            6 => Self::PT_PHDR,
39            7 => Self::PT_TLS,
40            _ => return Err(DisassemblerError::InvalidProgramType(value)),
41        })
42    }
43}
44
45impl From<ProgramType> for u32 {
46    fn from(val: ProgramType) -> Self {
47        match val {
48            ProgramType::PT_NULL => 0,
49            ProgramType::PT_LOAD => 1,
50            ProgramType::PT_DYNAMIC => 2,
51            ProgramType::PT_INTERP => 3,
52            ProgramType::PT_NOTE => 4,
53            ProgramType::PT_SHLIB => 5,
54            ProgramType::PT_PHDR => 6,
55            ProgramType::PT_TLS => 7,
56        }
57    }
58}
59
60impl From<ProgramType> for &str {
61    fn from(val: ProgramType) -> Self {
62        match val {
63            ProgramType::PT_NULL => "PT_NULL",
64            ProgramType::PT_LOAD => "PT_LOAD",
65            ProgramType::PT_DYNAMIC => "PT_DYNAMIC",
66            ProgramType::PT_INTERP => "PT_INTERP",
67            ProgramType::PT_NOTE => "PT_NOTE",
68            ProgramType::PT_SHLIB => "PT_SHLIB",
69            ProgramType::PT_PHDR => "PT_PHDR",
70            ProgramType::PT_TLS => "PT_TLS",
71        }
72    }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct ProgramFlags(pub u32);
77
78impl From<u32> for ProgramFlags {
79    fn from(value: u32) -> Self {
80        Self(value & 7)
81    }
82}
83
84impl std::fmt::Display for ProgramFlags {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        let x = match self.0 & PF_X as u32 == PF_X as u32 {
87            true => "X",
88            false => "*",
89        };
90
91        let r = match self.0 & PF_R as u32 == PF_R as u32 {
92            true => "R",
93            false => "*",
94        };
95
96        let w = match self.0 & PF_W as u32 == PF_W as u32 {
97            true => "W",
98            false => "*",
99        };
100        f.write_str(&format!("{}/{}/{}", r, w, x))
101    }
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct ProgramHeader {
106    pub p_type: ProgramType, // An offset to a string in the .shstrtab section that represents the name of this section.
107    pub p_flags: ProgramFlags, // Identifies the type of this header.
108    pub p_offset: u64,       // Offset of the segment in the file image.
109    pub p_vaddr: u64,        // Virtual address of the segment in memory.
110    pub p_paddr: u64, // On systems where physical address is relevant, reserved for segment's physical address.
111    pub p_filesz: u64, // Size in bytes of the section in the file image. May be 0.
112    pub p_memsz: u64, // Size in bytes of the segment in memory. May be 0.
113    pub p_align: u64, // 0 and 1 specify no alignment. Otherwise should be a positive, integral power of 2, with p_vaddr equating p_offset modulus p_align.
114}
115
116impl ProgramHeader {
117    pub fn from_elf_file(
118        elf_file: &ElfFile64<Endianness>,
119    ) -> Result<Vec<Self>, Vec<DisassemblerError>> {
120        let endian = elf_file.endian();
121        let program_headers_data = elf_file.elf_program_headers();
122        let mut errors = Vec::new();
123
124        let mut program_headers = Vec::new();
125        for ph in program_headers_data {
126            let p_type = ProgramType::try_from(ph.p_type.get(endian)).unwrap_or_else(|e| {
127                errors.push(e);
128                ProgramType::PT_NULL
129            });
130            let p_flags = ProgramFlags::from(ph.p_flags.get(endian));
131            let p_offset = ph.p_offset.get(endian);
132            let p_vaddr = ph.p_vaddr.get(endian);
133            let p_paddr = ph.p_paddr.get(endian);
134            let p_filesz = ph.p_filesz.get(endian);
135            let p_memsz = ph.p_memsz.get(endian);
136            let p_align = ph.p_align.get(endian);
137
138            program_headers.push(ProgramHeader {
139                p_type,
140                p_flags,
141                p_offset,
142                p_vaddr,
143                p_paddr,
144                p_filesz,
145                p_memsz,
146                p_align,
147            });
148        }
149
150        if errors.is_empty() {
151            Ok(program_headers)
152        } else {
153            Err(errors)
154        }
155    }
156
157    pub fn to_bytes(&self) -> Vec<u8> {
158        let mut b = (self.p_type.clone() as u32).to_le_bytes().to_vec();
159        b.extend_from_slice(&self.p_flags.0.to_le_bytes());
160        b.extend_from_slice(&self.p_offset.to_le_bytes());
161        b.extend_from_slice(&self.p_vaddr.to_le_bytes());
162        b.extend_from_slice(&self.p_paddr.to_le_bytes());
163        b.extend_from_slice(&self.p_filesz.to_le_bytes());
164        b.extend_from_slice(&self.p_memsz.to_le_bytes());
165        b.extend_from_slice(&self.p_align.to_le_bytes());
166        b
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use {super::*, crate::program::Program, hex_literal::hex};
173
174    #[test]
175    fn test_program_headers() {
176        let original_bytes = hex!(
177            "7F454C460201010000000000000000000300F700010000002001000000000000400000000000000028020000000000000000000040003800030040000600050001000000050000002001000000000000200100000000000020010000000000003000000000000000300000000000000000100000000000000100000004000000C001000000000000C001000000000000C0010000000000003C000000000000003C000000000000000010000000000000020000000600000050010000000000005001000000000000500100000000000070000000000000007000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007912A000000000007911182900000000B7000000010000002D21010000000000B70000000000000095000000000000001E0000000000000004000000000000000600000000000000C0010000000000000B0000000000000018000000000000000500000000000000F0010000000000000A000000000000000C00000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000120001002001000000000000300000000000000000656E747279706F696E7400002E74657874002E64796E737472002E64796E73796D002E64796E616D6963002E73687374727461620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000010000000600000000000000200100000000000020010000000000003000000000000000000000000000000008000000000000000000000000000000170000000600000003000000000000005001000000000000500100000000000070000000000000000400000000000000080000000000000010000000000000000F0000000B0000000200000000000000C001000000000000C001000000000000300000000000000004000000010000000800000000000000180000000000000007000000030000000200000000000000F001000000000000F0010000000000000C00000000000000000000000000000001000000000000000000000000000000200000000300000000000000000000000000000000000000FC010000000000002A00000000000000000000000000000001000000000000000000000000000000"
178        );
179        let program = Program::from_bytes(&original_bytes).unwrap();
180
181        // Verify we have the expected number of program headers.
182        assert_eq!(program.program_headers.len(), 3);
183
184        // Verify that serialized program headers match the original ELF data.
185        for (i, program_header) in program.program_headers.iter().enumerate() {
186            let serialized = program_header.to_bytes();
187            let header_offset = 0x40 + (i * 56);
188            let original_header_bytes = &original_bytes[header_offset..header_offset + 56];
189            assert_eq!(serialized, original_header_bytes,);
190        }
191    }
192
193    #[test]
194    fn test_program_type_conversions() {
195        // Test try_from with all valid values.
196        assert!(matches!(ProgramType::try_from(0), Ok(ProgramType::PT_NULL)));
197        assert!(matches!(ProgramType::try_from(1), Ok(ProgramType::PT_LOAD)));
198        assert!(matches!(
199            ProgramType::try_from(2),
200            Ok(ProgramType::PT_DYNAMIC)
201        ));
202        assert!(matches!(
203            ProgramType::try_from(3),
204            Ok(ProgramType::PT_INTERP)
205        ));
206        assert!(matches!(ProgramType::try_from(4), Ok(ProgramType::PT_NOTE)));
207        assert!(matches!(
208            ProgramType::try_from(5),
209            Ok(ProgramType::PT_SHLIB)
210        ));
211        assert!(matches!(ProgramType::try_from(6), Ok(ProgramType::PT_PHDR)));
212        assert!(matches!(ProgramType::try_from(7), Ok(ProgramType::PT_TLS)));
213
214        // Test try_from with invalid value.
215        assert!(ProgramType::try_from(99).is_err());
216
217        // Test into u32.
218        assert_eq!(u32::from(ProgramType::PT_NULL), 0);
219        assert_eq!(u32::from(ProgramType::PT_LOAD), 1);
220        assert_eq!(u32::from(ProgramType::PT_DYNAMIC), 2);
221        assert_eq!(u32::from(ProgramType::PT_INTERP), 3);
222        assert_eq!(u32::from(ProgramType::PT_NOTE), 4);
223        assert_eq!(u32::from(ProgramType::PT_SHLIB), 5);
224        assert_eq!(u32::from(ProgramType::PT_PHDR), 6);
225        assert_eq!(u32::from(ProgramType::PT_TLS), 7);
226
227        // Test into &str.
228        assert_eq!(<&str>::from(ProgramType::PT_NULL), "PT_NULL");
229        assert_eq!(<&str>::from(ProgramType::PT_LOAD), "PT_LOAD");
230        assert_eq!(<&str>::from(ProgramType::PT_DYNAMIC), "PT_DYNAMIC");
231        assert_eq!(<&str>::from(ProgramType::PT_INTERP), "PT_INTERP");
232        assert_eq!(<&str>::from(ProgramType::PT_NOTE), "PT_NOTE");
233        assert_eq!(<&str>::from(ProgramType::PT_SHLIB), "PT_SHLIB");
234        assert_eq!(<&str>::from(ProgramType::PT_PHDR), "PT_PHDR");
235        assert_eq!(<&str>::from(ProgramType::PT_TLS), "PT_TLS");
236    }
237
238    #[test]
239    fn test_program_flags_display() {
240        // Test different flag combinations.
241        assert_eq!(ProgramFlags::from(0).to_string(), "*/*/*"); // No flags
242        assert_eq!(ProgramFlags::from(1).to_string(), "*/*/X"); // Execute only
243        assert_eq!(ProgramFlags::from(2).to_string(), "*/W/*"); // Write only
244        assert_eq!(ProgramFlags::from(4).to_string(), "R/*/*"); // Read only
245        assert_eq!(ProgramFlags::from(5).to_string(), "R/*/X"); // Read + Execute
246        assert_eq!(ProgramFlags::from(6).to_string(), "R/W/*"); // Read + Write
247        assert_eq!(ProgramFlags::from(7).to_string(), "R/W/X"); // All flags
248    }
249}