zelf 0.1.0

A zero-allocation ELF parser.
Documentation
use flate2::read::GzDecoder;
use std::io::Read;
use std::ops::{BitAnd, Deref};
use zelf::context::Context;
use zelf::elf::Variant;
use zelf::program::{Program, Programs};
use zelf::section::{Section, SectionFlags32, Sections, Shstrtab};

pub fn decompress<'a, T: Context>(section: Section<'a, T>) -> impl Deref<Target = [u8]> + 'a
where
    <T as zelf::context::Context>::Integer: BitAnd<Output = T::Integer>,
{
    enum Return<'a> {
        Borrowed(&'a [u8]),
        Owned(Vec<u8>),
    }
    impl Deref for Return<'_> {
        type Target = [u8];

        fn deref(&self) -> &Self::Target {
            use Return::*;
            match self {
                Borrowed(r) => *r,
                Owned(v) => v,
            }
        }
    }
    use zelf::compression::Compression;
    use zelf::compression::CompressionType::*;
    let empty = Into::<T::Integer>::into(0);
    let compressed = Into::<T::Integer>::into(SectionFlags32::COMPRESSED.0);
    let flags = section.header().flags().into();
    if flags & compressed != empty {
        let compression = Compression::<T>::parse(section.content()).unwrap();
        match compression.header().typa() {
            Zlib => Return::Owned(
                GzDecoder::new(compression.content())
                    .bytes()
                    .map(Result::unwrap)
                    .collect(),
            ),
            _ => panic!("Unknown compression algorithm."),
        }
    } else {
        Return::Borrowed(section.content())
    }
}

fn format(s: &str, width: usize) -> String {
    let mut s = String::from(s);
    if s.len() <= width {
        while s.len() < width {
            s.push(' ');
        }
    } else {
        while s.len() > width - 5 {
            s.pop();
        }
        s.push_str("[...]");
    }
    s
}

