1#![no_std]
2#![warn(box_pointers, missing_copy_implementations, missing_debug_implementations)]
3#![warn(unused_extern_crates, unused_import_braces, unused_qualifications, unused_results)]
4#![warn(variant_size_differences)]
5
6macro_rules! check {
8 ($e:expr) => {
9 if !$e {
10 return Err("");
11 }
12 };
13 ($e:expr, $msg: expr) => {
14 if !$e {
15 return Err($msg);
16 }
17 };
18}
19
20#[cfg(feature = "compression")]
21extern crate std;
22#[cfg(feature = "compression")]
23extern crate flate2;
24
25extern crate zero;
26
27pub mod header;
28pub mod sections;
29pub mod program;
30pub mod symbol_table;
31pub mod dynamic;
32pub mod hash;
33
34use header::Header;
35use sections::{SectionHeader, SectionIter};
36use program::{ProgramHeader, ProgramIter};
37use zero::{read, read_str};
38
39pub type P32 = u32;
40pub type P64 = u64;
41
42#[derive(Debug)]
43pub struct ElfFile<'a> {
44 pub input: &'a [u8],
45 pub header: Header<'a>,
46}
47
48impl<'a> ElfFile<'a> {
49 pub fn new(input: &'a [u8]) -> Result<ElfFile<'a>, &'static str> {
50 header::parse_header(input).map(|header| ElfFile {input, header})
51 }
52
53 pub fn section_header(&self, index: u16) -> Result<SectionHeader<'a>, &'static str> {
54 sections::parse_section_header(self.input, self.header, index)
55 }
56
57 pub fn section_iter(&self) -> impl Iterator<Item = SectionHeader<'a>> + '_ {
58 SectionIter {
59 file: self,
60 next_index: 0,
61 }
62 }
63
64 pub fn program_header(&self, index: u16) -> Result<ProgramHeader<'a>, &'static str> {
65 program::parse_program_header(self.input, self.header, index)
66 }
67
68 pub fn program_iter(&self) -> impl Iterator<Item = ProgramHeader<'a>> + '_ {
69 ProgramIter {
70 file: self,
71 next_index: 0,
72 }
73 }
74
75 pub fn get_shstr(&self, index: u32) -> Result<&'a str, &'static str> {
76 self.get_shstr_table().map(|shstr_table| read_str(&shstr_table[(index as usize)..]))
77 }
78
79 pub fn get_string(&self, index: u32) -> Result<&'a str, &'static str> {
80 let header = self.find_section_by_name(".strtab").ok_or("no .strtab section")?;
81 if header.get_type()? != sections::ShType::StrTab {
82 return Err("expected .strtab to be StrTab");
83 }
84 Ok(read_str(&header.raw_data(self)[(index as usize)..]))
85 }
86
87 pub fn get_dyn_string(&self, index: u32) -> Result<&'a str, &'static str> {
88 let header = self.find_section_by_name(".dynstr").ok_or("no .dynstr section")?;
89 Ok(read_str(&header.raw_data(self)[(index as usize)..]))
90 }
91
92 pub fn find_section_by_name(&self, name: &str) -> Option<SectionHeader<'a>> {
95 for sect in self.section_iter() {
96 if let Ok(sect_name) = sect.get_name(self) {
97 if sect_name == name {
98 return Some(sect);
99 }
100 }
101 }
102
103 None
104 }
105
106 fn get_shstr_table(&self) -> Result<&'a [u8], &'static str> {
107 let header = self.section_header(self.header.pt2.sh_str_index());
109 header.map(|h| &self.input[(h.offset() as usize)..])
110 }
111}
112
113pub trait Extensions<'a> {
116 fn get_gnu_buildid(&self) -> Option<&'a [u8]>;
119
120 fn get_gnu_debuglink(&self) -> Option<(&'a str, u32)>;
123
124 fn get_gnu_debugaltlink(&self) -> Option<(&'a str, &'a [u8])>;
127}
128
129impl<'a> Extensions<'a> for ElfFile<'a> {
130 fn get_gnu_buildid(&self) -> Option<&'a [u8]> {
131 self.find_section_by_name(".note.gnu.build-id")
132 .and_then(|header| header.get_data(self).ok())
133 .and_then(|data| match data {
134 sections::SectionData::Note64(header, data) => Some((header, data)),
136 _ => None,
137 })
138 .and_then(|(header, data)| {
139 if header.type_() != 0x3 {
141 return None;
142 }
143
144 if header.name(data) != "GNU" {
145 return None;
146 }
147
148 Some(header.desc(data))
149 })
150 }
151
152 fn get_gnu_debuglink(&self) -> Option<(&'a str, u32)> {
153 self.find_section_by_name(".gnu_debuglink")
154 .and_then(|header| {
155 let data = header.raw_data(self);
156 let file = read_str(data);
157 let checksum_pos = ((file.len() + 4) / 4) * 4;
159 if checksum_pos + 4 <= data.len() {
160 let checksum: u32 = *read(&data[checksum_pos..]);
161 Some((file, checksum))
162 } else {
163 None
164 }
165 })
166 }
167
168 fn get_gnu_debugaltlink(&self) -> Option<(&'a str, &'a [u8])> {
169 self.find_section_by_name(".gnu_debugaltlink")
170 .map(|header| header.raw_data(self))
171 .and_then(|data| {
172 let file = read_str(data);
173 let checksum_pos = file.len() + 1;
175 if checksum_pos <= data.len() {
176 Some((file, &data[checksum_pos..]))
177 } else {
178 None
179 }
180 })
181 }
182}
183
184#[cfg(test)]
185#[macro_use]
186extern crate std;
187
188#[cfg(test)]
189mod test {
190 use std::prelude::v1::*;
191
192 use std::mem;
193
194 use super::*;
195 use header::{HeaderPt1, HeaderPt2_};
196
197 fn mk_elf_header(class: u8) -> Vec<u8> {
198 let header_size = mem::size_of::<HeaderPt1>() +
199 match class {
200 1 => mem::size_of::<HeaderPt2_<P32>>(),
201 2 => mem::size_of::<HeaderPt2_<P64>>(),
202 _ => 0,
203 };
204 let mut header = vec![0x7f, 'E' as u8, 'L' as u8, 'F' as u8];
205 let data = 1u8;
206 let version = 1u8;
207 header.extend_from_slice(&[class, data, version]);
208 header.resize(header_size, 0);
209 header
210 }
211
212 #[test]
213 fn interpret_class() {
214 assert!(ElfFile::new(&mk_elf_header(0)).is_err());
215 assert!(ElfFile::new(&mk_elf_header(1)).is_ok());
216 assert!(ElfFile::new(&mk_elf_header(2)).is_ok());
217 assert!(ElfFile::new(&mk_elf_header(42u8)).is_err());
218 }
219}