elf/
symbol.rs

1//! Parsing symbol table sections: `.symtab`, `.dynsym`
2use crate::abi;
3use crate::endian::EndianParse;
4use crate::file::Class;
5use crate::parse::{ParseAt, ParseError, ParsingTable};
6
7pub type SymbolTable<'data, E> = ParsingTable<'data, E, Symbol>;
8
9/// C-style 32-bit ELF Symbol definition
10///
11/// These C-style definitions are for users who want to implement their own ELF manipulation logic.
12#[derive(Debug)]
13#[repr(C)]
14pub struct Elf32_Sym {
15    pub st_name: u32,
16    pub st_value: u32,
17    pub st_size: u32,
18    pub st_info: u8,
19    pub st_other: u8,
20    pub st_shndx: u32,
21}
22
23/// C-style 64-bit ELF Symbol definition
24///
25/// These C-style definitions are for users who want to implement their own ELF manipulation logic.
26#[derive(Debug)]
27#[repr(C)]
28pub struct Elf64_Sym {
29    pub st_name: u32,
30    pub st_info: u8,
31    pub st_other: u8,
32    pub st_shndx: u16,
33    pub st_value: u64,
34    pub st_size: u64,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct Symbol {
39    /// This member holds an index into the symbol table's string table,
40    /// which holds the character representations of the symbol names. If the
41    /// value is non-zero, it represents a string table index that gives the
42    /// symbol name. Otherwise, the symbol table entry has no name.
43    pub st_name: u32,
44
45    /// Every symbol table entry is defined in relation to some section. This
46    /// member holds the relevant section header table index. As the sh_link and
47    /// sh_info interpretation table and the related text describe, some section
48    /// indexes indicate special meanings.
49    ///
50    /// If this member contains SHN_XINDEX, then the actual section header index
51    /// is too large to fit in this field. The actual value is contained in the
52    /// associated section of type SHT_SYMTAB_SHNDX.
53    pub st_shndx: u16,
54
55    /// This member specifies the symbol's type and binding attributes.
56    pub(super) st_info: u8,
57
58    /// This member currently specifies a symbol's visibility.
59    pub(super) st_other: u8,
60
61    /// This member gives the value of the associated symbol. Depending on the
62    /// context, this may be an absolute value, an address, and so on.
63    ///
64    /// * In relocatable files, st_value holds alignment constraints for a
65    ///   symbol whose section index is SHN_COMMON.
66    /// * In relocatable files, st_value holds a section offset for a defined
67    ///   symbol. st_value is an offset from the beginning of the section that
68    ///   st_shndx identifies.
69    /// * In executable and shared object files, st_value holds a virtual
70    ///   address. To make these files' symbols more useful for the dynamic
71    ///   linker, the section offset (file interpretation) gives way to a
72    ///   virtual address (memory interpretation) for which the section number
73    ///   is irrelevant.
74    pub st_value: u64,
75
76    /// This member gives the symbol's size.
77    /// For example, a data object's size is the number of bytes contained in
78    /// the object. This member holds 0 if the symbol has no size or an unknown
79    /// size.
80    pub st_size: u64,
81}
82
83impl Symbol {
84    /// Returns true if a symbol is undefined in this ELF object.
85    ///
86    /// When linking and loading, undefined symbols in this object get linked to
87    /// a defined symbol in another object.
88    pub fn is_undefined(&self) -> bool {
89        self.st_shndx == abi::SHN_UNDEF
90    }
91
92    pub fn st_symtype(&self) -> u8 {
93        self.st_info & 0xf
94    }
95
96    pub fn st_bind(&self) -> u8 {
97        self.st_info >> 4
98    }
99
100    pub fn st_vis(&self) -> u8 {
101        self.st_other & 0x3
102    }
103}
104
105impl ParseAt for Symbol {
106    fn parse_at<E: EndianParse>(
107        endian: E,
108        class: Class,
109        offset: &mut usize,
110        data: &[u8],
111    ) -> Result<Self, ParseError> {
112        let st_name: u32;
113        let st_value: u64;
114        let st_size: u64;
115        let st_shndx: u16;
116        let st_info: u8;
117        let st_other: u8;
118
119        if class == Class::ELF32 {
120            st_name = endian.parse_u32_at(offset, data)?;
121            st_value = endian.parse_u32_at(offset, data)? as u64;
122            st_size = endian.parse_u32_at(offset, data)? as u64;
123            st_info = endian.parse_u8_at(offset, data)?;
124            st_other = endian.parse_u8_at(offset, data)?;
125            st_shndx = endian.parse_u16_at(offset, data)?;
126        } else {
127            st_name = endian.parse_u32_at(offset, data)?;
128            st_info = endian.parse_u8_at(offset, data)?;
129            st_other = endian.parse_u8_at(offset, data)?;
130            st_shndx = endian.parse_u16_at(offset, data)?;
131            st_value = endian.parse_u64_at(offset, data)?;
132            st_size = endian.parse_u64_at(offset, data)?;
133        }
134
135        Ok(Symbol {
136            st_name,
137            st_value,
138            st_size,
139            st_shndx,
140            st_info,
141            st_other,
142        })
143    }
144
145    #[inline]
146    fn size_for(class: Class) -> usize {
147        match class {
148            Class::ELF32 => 16,
149            Class::ELF64 => 24,
150        }
151    }
152}
153
154#[cfg(test)]
155mod symbol_tests {
156    use super::*;
157
158    #[test]
159    fn symbol_undefined() {
160        let undef_sym = Symbol {
161            st_name: 0,
162            st_value: 0,
163            st_size: 0,
164            st_shndx: 0,
165            st_info: 0,
166            st_other: 0,
167        };
168        assert!(undef_sym.is_undefined());
169
170        let def_sym = Symbol {
171            st_name: 0,
172            st_value: 0,
173            st_size: 0,
174            st_shndx: 42,
175            st_info: 0,
176            st_other: 0,
177        };
178        assert!(!def_sym.is_undefined());
179    }
180}
181
182#[cfg(test)]
183mod parse_tests {
184    use super::*;
185    use crate::endian::{BigEndian, LittleEndian};
186    use crate::parse::{test_parse_for, test_parse_fuzz_too_short};
187
188    #[test]
189    fn parse_sym32_lsb() {
190        test_parse_for(
191            LittleEndian,
192            Class::ELF32,
193            Symbol {
194                st_name: 0x03020100,
195                st_value: 0x07060504,
196                st_size: 0x0B0A0908,
197                st_shndx: 0x0F0E,
198                st_info: 0x0C,
199                st_other: 0x0D,
200            },
201        );
202    }
203
204    #[test]
205    fn parse_sym32_msb() {
206        test_parse_for(
207            BigEndian,
208            Class::ELF32,
209            Symbol {
210                st_name: 0x00010203,
211                st_value: 0x04050607,
212                st_size: 0x08090A0B,
213                st_shndx: 0x0E0F,
214                st_info: 0x0C,
215                st_other: 0x0D,
216            },
217        );
218    }
219
220    #[test]
221    fn parse_sym64_lsb() {
222        test_parse_for(
223            LittleEndian,
224            Class::ELF64,
225            Symbol {
226                st_name: 0x03020100,
227                st_value: 0x0F0E0D0C0B0A0908,
228                st_size: 0x1716151413121110,
229                st_shndx: 0x0706,
230                st_info: 0x04,
231                st_other: 0x05,
232            },
233        );
234    }
235
236    #[test]
237    fn parse_sym64_msb() {
238        test_parse_for(
239            BigEndian,
240            Class::ELF64,
241            Symbol {
242                st_name: 0x00010203,
243                st_value: 0x08090A0B0C0D0E0F,
244                st_size: 0x1011121314151617,
245                st_shndx: 0x0607,
246                st_info: 0x04,
247                st_other: 0x05,
248            },
249        );
250    }
251
252    #[test]
253    fn parse_sym32_lsb_fuzz_too_short() {
254        test_parse_fuzz_too_short::<_, Symbol>(LittleEndian, Class::ELF32);
255    }
256
257    #[test]
258    fn parse_sym32_msb_fuzz_too_short() {
259        test_parse_fuzz_too_short::<_, Symbol>(BigEndian, Class::ELF32);
260    }
261
262    #[test]
263    fn parse_sym64_lsb_fuzz_too_short() {
264        test_parse_fuzz_too_short::<_, Symbol>(LittleEndian, Class::ELF64);
265    }
266
267    #[test]
268    fn parse_sym64_msb_fuzz_too_short() {
269        test_parse_fuzz_too_short::<_, Symbol>(BigEndian, Class::ELF64);
270    }
271}