elfy/
lib.rs

1#![warn(missing_docs)]
2
3//! A rusty crate for reading data from ELF files quickly and simply
4//! 
5//! Currently Elfy is focused on reading data important to statically compiled ARM
6//! executables, in the future it will support more architectures and ELF features
7
8use std::error::Error;
9use std::fmt::{ Display, Formatter };
10use std::io::{ Read, Seek, SeekFrom };
11use std::collections::HashMap;
12
13#[macro_use]
14mod macros;
15pub mod types;
16pub mod numeric;
17
18pub mod constants;
19
20use crate::types::*;
21
22/// The result type for Elfy, wraps a `ParseElfError`
23pub type ParseElfResult<T> = std::result::Result<T, ParseElfError>;
24
25/// The Elfy error type
26/// 
27/// Various errors may occur while parsing, including IO errors. This type captures all of them
28#[derive(Debug)]
29pub enum ParseElfError {
30    /// Captures an `std::io::Error`, this error is generated by the Rust STL
31    IoError{
32        /// The captured `std::io::Error`
33        inner: std::io::Error
34    },
35
36    #[allow(missing_docs)]
37    InvalidSectionType(u32),
38
39    #[allow(missing_docs)]
40    InvalidProgramFlags(u32),
41
42    #[allow(missing_docs)]
43    InvalidProgramHeader(u32),
44    
45    #[allow(missing_docs)]
46    InvalidVersion(u32),
47    
48    #[allow(missing_docs)]
49    InvalidMachine(u16),
50    
51    #[allow(missing_docs)]
52    InvalidElfType(u16),
53    
54    #[allow(missing_docs)]
55    InvalidOsAbi(u8),
56    
57    #[allow(missing_docs)]
58    InvalidIdentVersion(u8),
59    
60    #[allow(missing_docs)]
61    InvalidDataFormat(u8),
62    
63    #[allow(missing_docs)]
64    InvalidDataClass(u8),
65
66    /// Indicates that the `Descriptor` used to parse a given item within an ELF file is invalid in the parsers current state
67    /// 
68    /// Generally, this should never be seen under normal use cases. It may signal the presence of a bug within Elfy.
69    InvalidParsingDescriptor,
70}
71
72impl Error for ParseElfError {
73    fn source(&self) -> Option<&(dyn Error + 'static)> {
74        match self {
75            ParseElfError::IoError{ inner } => Some(inner),
76            _ => None,
77        }
78    }
79}
80
81impl Display for ParseElfError {
82    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
83        write!(f, "{:?}", self)
84    }
85}
86
87impl From<std::io::Error> for ParseElfError {
88    fn from(err: std::io::Error) -> ParseElfError {
89        ParseElfError::IoError{ inner: err }
90    }
91}
92
93/// Trait used to describe how individual parts of an ELF file are parsed
94trait Parslet {
95    fn parse<R: Read + Seek>(reader: &mut R, descriptor: &mut Descriptor) -> ParseElfResult<Self> where Self: Sized;
96}
97
98/// Describes context necessary to decode a given section of an ELF file
99/// 
100/// Correctly reading and interpretting ELF files is an incremental process, meaning
101/// not all of the necessary context is available immediately and is revealed
102/// as the file is processed. This type is used to maintain that context as we gather it.
103enum Descriptor {
104    None,
105    Data{ format: DataFormat, class: DataClass },
106}
107
108impl Descriptor {
109    fn data_class(&self) -> ParseElfResult<DataClass> {
110        match self {
111            Descriptor::Data{ class, .. } => Ok(*class), 
112            Descriptor::None => Err(ParseElfError::InvalidParsingDescriptor),
113        }
114    }
115
116    fn data_format(&self) -> ParseElfResult<DataFormat> {
117        match self {
118            Descriptor::Data{ format, .. } => Ok(*format),
119            Descriptor::None => Err(ParseElfError::InvalidParsingDescriptor)
120        }
121    }
122}
123
124
125/// Represents a parsed ELF (Executable and Linkable Format) file.
126/// 
127/// The ELF format is a common standard file format for executable files, object code,
128/// shared libraries, and core dumps.
129#[derive(Debug)]
130pub struct Elf {
131    header: ElfHeader,
132    sections: Vec<Section>,
133    segments: Vec<Segment>,
134    section_map: HashMap<String, usize>,
135}
136
137impl Elf {
138    /// Loads an ELF file from disk and parses it
139    /// 
140    /// # Errors
141    /// 
142    /// Returns 'Err' if the file can not be loaded or if parsing fails, with a description of the failure
143    /// 
144    /// # Examples
145    /// ```
146    /// # use crate::elfy::*;    
147    /// let elf = Elf::load("examples/example-binary").unwrap();
148    /// ```
149    pub fn load<P: AsRef<std::path::Path>>(path: P) -> ParseElfResult<Elf> {
150        let file = std::fs::File::open(path)?;
151        let mut buf = std::io::BufReader::new(file);
152        
153        Ok(Elf::parse(&mut buf)?)
154    }
155
156    /// Parses an ELF file from a reader source
157    /// 
158    /// 'reader' can be anything that implements both 'Read' and 'Seek'
159    /// 
160    /// # Errors
161    /// 
162    /// Returns 'Err' if parsing fails, with a description of what caused the failure
163    /// 
164    /// # Examples
165    /// ```
166    /// # use crate::elfy::*;    
167    /// # let file = std::fs::File::open("examples/example-binary").unwrap();
168    /// let mut buf = std::io::BufReader::new(file);
169    /// let elf = Elf::parse(&mut buf).unwrap();
170    /// ```
171    pub fn parse<R: Read + Seek>(reader: &mut R) -> ParseElfResult<Elf> {
172        let mut descriptor = Descriptor::None;
173        
174        let header = ElfHeader::parse(reader, &mut descriptor)?;
175        let sections = parse_sections(reader, &mut descriptor, &header)?;
176        let segments = parse_segments(reader, &mut descriptor, &header)?;
177        let mut section_map = HashMap::new();
178
179        associate_string_table(&mut section_map, &sections, &header);
180
181        Ok(Elf{
182            header,
183            sections,
184            segments,
185            section_map
186        })
187    }
188
189    /// Tries to retrieve a section by name, returns 'None' if the section does not exist
190    /// 
191    /// # Examples
192    /// ```
193    /// # use crate::elfy::prelude::*;
194    /// 
195    /// let elf = Elf::load("examples/example-binary").unwrap();
196    /// let text = elf.try_get_section(".text").unwrap();
197    /// 
198    /// assert_eq!(SectionFlags::AllocExecute, text.header().flags());
199    /// ```
200    pub fn try_get_section(&self, section_name: &str) -> Option<&Section> {
201        self.sections.get(*self.section_map.get(section_name)?)
202    }
203
204    /// Returns the ELF files header
205    pub fn header(&self) -> &ElfHeader {
206        &self.header
207    }
208
209    /// Returns an `Iterator` that iterates over references of sections contained in this `Elf` file
210    pub fn sections(&self) -> SectionIter {
211        SectionIter {
212            elf: &self,
213            idx: 0
214        }
215    }
216
217    /// Returns an `Iterator` that iterates over references of program headers contained in this `Elf` file
218    pub fn segments(&self) -> SegmentIter {
219        SegmentIter {
220            elf: &self,
221            idx: 0
222        }
223    }
224}
225
226// TODO: Iterators for headers and sections
227fn parse_sections<R: Read + Seek>(reader: &mut R, descriptor: &mut Descriptor, header: &ElfHeader) -> ParseElfResult<Vec<Section>> {
228    reader.seek(SeekFrom::Start(header.section_headers_offset()))?;
229    let mut sections = Vec::new();
230    for _ in 0..header.section_header_count() {
231        sections.push(Section::parse(reader, descriptor)?)
232    }
233    Ok(sections)
234}
235
236fn parse_segments<R: Read + Seek>(reader: &mut R, descriptor: &mut Descriptor, header: &ElfHeader) -> ParseElfResult<Vec<Segment>> {
237    reader.seek(SeekFrom::Start(header.program_headers_offset()))?;
238    let mut segments = Vec::new();
239    for _ in 0..header.program_header_count() {
240        segments.push(Segment::parse(reader, descriptor)?)
241    }
242    Ok(segments)
243}
244
245fn associate_string_table(section_map: &mut HashMap<String, usize>, sections: &[Section], header: &ElfHeader) {
246    if let Some(idx) = header.section_name_table_index() {
247        if let SectionData::Strings(table) = &sections[idx].data() {
248            for (i, _section) in sections.iter().enumerate() {
249                let name = table[i].clone();
250                section_map.insert(name, i);
251            }
252        }
253    }
254}
255
256/// Iterator over the sections in an ELF file
257pub struct SectionIter<'a> {
258    elf: &'a Elf,
259    idx: usize,
260}
261
262impl<'a> Iterator for SectionIter<'a> {
263    type Item = &'a Section;
264
265    fn next(&mut self) -> Option<Self::Item> {
266        let item = self.elf.sections.get(self.idx)?;
267        self.idx += 1;
268        Some(item)
269    }
270}
271
272/// Iterator over the program headers in an ELF file
273pub struct SegmentIter<'a> {
274    elf: &'a Elf,
275    idx: usize,
276}
277
278impl<'a> Iterator for SegmentIter<'a> {
279    type Item = &'a Segment;
280
281    fn next(&mut self) -> Option<Self::Item> {
282        let item = self.elf.segments.get(self.idx)?;
283        self.idx += 1;
284        Some(item)
285    }
286}
287
288/// The Elfy prelude
289pub mod prelude {
290    pub use crate::numeric::*;
291    pub use crate::types::*;
292    pub use crate::Elf;
293}
294
295#[cfg(test)]
296mod test {
297    use super::*;
298
299    fn _load_example_binary() -> Elf {
300        let elf = Elf::load("examples/example-binary").unwrap();
301        elf
302    }
303    
304    #[test]
305    fn get_section_bytes() {
306        let elf = _load_example_binary();        
307        let text = elf.try_get_section(".text").unwrap();
308        
309        if let SectionData::Bytes(_bytes) = text.data() {
310            // do something with bytes
311        }
312    }
313
314    #[test]
315    fn section_iters() {
316        let elf = _load_example_binary();
317
318        for (i, s) in elf.sections().enumerate() {
319            match i {
320                0 => assert_eq!(s.header().section_type(), SectionType::Null),
321                2 => assert_eq!(s.header().section_type(), SectionType::ProgramData),
322                5 => assert_eq!(s.header().section_type(), SectionType::SymbolTable),
323                6 => assert_eq!(s.header().section_type(), SectionType::StringTable),
324                _ => continue
325            }
326        }
327    }
328
329    #[test]
330    fn segment_iters() {
331        let elf = _load_example_binary();
332
333        println!("{:#?}", elf);
334
335        for (i, h) in elf.segments().enumerate() {
336            match i {
337                0 => assert_eq!(h.header().program_header_type(), ProgramHeaderType::Phdr),
338                1 => assert_eq!(h.header().program_header_type(), ProgramHeaderType::Loadable),
339                2 => assert_eq!(h.header().program_header_type(), ProgramHeaderType::Loadable),
340                3 => assert_eq!(h.header().program_header_type(), ProgramHeaderType::GnuStack),
341                4 => assert_eq!(h.header().program_header_type(), ProgramHeaderType::ArmExidx),
342                _ => continue
343            }
344        }
345    }
346}