elf 0.8.0

A pure-rust library for parsing ELF files
Documentation
//! Parsing the Section Header table
use crate::endian::EndianParse;
use crate::file::Class;
use crate::parse::{ParseAt, ParseError, ParsingTable};

pub type SectionHeaderTable<'data, E> = ParsingTable<'data, E, SectionHeader>;

/// C-style 32-bit ELF Section Header definition
///
/// These C-style definitions are for users who want to implement their own ELF manipulation logic.
#[derive(Debug)]
#[repr(C)]
pub struct Elf32_Shdr {
    pub sh_name: u32,
    pub sh_type: u32,
    pub sh_flags: u32,
    pub sh_addr: u32,
    pub sh_offset: u32,
    pub sh_size: u32,
    pub sh_link: u32,
    pub sh_info: u32,
    pub sh_addralign: u32,
    pub sh_entsize: u32,
}

/// C-style 64-bit ELF Section Header definition
///
/// These C-style definitions are for users who want to implement their own ELF manipulation logic.
#[derive(Debug)]
#[repr(C)]
pub struct Elf64_Shdr {
    pub sh_name: u32,
    pub sh_type: u32,
    pub sh_flags: u64,
    pub sh_addr: u64,
    pub sh_offset: u64,
    pub sh_size: u64,
    pub sh_link: u32,
    pub sh_info: u32,
    pub sh_addralign: u64,
    pub sh_entsize: u64,
}

/// Encapsulates the contents of an ELF Section Header
///
/// This is a Rust-native type that represents a Section Header that is bit-width-agnostic.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SectionHeader {
    /// Section Name
    pub sh_name: u32,
    /// Section Type
    pub sh_type: u32,
    /// Section Flags
    pub sh_flags: u64,
    /// in-memory address where this section is loaded
    pub sh_addr: u64,
    /// Byte-offset into the file where this section starts
    pub sh_offset: u64,
    /// Section size in bytes
    pub sh_size: u64,
    /// Defined by section type
    pub sh_link: u32,
    /// Defined by section type
    pub sh_info: u32,
    /// address alignment
    pub sh_addralign: u64,
    /// size of an entry if section data is an array of entries
    pub sh_entsize: u64,
}

impl ParseAt for SectionHeader {
    fn parse_at<E: EndianParse>(
        endian: E,
        class: Class,
        offset: &mut usize,
        data: &[u8],
    ) -> Result<Self, ParseError> {
        match class {
            Class::ELF32 => Ok(SectionHeader {
                sh_name: endian.parse_u32_at(offset, data)?,
                sh_type: endian.parse_u32_at(offset, data)?,
                sh_flags: endian.parse_u32_at(offset, data)? as u64,
                sh_addr: endian.parse_u32_at(offset, data)? as u64,
                sh_offset: endian.parse_u32_at(offset, data)? as u64,
                sh_size: endian.parse_u32_at(offset, data)? as u64,
                sh_link: endian.parse_u32_at(offset, data)?,
                sh_info: endian.parse_u32_at(offset, data)?,
                sh_addralign: endian.parse_u32_at(offset, data)? as u64,
                sh_entsize: endian.parse_u32_at(offset, data)? as u64,
            }),
            Class::ELF64 => Ok(SectionHeader {
                sh_name: endian.parse_u32_at(offset, data)?,
                sh_type: endian.parse_u32_at(offset, data)?,
                sh_flags: endian.parse_u64_at(offset, data)?,
                sh_addr: endian.parse_u64_at(offset, data)?,
                sh_offset: endian.parse_u64_at(offset, data)?,
                sh_size: endian.parse_u64_at(offset, data)?,
                sh_link: endian.parse_u32_at(offset, data)?,
                sh_info: endian.parse_u32_at(offset, data)?,
                sh_addralign: endian.parse_u64_at(offset, data)?,
                sh_entsize: endian.parse_u64_at(offset, data)?,
            }),
        }
    }

    #[inline]
    fn size_for(class: Class) -> usize {
        match class {
            Class::ELF32 => 40,
            Class::ELF64 => 64,
        }
    }
}

