Skip to main content

sbpf_disassembler/
section_header.rs

1use {
2    crate::{errors::DisassemblerError, section_header_entry::SectionHeaderEntry},
3    object::{Endianness, read::elf::ElfFile64},
4    serde::{Deserialize, Serialize},
5    std::fmt::{Debug, Display},
6};
7
8#[allow(non_camel_case_types)]
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[repr(u32)]
11pub enum SectionHeaderType {
12    SHT_NULL = 0x00,           // Section header table entry unused
13    SHT_PROGBITS = 0x01,       // Program data
14    SHT_SYMTAB = 0x02,         // Symbol table
15    SHT_STRTAB = 0x03,         // String table
16    SHT_RELA = 0x04,           // Relocation entries with addends
17    SHT_HASH = 0x05,           // Symbol hash table
18    SHT_DYNAMIC = 0x06,        // Dynamic linking information
19    SHT_NOTE = 0x07,           // Notes
20    SHT_NOBITS = 0x08,         // Program space with no data (bss)
21    SHT_REL = 0x09,            // Relocation entries, no addends
22    SHT_SHLIB = 0x0A,          // Reserved
23    SHT_DYNSYM = 0x0B,         // Dynamic linker symbol table
24    SHT_INIT_ARRAY = 0x0E,     // Array of constructors
25    SHT_FINI_ARRAY = 0x0F,     // Array of destructors
26    SHT_PREINIT_ARRAY = 0x10,  // Array of pre-constructors
27    SHT_GROUP = 0x11,          // Section group
28    SHT_SYMTAB_SHNDX = 0x12,   // Extended section indices
29    SHT_NUM = 0x13,            // Number of defined types.
30    SHT_GNU_HASH = 0x6ffffff6, // GNU Hash
31}
32
33impl TryFrom<u32> for SectionHeaderType {
34    type Error = DisassemblerError;
35
36    fn try_from(value: u32) -> Result<Self, Self::Error> {
37        Ok(match value {
38            0x00 => Self::SHT_NULL,
39            0x01 => Self::SHT_PROGBITS,
40            0x02 => Self::SHT_SYMTAB,
41            0x03 => Self::SHT_STRTAB,
42            0x04 => Self::SHT_RELA,
43            0x05 => Self::SHT_HASH,
44            0x06 => Self::SHT_DYNAMIC,
45            0x07 => Self::SHT_NOTE,
46            0x08 => Self::SHT_NOBITS,
47            0x09 => Self::SHT_REL,
48            0x0A => Self::SHT_SHLIB,
49            0x0B => Self::SHT_DYNSYM,
50            0x0E => Self::SHT_INIT_ARRAY,
51            0x0F => Self::SHT_FINI_ARRAY,
52            0x10 => Self::SHT_PREINIT_ARRAY,
53            0x11 => Self::SHT_GROUP,
54            0x12 => Self::SHT_SYMTAB_SHNDX,
55            0x13 => Self::SHT_NUM,
56            0x6ffffff6 => Self::SHT_GNU_HASH,
57            _ => return Err(DisassemblerError::InvalidSectionHeaderType(value)),
58        })
59    }
60}
61
62impl Display for SectionHeaderType {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        f.write_str(Into::<&str>::into(self.clone()))
65    }
66}
67
68impl From<SectionHeaderType> for &str {
69    fn from(val: SectionHeaderType) -> Self {
70        match val {
71            SectionHeaderType::SHT_NULL => "SHT_NULL",
72            SectionHeaderType::SHT_PROGBITS => "SHT_PROGBITS",
73            SectionHeaderType::SHT_SYMTAB => "SHT_SYMTAB",
74            SectionHeaderType::SHT_STRTAB => "SHT_STRTAB",
75            SectionHeaderType::SHT_RELA => "SHT_RELA",
76            SectionHeaderType::SHT_HASH => "SHT_HASH",
77            SectionHeaderType::SHT_DYNAMIC => "SHT_DYNAMIC",
78            SectionHeaderType::SHT_NOTE => "SHT_NOTE",
79            SectionHeaderType::SHT_NOBITS => "SHT_NOBITS",
80            SectionHeaderType::SHT_REL => "SHT_REL",
81            SectionHeaderType::SHT_SHLIB => "SHT_SHLIB",
82            SectionHeaderType::SHT_DYNSYM => "SHT_DYNSYM",
83            SectionHeaderType::SHT_INIT_ARRAY => "SHT_INIT_ARRAY",
84            SectionHeaderType::SHT_FINI_ARRAY => "SHT_FINI_ARRAY",
85            SectionHeaderType::SHT_PREINIT_ARRAY => "SHT_PREINIT_ARRAY",
86            SectionHeaderType::SHT_GROUP => "SHT_GROUP",
87            SectionHeaderType::SHT_SYMTAB_SHNDX => "SHT_SYMTAB_SHNDX",
88            SectionHeaderType::SHT_NUM => "SHT_NUM",
89            SectionHeaderType::SHT_GNU_HASH => "SHT_GNU_HASH",
90        }
91    }
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct SectionHeader {
96    pub sh_name: u32, // An offset to a string in the .shstrtab section that represents the name of this section.
97    pub sh_type: SectionHeaderType, // Identifies the type of this header.
98    pub sh_flags: u64, // Identifies the attributes of the section.
99    pub sh_addr: u64, // Virtual address of the section in memory, for sections that are loaded.
100    pub sh_offset: u64, // Offset of the section in the file image.
101    pub sh_size: u64, // Size in bytes of the section in the file image. May be 0.
102    pub sh_link: u32, // Contains the section index of an associated section. This field is used for several purposes, depending on the type of section.
103    pub sh_info: u32, // Contains extra information about the section. This field is used for several purposes, depending on the type of section.
104    pub sh_addralign: u64, // Contains the required alignment of the section. This field must be a power of two.
105    pub sh_entsize: u64, // Contains the size, in bytes, of each entry, for sections that contain fixed-size entries. Otherwise, this field contains zero.
106}
107
108impl SectionHeader {
109    pub fn from_elf_file(
110        elf_file: &ElfFile64<Endianness>,
111    ) -> Result<(Vec<Self>, Vec<SectionHeaderEntry>), Vec<DisassemblerError>> {
112        let endian = elf_file.endian();
113        let section_headers_data: Vec<_> = elf_file.elf_section_table().iter().collect();
114
115        let mut errors = Vec::new();
116
117        let mut section_headers = Vec::new();
118        for sh in section_headers_data.iter() {
119            let sh_name = sh.sh_name.get(endian);
120            let sh_type = SectionHeaderType::try_from(sh.sh_type.get(endian)).unwrap_or_else(|e| {
121                errors.push(e);
122                SectionHeaderType::SHT_NULL
123            });
124            let sh_flags = sh.sh_flags.get(endian);
125            let sh_addr = sh.sh_addr.get(endian);
126            let sh_offset = sh.sh_offset.get(endian);
127            let sh_size = sh.sh_size.get(endian);
128            let sh_link = sh.sh_link.get(endian);
129            let sh_info = sh.sh_info.get(endian);
130            let sh_addralign = sh.sh_addralign.get(endian);
131            let sh_entsize = sh.sh_entsize.get(endian);
132
133            section_headers.push(SectionHeader {
134                sh_name,
135                sh_type,
136                sh_flags,
137                sh_addr,
138                sh_offset,
139                sh_size,
140                sh_link,
141                sh_info,
142                sh_addralign,
143                sh_entsize,
144            });
145        }
146
147        // v3 binaries omit the section header table entirely. With no section
148        // headers there are no names to resolve, so return empty here; the
149        // caller reconstructs .text/.rodata views from the program headers.
150        if section_headers.is_empty() {
151            return Ok((Vec::new(), Vec::new()));
152        }
153
154        let data = elf_file.data();
155        let elf_header = elf_file.elf_header();
156        let e_shstrndx = elf_header.e_shstrndx.get(endian);
157        let Some(shstrndx) = section_headers.get(e_shstrndx as usize) else {
158            errors.push(DisassemblerError::InvalidShstrndx {
159                shstrndx: e_shstrndx,
160                shnum: section_headers.len(),
161            });
162            return Err(errors);
163        };
164        let strtab_start = shstrndx.sh_offset as usize;
165        let strtab_end = strtab_start.saturating_add(shstrndx.sh_size as usize);
166        let Some(shstrndx_value) = data.get(strtab_start..strtab_end) else {
167            errors.push(DisassemblerError::SectionDataOutOfBounds {
168                section: ".shstrtab".to_string(),
169                offset: shstrndx.sh_offset,
170                size: shstrndx.sh_size,
171                file_len: data.len(),
172            });
173            return Err(errors);
174        };
175        let shstrndx_value = shstrndx_value.to_vec();
176
177        let mut section_header_entries = Vec::with_capacity(section_headers.len());
178        for s in &section_headers {
179            let current_offset = s.sh_name as usize;
180
181            // Find the null terminator for this string.
182            let label = match shstrndx_value.get(current_offset..) {
183                Some(label_bytes) if !label_bytes.is_empty() => {
184                    let null_pos = label_bytes
185                        .iter()
186                        .position(|&b| b == 0)
187                        .unwrap_or(label_bytes.len());
188                    String::from_utf8(
189                        label_bytes[..=null_pos.min(label_bytes.len().saturating_sub(1))].to_vec(),
190                    )
191                    .unwrap_or("default".to_string())
192                }
193                _ => {
194                    errors.push(DisassemblerError::InvalidSectionName {
195                        sh_name: s.sh_name,
196                        strtab_len: shstrndx_value.len(),
197                    });
198                    "default".to_string()
199                }
200            };
201
202            let data_start = s.sh_offset as usize;
203            let data_end = data_start.saturating_add(s.sh_size as usize);
204            let section_data = match data.get(data_start..data_end) {
205                Some(d) => d.to_vec(),
206                None => {
207                    errors.push(DisassemblerError::SectionDataOutOfBounds {
208                        section: label.trim_end_matches('\0').to_string(),
209                        offset: s.sh_offset,
210                        size: s.sh_size,
211                        file_len: data.len(),
212                    });
213                    // Best effort: keep whatever bytes exist at the offset.
214                    data.get(data_start..)
215                        .map(<[u8]>::to_vec)
216                        .unwrap_or_default()
217                }
218            };
219
220            match SectionHeaderEntry::new(label, s.sh_offset as usize, section_data) {
221                Ok(entry) => section_header_entries.push(entry),
222                Err(e) => errors.push(e),
223            }
224        }
225
226        if errors.is_empty() {
227            Ok((section_headers, section_header_entries))
228        } else {
229            Err(errors)
230        }
231    }
232
233    pub fn to_bytes(&self) -> Vec<u8> {
234        let mut b = self.sh_name.to_le_bytes().to_vec();
235        b.extend_from_slice(&(self.sh_type.clone() as u32).to_le_bytes());
236        b.extend_from_slice(&self.sh_flags.to_le_bytes());
237        b.extend_from_slice(&self.sh_addr.to_le_bytes());
238        b.extend_from_slice(&self.sh_offset.to_le_bytes());
239        b.extend_from_slice(&self.sh_size.to_le_bytes());
240        b.extend_from_slice(&self.sh_link.to_le_bytes());
241        b.extend_from_slice(&self.sh_info.to_le_bytes());
242        b.extend_from_slice(&self.sh_addralign.to_le_bytes());
243        b.extend_from_slice(&self.sh_entsize.to_le_bytes());
244        b
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use {super::*, crate::program::Program, hex_literal::hex};
251
252    #[test]
253    fn test_section_headers() {
254        let program = Program::from_bytes(&hex!("7F454C460201010000000000000000000300F700010000002001000000000000400000000000000028020000000000000000000040003800030040000600050001000000050000002001000000000000200100000000000020010000000000003000000000000000300000000000000000100000000000000100000004000000C001000000000000C001000000000000C0010000000000003C000000000000003C000000000000000010000000000000020000000600000050010000000000005001000000000000500100000000000070000000000000007000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007912A000000000007911182900000000B7000000010000002D21010000000000B70000000000000095000000000000001E0000000000000004000000000000000600000000000000C0010000000000000B0000000000000018000000000000000500000000000000F0010000000000000A000000000000000C00000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000120001002001000000000000300000000000000000656E747279706F696E7400002E74657874002E64796E737472002E64796E73796D002E64796E616D6963002E73687374727461620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000010000000600000000000000200100000000000020010000000000003000000000000000000000000000000008000000000000000000000000000000170000000600000003000000000000005001000000000000500100000000000070000000000000000400000000000000080000000000000010000000000000000F0000000B0000000200000000000000C001000000000000C001000000000000300000000000000004000000010000000800000000000000180000000000000007000000030000000200000000000000F001000000000000F0010000000000000C00000000000000000000000000000001000000000000000000000000000000200000000300000000000000000000000000000000000000FC010000000000002A00000000000000000000000000000001000000000000000000000000000000")).unwrap();
255
256        // Verify we have the expected number of section headers.
257        assert_eq!(program.section_headers.len(), 6);
258        assert_eq!(program.section_header_entries.len(), 6);
259    }
260
261    #[test]
262    fn test_section_header_type_conversions() {
263        // Test all valid TryFrom conversions.
264        assert!(matches!(
265            SectionHeaderType::try_from(0x00),
266            Ok(SectionHeaderType::SHT_NULL)
267        ));
268        assert!(matches!(
269            SectionHeaderType::try_from(0x01),
270            Ok(SectionHeaderType::SHT_PROGBITS)
271        ));
272        assert!(matches!(
273            SectionHeaderType::try_from(0x02),
274            Ok(SectionHeaderType::SHT_SYMTAB)
275        ));
276        assert!(matches!(
277            SectionHeaderType::try_from(0x03),
278            Ok(SectionHeaderType::SHT_STRTAB)
279        ));
280        assert!(matches!(
281            SectionHeaderType::try_from(0x04),
282            Ok(SectionHeaderType::SHT_RELA)
283        ));
284        assert!(matches!(
285            SectionHeaderType::try_from(0x05),
286            Ok(SectionHeaderType::SHT_HASH)
287        ));
288        assert!(matches!(
289            SectionHeaderType::try_from(0x06),
290            Ok(SectionHeaderType::SHT_DYNAMIC)
291        ));
292        assert!(matches!(
293            SectionHeaderType::try_from(0x07),
294            Ok(SectionHeaderType::SHT_NOTE)
295        ));
296        assert!(matches!(
297            SectionHeaderType::try_from(0x08),
298            Ok(SectionHeaderType::SHT_NOBITS)
299        ));
300        assert!(matches!(
301            SectionHeaderType::try_from(0x09),
302            Ok(SectionHeaderType::SHT_REL)
303        ));
304        assert!(matches!(
305            SectionHeaderType::try_from(0x0A),
306            Ok(SectionHeaderType::SHT_SHLIB)
307        ));
308        assert!(matches!(
309            SectionHeaderType::try_from(0x0B),
310            Ok(SectionHeaderType::SHT_DYNSYM)
311        ));
312        assert!(matches!(
313            SectionHeaderType::try_from(0x0E),
314            Ok(SectionHeaderType::SHT_INIT_ARRAY)
315        ));
316        assert!(matches!(
317            SectionHeaderType::try_from(0x0F),
318            Ok(SectionHeaderType::SHT_FINI_ARRAY)
319        ));
320        assert!(matches!(
321            SectionHeaderType::try_from(0x10),
322            Ok(SectionHeaderType::SHT_PREINIT_ARRAY)
323        ));
324        assert!(matches!(
325            SectionHeaderType::try_from(0x11),
326            Ok(SectionHeaderType::SHT_GROUP)
327        ));
328        assert!(matches!(
329            SectionHeaderType::try_from(0x12),
330            Ok(SectionHeaderType::SHT_SYMTAB_SHNDX)
331        ));
332        assert!(matches!(
333            SectionHeaderType::try_from(0x13),
334            Ok(SectionHeaderType::SHT_NUM)
335        ));
336        assert!(matches!(
337            SectionHeaderType::try_from(0x6ffffff6),
338            Ok(SectionHeaderType::SHT_GNU_HASH)
339        ));
340
341        // Test invalid value
342        assert!(SectionHeaderType::try_from(0xFF).is_err());
343    }
344
345    #[test]
346    fn test_section_header_type_to_str() {
347        // Test all Into<&str> conversions.
348        assert_eq!(<&str>::from(SectionHeaderType::SHT_NULL), "SHT_NULL");
349        assert_eq!(
350            <&str>::from(SectionHeaderType::SHT_PROGBITS),
351            "SHT_PROGBITS"
352        );
353        assert_eq!(<&str>::from(SectionHeaderType::SHT_SYMTAB), "SHT_SYMTAB");
354        assert_eq!(<&str>::from(SectionHeaderType::SHT_STRTAB), "SHT_STRTAB");
355        assert_eq!(<&str>::from(SectionHeaderType::SHT_RELA), "SHT_RELA");
356        assert_eq!(<&str>::from(SectionHeaderType::SHT_HASH), "SHT_HASH");
357        assert_eq!(<&str>::from(SectionHeaderType::SHT_DYNAMIC), "SHT_DYNAMIC");
358        assert_eq!(<&str>::from(SectionHeaderType::SHT_NOTE), "SHT_NOTE");
359        assert_eq!(<&str>::from(SectionHeaderType::SHT_NOBITS), "SHT_NOBITS");
360        assert_eq!(<&str>::from(SectionHeaderType::SHT_REL), "SHT_REL");
361        assert_eq!(<&str>::from(SectionHeaderType::SHT_SHLIB), "SHT_SHLIB");
362        assert_eq!(<&str>::from(SectionHeaderType::SHT_DYNSYM), "SHT_DYNSYM");
363        assert_eq!(
364            <&str>::from(SectionHeaderType::SHT_INIT_ARRAY),
365            "SHT_INIT_ARRAY"
366        );
367        assert_eq!(
368            <&str>::from(SectionHeaderType::SHT_FINI_ARRAY),
369            "SHT_FINI_ARRAY"
370        );
371        assert_eq!(
372            <&str>::from(SectionHeaderType::SHT_PREINIT_ARRAY),
373            "SHT_PREINIT_ARRAY"
374        );
375        assert_eq!(<&str>::from(SectionHeaderType::SHT_GROUP), "SHT_GROUP");
376        assert_eq!(
377            <&str>::from(SectionHeaderType::SHT_SYMTAB_SHNDX),
378            "SHT_SYMTAB_SHNDX"
379        );
380        assert_eq!(<&str>::from(SectionHeaderType::SHT_NUM), "SHT_NUM");
381        assert_eq!(
382            <&str>::from(SectionHeaderType::SHT_GNU_HASH),
383            "SHT_GNU_HASH"
384        );
385    }
386
387    #[test]
388    fn test_section_header_type_display() {
389        assert_eq!(SectionHeaderType::SHT_PROGBITS.to_string(), "SHT_PROGBITS");
390        assert_eq!(SectionHeaderType::SHT_DYNAMIC.to_string(), "SHT_DYNAMIC");
391    }
392
393    #[test]
394    fn test_section_header_to_bytes() {
395        let header = SectionHeader {
396            sh_name: 1,
397            sh_type: SectionHeaderType::SHT_PROGBITS,
398            sh_flags: 6,
399            sh_addr: 0x120,
400            sh_offset: 0x120,
401            sh_size: 48,
402            sh_link: 0,
403            sh_info: 0,
404            sh_addralign: 8,
405            sh_entsize: 0,
406        };
407
408        let bytes = header.to_bytes();
409        assert_eq!(bytes.len(), 64);
410
411        // Check first few fields.
412        assert_eq!(
413            u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
414            1
415        );
416        assert_eq!(
417            u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
418            1
419        );
420    }
421}