#[cfg(feature = "std")]
pub use super::error;
#[cfg(feature = "std")]
pub mod strtab;
#[macro_use]
mod gnu_hash;
#[macro_use]
pub mod header;
#[macro_use]
pub mod program_header;
#[macro_use]
pub mod section_header;
#[macro_use]
pub mod sym;
#[macro_use]
pub mod dyn;
#[macro_use]
pub mod reloc;
#[cfg(all(feature = "std", feature = "elf32", feature = "elf64", feature = "endian_fd"))]
pub use self::impure::*;
#[cfg(all(feature = "std", feature = "elf32", feature = "elf64", feature = "endian_fd"))]
#[macro_use]
mod impure {
use scroll::{self, ctx, Endian};
use std::io::Read;
use std::ops::Deref;
use super::header;
use super::strtab::Strtab;
use super::error;
use elf32;
use elf64;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Unified<T32, T64> {
Elf32(T32),
Elf64(T64),
}
macro_rules! impl_deref {
() => {
fn deref(&self) -> &Self::Target {
match *self {
Unified::Elf32(ref thing) => {
thing
},
Unified::Elf64(ref thing) => {
thing
},
}
}
}
}
macro_rules! wrap {
(elf32, $item:ident) => {
Unified::Elf32($item)
};
(elf64, $item:ident) => {
Unified::Elf64($item)
}
}
#[derive(Debug, Clone)]
pub struct ElfVec<T32, T64> {
count: usize,
contents: Unified<Vec<T32>, Vec<T64>>
}
impl<T32, T64> ElfVec<T32, T64> where T32: Clone, T64: Clone {
pub fn new (contents: Unified<Vec<T32>, Vec<T64>>) -> ElfVec<T32, T64> {
let count = match contents {
Unified::Elf32(ref vec) => vec.len(),
Unified::Elf64(ref vec) => vec.len(),
};
ElfVec{
count: count,
contents: contents
}
}
pub fn len (&self) -> usize {
self.count
}
pub fn get (&self, _index: usize) -> Unified<T32, T64> {
match self.contents {
Unified::Elf32(ref vec) => Unified::Elf32(vec[_index].clone()),
Unified::Elf64(ref vec) => Unified::Elf64(vec[_index].clone()),
}
}
}
#[derive(Debug, Clone)]
pub struct ElfVecIter<T32, T64> {
current: usize,
contents: Unified<Vec<T32>, Vec<T64>>,
end: usize,
}
impl<T32, T64> Iterator for ElfVecIter<T32, T64> where T32: Clone, T64: Clone {
type Item = Unified<T32, T64>;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
None
} else {
let res = match self.contents {
Unified::Elf32(ref vec) => Unified::Elf32(vec[self.current].clone()),
Unified::Elf64(ref vec) => Unified::Elf64(vec[self.current].clone()),
};
self.current += 1;
Some(res)
}
}
}
#[derive(Debug, Clone)]
pub struct ElfVecIterBorrow<'a, T32:'a, T64:'a> {
current: usize,
contents: &'a Unified<Vec<T32>, Vec<T64>>,
end: usize,
}
impl<'a, T32:'a, T64:'a> Iterator for ElfVecIterBorrow<'a, T32, T64> where T32: Clone, T64: Clone {
type Item = Unified<T32, T64>;
fn next(&mut self) -> Option<Self::Item> {
if self.current == self.end {
None
} else {
let res = match self.contents {
&Unified::Elf32(ref vec) => Unified::Elf32(vec[self.current].clone()),
&Unified::Elf64(ref vec) => Unified::Elf64(vec[self.current].clone()),
};
self.current += 1;
Some(res)
}
}
}
impl<'a, T32, T64> IntoIterator for &'a ElfVec<T32, T64> where T32: Clone, T64: Clone {
type Item = Unified<T32, T64>;
type IntoIter = ElfVecIterBorrow<'a, T32, T64>;
fn into_iter(self) -> Self::IntoIter {
ElfVecIterBorrow {
current: 0,
end: self.count,
contents: &self.contents,
}
}
}
impl<T32, T64> IntoIterator for ElfVec<T32, T64> where T32: Clone, T64: Clone {
type Item = Unified<T32, T64>;
type IntoIter = ElfVecIter<T32, T64>;
fn into_iter(self) -> Self::IntoIter {
ElfVecIter {
current: 0,
end: self.count,
contents: self.contents,
}
}
}
macro_rules! elf_list {
($class:ident, $collection:ident) => {
ElfVec::new(wrap!($class, $collection))
}
}
pub type Header = Unified<elf32::header::Header, elf64::header::Header>;
pub type ProgramHeader = Unified<elf32::program_header::ProgramHeader, elf64::program_header::ProgramHeader>;
pub type SectionHeader = Unified<elf32::section_header::SectionHeader, elf64::section_header::SectionHeader>;
pub type Sym = Unified<elf32::sym::Sym, elf64::sym::Sym>;
pub type Dyn = Unified<elf32::dyn::Dyn, elf64::dyn::Dyn>;
impl Deref for Header {
type Target = super::header::ElfHeader;
impl_deref!();
}
impl Deref for ProgramHeader {
type Target = super::program_header::ElfProgramHeader;
impl_deref!();
}
impl Deref for SectionHeader {
type Target = super::section_header::ElfSectionHeader;
impl_deref!();
}
impl Deref for Sym {
type Target = super::sym::ElfSym;
impl_deref!();
}
impl Deref for Dyn {
type Target = super::dyn::ElfDyn;
impl_deref!();
}
pub type ProgramHeaders = ElfVec<elf32::program_header::ProgramHeader, elf64::program_header::ProgramHeader>;
pub type SectionHeaders = ElfVec<elf32::section_header::SectionHeader, elf64::section_header::SectionHeader>;
pub type Syms = ElfVec<elf32::sym::Sym, elf64::sym::Sym>;
pub type Dynamic = ElfVec<elf32::dyn::Dyn, elf64::dyn::Dyn>;
#[derive(Debug)]
pub struct Elf {
pub header: Header,
pub program_headers: ProgramHeaders,
pub section_headers: SectionHeaders,
pub shdr_strtab: Strtab<'static>,
pub dynstrtab: Strtab<'static>,
pub dynsyms: Syms,
pub syms: Syms,
pub strtab: Strtab<'static>,
pub dynamic: Option<Dynamic>,
pub dynrelas: Vec<super::reloc::Reloc>,
pub dynrels: Vec<super::reloc::Reloc>,
pub pltrelocs: Vec<super::reloc::Reloc>,
pub shdr_relocs: Vec<super::reloc::Reloc>,
pub soname: Option<String>,
pub interpreter: Option<String>,
pub libraries: Vec<String>,
pub is_64: bool,
pub is_lib: bool,
pub entry: u64,
pub bias: u64,
pub little_endian: bool,
}
macro_rules! wrap_dyn {
($class:ident, $dynamic:ident) => {{
if let Some(dynamic) = $dynamic {
Some (elf_list!($class, dynamic))
} else {
None
}
}}
}
macro_rules! intmax {
(elf32) => {
!0
};
(elf64) => {
::core::u64::MAX
}
}
macro_rules! parse_impl {
($class:ident, $fd:ident) => {{
let header = $fd.pread::<$class::header::Header>(0)?;
let entry = header.e_entry as usize;
let is_lib = header.e_type == $class::header::ET_DYN;
let is_lsb = header.e_ident[$class::header::EI_DATA] == $class::header::ELFDATA2LSB;
let endianness = scroll::Endian::from(is_lsb);
let is_64 = header.e_ident[$class::header::EI_CLASS] == $class::header::ELFCLASS64;
let program_headers = $class::program_header::ProgramHeader::parse($fd, header.e_phoff as usize, header.e_phnum as usize, endianness)?;
let dynamic = $class::dyn::parse($fd, &program_headers, endianness)?;
let mut bias: usize = 0;
for ph in &program_headers {
if ph.p_type == $class::program_header::PT_LOAD {
bias = ((intmax!($class) - ph.p_vaddr).wrapping_add(1)) as usize;
break;
}
}
let mut interpreter = None;
for ph in &program_headers {
if ph.p_type == $class::program_header::PT_INTERP {
let count = (ph.p_filesz - 1) as usize;
let offset = ph.p_offset as usize;
interpreter = Some($fd.pread_slice::<str>(offset, count)?.to_string());
}
}
let section_headers = $class::section_header::SectionHeader::parse($fd, header.e_shoff as usize, header.e_shnum as usize, endianness)?;
let mut syms = vec![];
let mut strtab = $class::strtab::Strtab::default();
for shdr in §ion_headers {
if shdr.sh_type as u32 == $class::section_header::SHT_SYMTAB {
let count = shdr.sh_size / shdr.sh_entsize;
syms = $class::sym::parse($fd, shdr.sh_offset as usize, count as usize, endianness)?;
}
if shdr.sh_type as u32 == $class::section_header::SHT_STRTAB {
strtab = $class::strtab::Strtab::parse($fd, shdr.sh_offset as usize, shdr.sh_size as usize, 0x0)?;
}
}
let strtab_idx = header.e_shstrndx as usize;
let shdr_strtab = if strtab_idx >= section_headers.len() {
$class::strtab::Strtab::default()
} else {
let shdr = §ion_headers[strtab_idx];
try!($class::strtab::Strtab::parse($fd, shdr.sh_offset as usize, shdr.sh_size as usize, 0x0))
};
let mut soname = None;
let mut libraries = vec![];
let mut dynsyms = vec![];
let mut dynrelas = vec![];
let mut dynrels = vec![];
let mut pltrelocs = vec![];
let mut dynstrtab = $class::strtab::Strtab::default();
if let Some(ref dynamic) = dynamic {
let dyn_info = $class::dyn::DynamicInfo::new(&*dynamic.as_slice(), bias); dynstrtab = $class::strtab::Strtab::parse($fd,
dyn_info.strtab,
dyn_info.strsz,
0x0)?;
if dyn_info.soname != 0 {
soname = Some(dynstrtab.get(dyn_info.soname).to_owned())
}
if dyn_info.needed_count > 0 {
let needed = unsafe { $class::dyn::get_needed(dynamic, &dynstrtab, dyn_info.needed_count)};
libraries = Vec::with_capacity(dyn_info.needed_count);
for lib in needed {
libraries.push(lib.to_owned());
}
}
let num_syms = (dyn_info.strtab - dyn_info.symtab) / dyn_info.syment;
dynsyms = $class::sym::parse($fd, dyn_info.symtab, num_syms, endianness)?;
dynrelas = $class::reloc::parse($fd, dyn_info.rela, dyn_info.relasz, endianness, true)?;
dynrels = $class::reloc::parse($fd, dyn_info.rel, dyn_info.relsz, endianness, false)?;
let is_rela = dyn_info.pltrel as u64 == super::dyn::DT_RELA;
pltrelocs = $class::reloc::parse($fd, dyn_info.jmprel, dyn_info.pltrelsz, endianness, is_rela)?;
}
let shdr_relocs = {
let mut relocs = vec![];
if header.e_type == super::header::ET_REL {
for section in §ion_headers {
println!("section {:?}", section);
if section.sh_type == super::section_header::SHT_REL {
let sh_relocs = $class::reloc::parse($fd, section.sh_offset as usize, section.sh_size as usize, endianness, false)?;
println!("sh_relocs {:?}", sh_relocs);
relocs.extend_from_slice(&sh_relocs);
}
if section.sh_type == super::section_header::SHT_RELA {
let sh_relocs = $class::reloc::parse($fd, section.sh_offset as usize, section.sh_size as usize, endianness, true)?;
relocs.extend_from_slice(&sh_relocs);
}
}
}
relocs
};
Ok(Elf {
header: wrap!( $class, header),
program_headers: elf_list!( $class, program_headers),
section_headers: elf_list!( $class, section_headers),
shdr_strtab: shdr_strtab,
dynamic: wrap_dyn!($class, dynamic),
dynsyms: elf_list!($class, dynsyms),
dynstrtab: dynstrtab,
syms: elf_list!($class, syms),
strtab: strtab,
dynrelas: dynrelas,
dynrels: dynrels,
pltrelocs: pltrelocs,
shdr_relocs: shdr_relocs,
soname: soname,
interpreter: interpreter,
libraries: libraries,
is_64: is_64,
is_lib: is_lib,
entry: entry as u64,
bias: bias as u64,
little_endian: is_lsb,
})
}};
}
impl Elf {
pub fn parse<S: scroll::Gread + scroll::Pread<scroll::ctx::DefaultCtx, error::Error>>(buffer: &S) -> error::Result<Self> {
match header::peek(buffer)? {
(header::ELFCLASS32, _is_lsb) => {
parse_impl!(elf32, buffer)
},
(header::ELFCLASS64, _is_lsb) => {
parse_impl!(elf64, buffer)
},
(class, endianness) => {
Err(error::Error::Malformed(format!("Unknown values in ELF ident header: class: {} endianness: {}",
class,
endianness)).into())
}
}
}
pub fn try_from<R: Read> (fd: &mut R) -> error::Result<Self> {
let buffer = scroll::Buffer::try_from(fd)?;
Elf::parse(&buffer)
}
}
impl<'a> ctx::TryFromCtx<'a> for Elf {
type Error = error::Error;
fn try_from_ctx(src: &'a [u8], (_, _): (usize, Endian)) -> Result<Self, Self::Error> {
Elf::parse(&src)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use scroll;
#[test]
fn endian_trait_parse() {
let crt1: Vec<u8> = include!("../../etc/crt1.rs");
let buffer = scroll::Buffer::new(crt1);
match Elf::parse(&buffer) {
Ok (binary) => {
assert!(binary.is_64);
assert!(!binary.is_lib);
assert_eq!(binary.entry, 0);
assert_eq!(binary.bias, 0);
let syms = binary.syms;
let mut i = 0;
assert!(binary.section_headers.len() != 0);
for sym in &syms {
if i == 11 {
let symtab = binary.strtab;
assert_eq!(&symtab[sym.st_name() as usize], "_start");
break;
}
i += 1;
}
assert!(syms.len() != 0);
},
Err (err) => {
println!("failed: {:?}", err);
assert!(false)
}
}
}
}