impl SectionHeader {
    /// Helper method which uses checked integer math to get a tuple of (start,end) for
    /// this SectionHeader's (sh_offset, sh_offset + sh_size)
    pub(crate) fn get_data_range(&self) -> Result<(usize, usize), ParseError> {
        let start: usize = self.sh_offset.try_into()?;
        let size: usize = self.sh_size.try_into()?;
        let end = start.checked_add(size).ok_or(ParseError::IntegerOverflow)?;
        Ok((start, end))
    }
}

#[cfg(test)]
mod parse_tests {
    use super::*;
    use crate::endian::{BigEndian, LittleEndian};
    use crate::parse::{test_parse_for, test_parse_fuzz_too_short};

    #[test]
    fn parse_shdr32_lsb() {
        test_parse_for(
            LittleEndian,
            Class::ELF32,
            SectionHeader {
                sh_name: 0x03020100,
                sh_type: 0x07060504,
                sh_flags: 0xB0A0908,
                sh_addr: 0x0F0E0D0C,
                sh_offset: 0x13121110,
                sh_size: 0x17161514,
                sh_link: 0x1B1A1918,
                sh_info: 0x1F1E1D1C,
                sh_addralign: 0x23222120,
                sh_entsize: 0x27262524,
            },
        );
    }

    #[test]
    fn parse_shdr32_msb() {
        test_parse_for(
            BigEndian,
            Class::ELF32,
            SectionHeader {
                sh_name: 0x00010203,
                sh_type: 0x04050607,
                sh_flags: 0x08090A0B,
                sh_addr: 0x0C0D0E0F,
                sh_offset: 0x10111213,
                sh_size: 0x14151617,
                sh_link: 0x18191A1B,
                sh_info: 0x1C1D1E1F,
                sh_addralign: 0x20212223,
                sh_entsize: 0x24252627,
            },
        );
    }

    #[test]
    fn parse_shdr64_lsb() {
        test_parse_for(
            LittleEndian,
            Class::ELF64,
            SectionHeader {
                sh_name: 0x03020100,
                sh_type: 0x07060504,
                sh_flags: 0x0F0E0D0C0B0A0908,
                sh_addr: 0x1716151413121110,
                sh_offset: 0x1F1E1D1C1B1A1918,
                sh_size: 0x2726252423222120,
                sh_link: 0x2B2A2928,
                sh_info: 0x2F2E2D2C,
                sh_addralign: 0x3736353433323130,
                sh_entsize: 0x3F3E3D3C3B3A3938,
            },
        );
    }

    #[test]
    fn parse_shdr64_msb() {
        test_parse_for(
            BigEndian,
            Class::ELF64,
            SectionHeader {
                sh_name: 0x00010203,
                sh_type: 0x04050607,
                sh_flags: 0x08090A0B0C0D0E0F,
                sh_addr: 0x1011121314151617,
                sh_offset: 0x18191A1B1C1D1E1F,
                sh_size: 0x2021222324252627,
                sh_link: 0x28292A2B,
                sh_info: 0x2C2D2E2F,
                sh_addralign: 0x3031323334353637,
                sh_entsize: 0x38393A3B3C3D3E3F,
            },
        );
    }

    #[test]
    fn parse_shdr32_lsb_fuzz_too_short() {
        test_parse_fuzz_too_short::<_, SectionHeader>(LittleEndian, Class::ELF32);
    }

    #[test]
    fn parse_shdr32_msb_fuzz_too_short() {
        test_parse_fuzz_too_short::<_, SectionHeader>(BigEndian, Class::ELF32);
    }

    #[test]
    fn parse_shdr64_lsb_fuzz_too_short() {
        test_parse_fuzz_too_short::<_, SectionHeader>(LittleEndian, Class::ELF64);
    }

    #[test]
    fn parse_shdr64_msb_fuzz_too_short() {
        test_parse_fuzz_too_short::<_, SectionHeader>(BigEndian, Class::ELF64);
    }
}