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.and_then(|h| {
110 let offset = h.offset() as usize;
111 if self.input.len() < offset {
112 return Err("File is shorter than section offset");
113 }
114 Ok(&self.input[offset..])
115 })
116 }
117}
118
119pub trait Extensions<'a> {
122 fn get_gnu_buildid(&self) -> Option<&'a [u8]>;
125
126 fn get_gnu_debuglink(&self) -> Option<(&'a str, u32)>;
129
130 fn get_gnu_debugaltlink(&self) -> Option<(&'a str, &'a [u8])>;
133}
134
135impl<'a> Extensions<'a> for ElfFile<'a> {
136 fn get_gnu_buildid(&self) -> Option<&'a [u8]> {
137 self.find_section_by_name(".note.gnu.build-id")
138 .and_then(|header| header.get_data(self).ok())
139 .and_then(|data| match data {
140 sections::SectionData::Note64(header, data) => Some((header, data)),
142 _ => None,
143 })
144 .and_then(|(header, data)| {
145 if header.type_() != 0x3 {
147 return None;
148 }
149
150 if header.name(data) != "GNU" {
151 return None;
152 }
153
154 Some(header.desc(data))
155 })
156 }
157
158 fn get_gnu_debuglink(&self) -> Option<(&'a str, u32)> {
159 self.find_section_by_name(".gnu_debuglink")
160 .and_then(|header| {
161 let data = header.raw_data(self);
162 let file = read_str(data);
163 let checksum_pos = ((file.len() + 4) / 4) * 4;
165 if checksum_pos + 4 <= data.len() {
166 let checksum: u32 = *read(&data[checksum_pos..]);
167 Some((file, checksum))
168 } else {
169 None
170 }
171 })
172 }
173
174 fn get_gnu_debugaltlink(&self) -> Option<(&'a str, &'a [u8])> {
175 self.find_section_by_name(".gnu_debugaltlink")
176 .map(|header| header.raw_data(self))
177 .and_then(|data| {
178 let file = read_str(data);
179 let checksum_pos = file.len() + 1;
181 if checksum_pos <= data.len() {
182 Some((file, &data[checksum_pos..]))
183 } else {
184 None
185 }
186 })
187 }
188}
189
190#[cfg(test)]
191#[macro_use]
192extern crate std;
193
194#[cfg(test)]
195mod test {
196 use std::prelude::v1::*;
197
198 use std::mem;
199
200 use super::*;
201 use header::{HeaderPt1, HeaderPt2_};
202
203 fn mk_elf_header(class: u8) -> Vec<u8> {
204 let header_size = mem::size_of::<HeaderPt1>() +
205 match class {
206 1 => mem::size_of::<HeaderPt2_<P32>>(),
207 2 => mem::size_of::<HeaderPt2_<P64>>(),
208 _ => 0,
209 };
210 let mut header = vec![0x7f, 'E' as u8, 'L' as u8, 'F' as u8];
211 let data = 1u8;
212 let version = 1u8;
213 header.extend_from_slice(&[class, data, version]);
214 header.resize(header_size, 0);
215 header
216 }
217
218 #[test]
219 fn interpret_class() {
220 assert!(ElfFile::new(&mk_elf_header(0)).is_err());
221 assert!(ElfFile::new(&mk_elf_header(1)).is_ok());
222 assert!(ElfFile::new(&mk_elf_header(2)).is_ok());
223 assert!(ElfFile::new(&mk_elf_header(42u8)).is_err());
224 }
225}