1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
pub mod header;
pub mod program_header;
pub mod dyn;
pub mod rela;
pub mod sym;
pub mod strtab;
pub mod gnu_hash;

use std::path::Path;
use std::fs::File;
use std::io;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom::Start;

#[derive(Debug)]
pub struct Elf {
    pub header: header::Header,
    pub program_headers: Vec<program_header::ProgramHeader>,
    pub dynamic: Option<Vec<dyn::Dyn>>,
    pub symtab: Vec<sym::Sym>,
    pub rela: Vec<rela::Rela>,
    pub pltrela: Vec<rela::Rela>,
    pub strtab: Vec<String>,
    pub soname: Option<String>,
    pub interpreter: Option<String>,
    pub libraries: Vec<String>,
    pub is_lib: bool,
    pub size: usize,
    pub entry: usize,
}

impl Elf {
    pub fn from_path<'a>(path: &Path) -> io::Result<Elf> {
        let mut fd = try!(File::open(&path));
        let metadata = fd.metadata().unwrap();
        if metadata.len() < header::EHDR_SIZE as u64 {
            let error = io::Error::new(io::ErrorKind::Other,
                                       format!("Error: {:?} size is smaller than an ELF header",
                                               path.as_os_str()));
            Err(error)
        } else {
            let header = try!(header::Header::from_fd(&mut fd));
            let entry = header.e_entry as usize;
            let is_lib = header.e_type == header::ET_DYN;
            let is_lsb = header.e_ident[header::EI_DATA] == header::ELFDATA2LSB;

            let program_headers = try!(program_header::ProgramHeader::from_fd(&mut fd, header.e_phoff, header.e_phnum as usize, is_lsb));

            let dynamic = try!(dyn::from_fd(&mut fd, &program_headers, is_lsb));

            let mut bias: usize = 0;
            for ph in &program_headers {
                if ph.p_type == program_header::PT_LOAD {
                    bias = ((::std::u64::MAX - ph.p_vaddr).wrapping_add(1)) as usize; // my name's David
                    break;
                }
            }

            let mut interpreter = None;
            for ph in &program_headers {
                if ph.p_type == program_header::PT_INTERP {
                    let mut bytes = vec![0u8; (ph.p_filesz - 1) as usize];
                    try!(fd.seek(Start(ph.p_offset)));
                    try!(fd.read(&mut bytes));
                    interpreter = Some(String::from_utf8(bytes).unwrap())
                }
            }

            let mut soname = None;
            let mut libraries = vec![];
            let mut symtab = vec![];
            let mut rela = vec![];
            let mut pltrela = vec![];
            let mut strtabv = vec![];
            if let Some(ref dynamic) = dynamic {
                let link_info = dyn::DynamicInfo::new(&dynamic, bias); // we explicitly overflow the values here with our bias
                let strtab = try!(strtab::Strtab::from_fd(&mut fd,
                                                          link_info.strtab,
                                                          link_info.strsz));
                if link_info.soname != 0 {
                    soname = Some(strtab.get(link_info.soname).to_owned())
                }
                if link_info.needed_count > 0 {
                    let needed = dyn::get_needed(dynamic, &strtab, link_info.needed_count);
                    libraries = Vec::with_capacity(link_info.needed_count);
                    for lib in needed {
                        libraries.push(lib.to_owned());
                    }
                }

                let num_syms = (link_info.strtab - link_info.symtab) / link_info.syment; // old caveat about how this is probably not safe but rdr has been doing it with tons of binaries and never any problems
                symtab = try!(sym::from_fd(&mut fd, link_info.symtab, num_syms, is_lsb));

                rela = try!(rela::from_fd(&mut fd, link_info.rela, link_info.relasz, is_lsb));
                pltrela = try!(rela::from_fd(&mut fd, link_info.jmprel, link_info.pltrelsz, is_lsb));
                strtabv = strtab.to_vec();

            }

            let elf = Elf {
                header: header,
                program_headers: program_headers,
                dynamic: dynamic,
                symtab: symtab,
                rela: rela,
                pltrela: pltrela,
                strtab: strtabv,
                soname: soname,
                interpreter: interpreter,
                libraries: libraries,
                is_lib: is_lib,
                size: metadata.len() as usize,
                entry: entry,
            };

            Ok(elf)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use elf;
    #[test]
    fn read_ls() {
        let _ = elf::Elf::from_path(Path::new("/bin/ls"));
    }
}