#![no_std]
#![warn(box_pointers, missing_copy_implementations, missing_debug_implementations)]
#![warn(unused_extern_crates, unused_import_braces, unused_qualifications, unused_results)]
#![warn(variant_size_differences)]
macro_rules! check {
($e:expr) => {
if !$e {
return Err("");
}
};
($e:expr, $msg: expr) => {
if !$e {
return Err($msg);
}
};
}
#[cfg(feature = "compression")]
extern crate std;
#[cfg(feature = "compression")]
extern crate flate2;
extern crate zero;
pub mod header;
pub mod sections;
pub mod program;
pub mod symbol_table;
pub mod dynamic;
pub mod hash;
use header::Header;
use sections::{SectionHeader, SectionIter};
use program::{ProgramHeader, ProgramIter};
use zero::{read, read_str};
pub type P32 = u32;
pub type P64 = u64;
#[derive(Debug)]
pub struct ElfFile<'a> {
pub input: &'a [u8],
pub header: Header<'a>,
}
impl<'a> ElfFile<'a> {
pub fn new(input: &'a [u8]) -> Result<ElfFile<'a>, &'static str> {
header::parse_header(input).map(|header| ElfFile {input, header})
}
pub fn section_header(&self, index: u16) -> Result<SectionHeader<'a>, &'static str> {
sections::parse_section_header(self.input, self.header, index)
}
pub fn section_iter(&self) -> impl Iterator<Item = SectionHeader<'a>> + '_ {
SectionIter {
file: self,
next_index: 0,
}
}
pub fn program_header(&self, index: u16) -> Result<ProgramHeader<'a>, &'static str> {
program::parse_program_header(self.input, self.header, index)
}
pub fn program_iter(&self) -> impl Iterator<Item = ProgramHeader<'a>> + '_ {
ProgramIter {
file: self,
next_index: 0,
}
}
pub fn get_shstr(&self, index: u32) -> Result<&'a str, &'static str> {
self.get_shstr_table().map(|shstr_table| read_str(&shstr_table[(index as usize)..]))
}
pub fn get_string(&self, index: u32) -> Result<&'a str, &'static str> {
let header = self.find_section_by_name(".strtab").ok_or("no .strtab section")?;
if header.get_type()? != sections::ShType::StrTab {
return Err("expected .strtab to be StrTab");
}
Ok(read_str(&header.raw_data(self)[(index as usize)..]))
}
pub fn get_dyn_string(&self, index: u32) -> Result<&'a str, &'static str> {
let header = self.find_section_by_name(".dynstr").ok_or("no .dynstr section")?;
Ok(read_str(&header.raw_data(self)[(index as usize)..]))
}
pub fn find_section_by_name(&self, name: &str) -> Option<SectionHeader<'a>> {
for sect in self.section_iter() {
if let Ok(sect_name) = sect.get_name(self) {
if sect_name == name {
return Some(sect);
}
}
}
None
}
fn get_shstr_table(&self) -> Result<&'a [u8], &'static str> {
let header = self.section_header(self.header.pt2.sh_str_index());
header.map(|h| &self.input[(h.offset() as usize)..])
}
}
pub trait Extensions<'a> {
fn get_gnu_buildid(&self) -> Option<&'a [u8]>;
fn get_gnu_debuglink(&self) -> Option<(&'a str, u32)>;
fn get_gnu_debugaltlink(&self) -> Option<(&'a str, &'a [u8])>;
}
impl<'a> Extensions<'a> for ElfFile<'a> {
fn get_gnu_buildid(&self) -> Option<&'a [u8]> {
self.find_section_by_name(".note.gnu.build-id")
.and_then(|header| header.get_data(self).ok())
.and_then(|data| match data {
sections::SectionData::Note64(header, data) => Some((header, data)),
_ => None,
})
.and_then(|(header, data)| {
if header.type_() != 0x3 {
return None;
}
if header.name(data) != "GNU" {
return None;
}
Some(header.desc(data))
})
}
fn get_gnu_debuglink(&self) -> Option<(&'a str, u32)> {
self.find_section_by_name(".gnu_debuglink")
.and_then(|header| {
let data = header.raw_data(self);
let file = read_str(data);
let checksum_pos = ((file.len() + 4) / 4) * 4;
if checksum_pos + 4 <= data.len() {
let checksum: u32 = *read(&data[checksum_pos..]);
Some((file, checksum))
} else {
None
}
})
}
fn get_gnu_debugaltlink(&self) -> Option<(&'a str, &'a [u8])> {
self.find_section_by_name(".gnu_debugaltlink")
.map(|header| header.raw_data(self))
.and_then(|data| {
let file = read_str(data);
let checksum_pos = file.len() + 1;
if checksum_pos <= data.len() {
Some((file, &data[checksum_pos..]))
} else {
None
}
})
}
}
#[cfg(test)]
#[macro_use]
extern crate std;
#[cfg(test)]
mod test {
use std::prelude::v1::*;
use std::mem;
use super::*;
use header::{HeaderPt1, HeaderPt2_};
fn mk_elf_header(class: u8) -> Vec<u8> {
let header_size = mem::size_of::<HeaderPt1>() +
match class {
1 => mem::size_of::<HeaderPt2_<P32>>(),
2 => mem::size_of::<HeaderPt2_<P64>>(),
_ => 0,
};
let mut header = vec![0x7f, 'E' as u8, 'L' as u8, 'F' as u8];
let data = 1u8;
let version = 1u8;
header.extend_from_slice(&[class, data, version]);
header.resize(header_size, 0);
header
}
#[test]
fn interpret_class() {
assert!(ElfFile::new(&mk_elf_header(0)).is_err());
assert!(ElfFile::new(&mk_elf_header(1)).is_ok());
assert!(ElfFile::new(&mk_elf_header(2)).is_ok());
assert!(ElfFile::new(&mk_elf_header(42u8)).is_err());
}
}