#[rustfmt::skip]
pub fn show<'a, T: Context>(elf: Variant<'a, T>)
where
    <T as zelf::context::Context>::Integer: BitAnd<Output = T::Integer>,
    <T as zelf::context::Context>::Integer: std::fmt::LowerHex,
    <T as zelf::context::Context>::SectionFlags: std::fmt::LowerHex,
{
    use zelf::{Class::*, Data::*, Version::*};
    println!("ELF Header:");
    println!("  Magic:   {:?}", elf.header().ident().as_bytes());
    println!("  Class:                             {}", match T::CLASS { Class32 => "ELF32", Class64 => "ELF64" });
    println!("  Data:                              {}", match T::DATA { Little => "2's complement, little endian", Big => "2's complement, big endian" });
    println!("  Version:                           {}", match T::VERSION { One => "1 (current)" });
    println!("  OS/ABI:                            {:?}", elf.header().ident().os_abi());
    println!("  ABI Version:                       {:?}", elf.header().ident().abi_version());
    println!("  Type:                              {:?}", elf.header().typa());
    println!("  Machine:                           {:#x}", elf.header().machine());
    println!("  Verison:                           {:#x}", elf.header().version());
    println!("  Entry point address:               {:#x}", elf.header().entry());
    println!("  Start of program headers:          {:#?} (bytes into file)", elf.header().phoff());
    println!("  Start of section headers:          {:#?} (bytes into file)", elf.header().shoff());
    println!("  Flags:                             {:#x}", elf.header().flags());
    println!("  Size of this header:               {:?} (bytes)", elf.header().shentsize());
    println!("  Size of program headers:           {:?} (bytes)", elf.header().phentsize());
    println!("  Number of program headers:         {:?}", elf.header().phnum());
    println!("  Size of section headers:           {:?} (bytes)", elf.header().shentsize());
    println!("  Number of section headers:         {:?} (bytes)", elf.header().shnum());
    println!("  Section header string table index: {:?}", elf.header().shstrndx());
    println!();
    if let Some(sections) = Sections::parse(elf).unwrap() {
        let shstrtab = Shstrtab::parse(sections).unwrap().unwrap();
        println!("Section Headers:");
        println!("  [No]  Name              Type              Address           Align");
        println!("        Size              EntSize           Flags  Link  Info");
        for i in 0..sections.num() {
            use zelf::section::SectionType::*;
            let section = Section::parse(sections, i).unwrap().unwrap();
            let name = shstrtab
                .strtab()
                .find(section.header().name() as usize)
                .map(core::str::from_utf8)
                .unwrap_or(Ok("<Lost>"))
                .unwrap_or("<Invaild UTF-8 String>");
            let typa = format!("{:?}", section.header().typa());
            print!("  [{:2}]", i);
            print!("  {}", format(name, 16));
            print!("  {}", format(&typa, 16));
            print!("  {:016x}", section.header().addr());
            print!("  {:016x}", section.header().addralign());
            println!();
            print!("      ");
            print!("  {:016x}", section.header().size());
            print!("  {:016x}", section.header().entsize());
            print!("  {:4x}", section.header().flags());
            print!("  {:4x}", section.header().link());
            print!("  {:4x}", section.header().info());
            println!();
            match section.header().typa() {
                Symtab | Dynsym => {
                    zelf::symtab::Symtab::<T>::parse(&decompress(section)).unwrap();
                }
                Strtab => {
                    zelf::strtab::Strtab::parse(&decompress(section)).unwrap();
                }
                Rela => {
                    zelf::rela::Rela::<T>::parse(&decompress(section)).unwrap();
                }
                Hash => {
                    zelf::hash::Hash::<T>::parse(&decompress(section)).unwrap();
                }
                Dynamic => {
                    zelf::dynamic::Dynamic::<T>::parse(&decompress(section)).unwrap();
                }
                Note => {
                    let content = decompress(section);
                    let note = zelf::note::Note::parse::<T>(&content).unwrap();
                    let name =
                        core::str::from_utf8(note.name()).unwrap_or("<Invaild UTF-8 String>");
                    println!("    [note: {}, {:?}]", name, note.descriptor());
                }
                Rel => {
                    zelf::rel::Rel::<T>::parse(&decompress(section)).unwrap();
                }
                InitArray | FiniArray | PreinitArray => {
                    zelf::array::Array::<T>::parse(&decompress(section)).unwrap();
                }
                Group => {
                    zelf::group::Group::<T>::parse(&decompress(section)).unwrap();
                }
                SymtabShndx => {
                    zelf::shndx::Shndx::<T>::parse(&decompress(section)).unwrap();
                }
                _ => (),
            }
        }
        println!();
    }
    if let Some(programs) = Programs::parse(elf).unwrap() {
        println!("Program Headers:");
        println!("  Type        VirtAddr          PhysAddr          Align");
        println!("              FileSiz           MemSiz            Flags");
        for i in 0..programs.num() {
            use zelf::program::ProgramType::*;
            let program = Program::parse(programs, i).unwrap().unwrap();
            let typa = format!("{:?}", program.header().typa());
            print!("  {}", format(&typa, 10));
            print!("  {:016x}", program.header().vaddr());
            print!("  {:016x}", program.header().paddr());
            print!("  {:016x}", program.header().align());
            println!();
            print!("            ");
            print!("  {:016x}", program.header().filesz());
            print!("  {:016x}", program.header().memsz());
            print!("  {:016x}", program.header().flags());
            println!();
            match program.header().typa() {
                Interp => {
                    let interp = zelf::interp::Interp::parse(program.content()).unwrap();
                    let path = core::str::from_utf8(interp.path()).unwrap();
                    println!("    [requesting program interpeter: {}]", path);
                }
                Dynamic => {
                    zelf::dynamic::Dynamic::<T>::parse(program.content()).unwrap();
                }
                Note => {
                    let note = zelf::note::Note::parse::<T>(program.content()).unwrap();
                    let name =
                        core::str::from_utf8(note.name()).unwrap_or("<Invaild UTF-8 String>");
                    println!("    [note: {}, {:?}]", name, note.descriptor());
                }
                _ => (),
            }
        }
        println!();
    }
}