elf/
string_table.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Interpreting string table sections: `.strtab`, [SHT_STRTAB][crate::abi::SHT_STRTAB]
use crate::parse::ParseError;
use core::str::from_utf8;

#[derive(Debug, Default, Clone, Copy)]
pub struct StringTable<'data> {
    data: &'data [u8],
}

impl<'data> StringTable<'data> {
    pub fn new(data: &'data [u8]) -> Self {
        StringTable { data }
    }

    pub fn get_raw(&self, offset: usize) -> Result<&'data [u8], ParseError> {
        if self.data.is_empty() {
            return Err(ParseError::BadOffset(offset as u64));
        };

        let start = self
            .data
            .get(offset..)
            .ok_or(ParseError::BadOffset(offset as u64))?;
        let end = start
            .iter()
            .position(|&b| b == 0u8)
            .ok_or(ParseError::StringTableMissingNul(offset as u64))?;

        Ok(start.split_at(end).0)
    }

    pub fn get(&self, offset: usize) -> Result<&'data str, ParseError> {
        let raw_data = self.get_raw(offset)?;
        Ok(from_utf8(raw_data)?)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_empty_table_errors() {
        let st = StringTable::default();
        assert!(matches!(st.get(0), Err(ParseError::BadOffset(0))));
        assert!(matches!(st.get(1), Err(ParseError::BadOffset(1))));
    }

    /// Note: ELF string tables are defined to always start with a NUL and use
    /// index 0 to give an empty string, so getting a string starting at a NUL
    /// should properly give an empty string.
    #[test]
    fn test_get_index_0_gives_empty_string() {
        let data = [0u8, 42u8, 0u8];
        let st = StringTable::new(&data);
        assert_eq!(st.get(0).unwrap(), "");
    }

    #[test]
    fn test_get_raw_works() {
        let data = [0u8, 0x45, 0x4C, 0x46, 0u8];
        let st = StringTable::new(&data);
        assert_eq!(st.get_raw(1).unwrap(), [0x45, 0x4c, 0x46]);
    }

    #[test]
    fn test_get_string_works() {
        let data = [0u8, 0x45, 0x4C, 0x46, 0u8];
        let st = StringTable::new(&data);
        assert_eq!(st.get(1).unwrap(), "ELF");
    }

    #[test]
    fn test_get_raw_index_out_of_bounds_errors() {
        let data = [0u8, 0x45, 0x4C, 0x46, 0u8];
        let st = StringTable::new(&data);
        let result = st.get_raw(7);
        assert!(
            matches!(result, Err(ParseError::BadOffset(7))),
            "Unexpected Error type found: {result:?}"
        );
    }

    #[test]
    fn test_get_index_out_of_bounds_errors() {
        let data = [0u8, 0x45, 0x4C, 0x46, 0u8];
        let st = StringTable::new(&data);
        let result = st.get(7);
        assert!(
            matches!(result, Err(ParseError::BadOffset(7))),
            "Unexpected Error type found: {result:?}"
        );
    }

    #[test]
    fn test_get_raw_with_malformed_table_no_trailing_nul() {
        let data = [0u8, 0x45, 0x4C, 0x46];
        let st = StringTable::new(&data);
        let result = st.get_raw(1);
        assert!(
            matches!(result, Err(ParseError::StringTableMissingNul(1))),
            "Unexpected Error type found: {result:?}"
        );
    }

    #[test]
    fn test_get_with_malformed_table_no_trailing_nul() {
        let data = [0u8, 0x45, 0x4C, 0x46];
        let st = StringTable::new(&data);
        let result = st.get(1);
        assert!(
            matches!(result, Err(ParseError::StringTableMissingNul(1))),
            "Unexpected Error type found: {result:?}"
        );
    }
}