elfkit 0.0.7

an elf parser and manipulation library in pure rust
Documentation
use error::Error;
use header::Header;
use relocation::Relocation;
use dynamic::Dynamic;
use symbol::Symbol;
use strtab::Strtab;
use types;

use std::io::{Read, Seek, SeekFrom, Write};
use std::io::BufWriter;

#[derive(Default, Debug, Clone)]
pub struct SectionHeader {
    pub name:       u32,
    pub shtype:     types::SectionType,
    pub flags:      types::SectionFlags,
    pub addr:       u64,
    pub offset:     u64,
    pub size:       u64,
    pub link:       u32,
    pub info:       u32,
    pub addralign:  u64,
    pub entsize:    u64,
}

impl SectionHeader {
    pub fn entsize(eh: &Header) -> usize {
        4 + 4 + match eh.ident_class {
            types::Class::Class64 => 6 * 8,
            types::Class::Class32 => 6 * 4,
        } + 4 + 4
    }

    pub fn from_reader<R>(io: &mut R, eh: &Header) -> Result<SectionHeader, Error>
    where
        R: Read,
    {
        elf_dispatch_endianness!(eh => {
            let mut r = SectionHeader::default();
            r.name   = read_u32(io)?;
            let reb  = read_u32(io)?;
            r.shtype = types::SectionType(reb);

            elf_dispatch_uclass!(eh => {
                let reb = read_uclass(io)?;
                r.flags = match types::SectionFlags::from_bits(reb) {
                    Some(v) => v,
                    None => return Err(Error::InvalidSectionFlags(reb)),
                };
                r.addr   = read_uclass(io)?;
                r.offset = read_uclass(io)?;
                r.size   = read_uclass(io)?;
                r.link   = read_u32(io)?;
                r.info   = read_u32(io)?;
                r.addralign = read_uclass(io)?;
                r.entsize = read_uclass(io)?;
                Ok(r)
            })
        })
    }

    pub fn to_writer<R>(&self, eh: &Header, io: &mut R) -> Result<(), Error>
    where
        R: Write,
    {
        let mut w = BufWriter::new(io);
        elf_write_u32!(eh, w, self.name)?;
        elf_write_u32!(eh, w, self.shtype.to_u32())?;
        elf_write_uclass!(eh, w, self.flags.bits())?;

        elf_write_uclass!(eh, w, self.addr)?;
        elf_write_uclass!(eh, w, self.offset)?;
        elf_write_uclass!(eh, w, self.size)?;
        elf_write_u32!(eh, w, self.link)?;
        elf_write_u32!(eh, w, self.info)?;
        elf_write_uclass!(eh, w, self.addralign)?;
        elf_write_uclass!(eh, w, self.entsize)?;
        Ok(())
    }
}

#[derive(Debug, Clone)]
pub enum SectionContent {
    None,
    Unloaded,
    Raw(Vec<u8>),
    Relocations(Vec<Relocation>),
    Symbols(Vec<Symbol>),
    Dynamic(Vec<Dynamic>),
    Strtab(Strtab),
}

impl Default for SectionContent {
    fn default() -> Self {
        SectionContent::None
    }
}
impl SectionContent {
    pub fn as_dynamic_mut(&mut self) -> Option<&mut Vec<Dynamic>> {
        match self {
            &mut SectionContent::Dynamic(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn as_dynamic(&self) -> Option<&Vec<Dynamic>> {
        match self {
            &SectionContent::Dynamic(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn into_dynamic(self) -> Option<Vec<Dynamic>> {
        match self {
            SectionContent::Dynamic(v) => Some(v),
            _ => None,
        }
    }
    pub fn as_strtab_mut(&mut self) -> Option<&mut Strtab> {
        match self {
            &mut SectionContent::Strtab(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn as_symbols(&self) -> Option<&Vec<Symbol>> {
        match self {
            &SectionContent::Symbols(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn as_symbols_mut(&mut self) -> Option<&mut Vec<Symbol>> {
        match self {
            &mut SectionContent::Symbols(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn into_symbols(self) -> Option<Vec<Symbol>> {
        match self {
            SectionContent::Symbols(v) => Some(v),
            _ => None,
        }
    }
    pub fn as_relocations(&self) -> Option<&Vec<Relocation>> {
        match self {
            &SectionContent::Relocations(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn as_relocations_mut(&mut self) -> Option<&mut Vec<Relocation>> {
        match self {
            &mut SectionContent::Relocations(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn into_relocations(self) -> Option<Vec<Relocation>> {
        match self {
            SectionContent::Relocations(v) => Some(v),
            _ => None,
        }
    }
    pub fn as_raw(&self) -> Option<&Vec<u8>> {
        match self {
            &SectionContent::Raw(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn as_raw_mut(&mut self) -> Option<&mut Vec<u8>> {
        match self {
            &mut SectionContent::Raw(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn into_raw(self) -> Option<Vec<u8>> {
        match self {
            SectionContent::Raw(v) => Some(v),
            _ => None,
        }
    }
    pub fn size(&self, eh: &Header) -> usize {
        match self {
            &SectionContent::Unloaded => panic!("cannot size unloaded section"),
            &SectionContent::None => 0,
            &SectionContent::Raw(ref v) => v.len(),
            &SectionContent::Dynamic(ref v) => v.len() * Dynamic::entsize(eh),
            &SectionContent::Strtab(ref v) => v.len(eh),
            &SectionContent::Symbols(ref v) => v.len() * Symbol::entsize(eh),
            &SectionContent::Relocations(ref v) => v.len() * Relocation::entsize(eh),
        }
    }
}

#[derive(Debug, Default, Clone)]
pub struct Section {
    pub header:     SectionHeader,
    pub name:       Vec<u8>,
    pub content:    SectionContent,
    pub addrlock:   bool,
}


impl Section {
    pub fn size(&self, eh: &Header) -> usize {
        self.content.size(eh)
    }
    pub fn new(
        name:       Vec<u8>,
        shtype:     types::SectionType,
        flags:      types::SectionFlags,
        content:    SectionContent,
        link:       u32,
        info:       u32,
    ) -> Section {
        Section {
            name: name,
            header: SectionHeader {
                name: 0,
                shtype: shtype,
                flags: flags,
                addr: 0,
                offset: 0,
                size: 0,
                link: link,
                info: info,
                addralign: 0,
                entsize: 0,
            },
            content: content,
            addrlock: false,
        }
    }

    pub fn sync(
        &mut self,
        eh: &Header,
        mut linked: Option<&mut SectionContent>,
    ) -> Result<(), Error> {
        match self.content {
            SectionContent::Unloaded => {
                return Err(Error::SyncingUnloadedSection);
            },
            SectionContent::Relocations(_) => {
                self.header.entsize = Relocation::entsize(eh) as u64;
            }
            SectionContent::Symbols(ref mut vv) => {
                for (i, sym) in vv.iter().enumerate() {
                    if sym.bind == types::SymbolBind::GLOBAL {
                        self.header.info = i as u32;
                        break;
                    }
                }
                for v in vv {
                    v.sync(linked.as_mut().map(|r| &mut **r), eh)?;
                }
                self.header.entsize = Symbol::entsize(eh) as u64;
            }
            SectionContent::Dynamic(ref mut vv) => {
                for v in vv {
                    v.sync(linked.as_mut().map(|r| &mut **r), eh)?;
                }
                self.header.entsize = Dynamic::entsize(eh) as u64;
            }
            SectionContent::Strtab(_) => {
                self.header.entsize = Strtab::entsize(eh) as u64;
            }
            SectionContent::None | SectionContent::Raw(_) => {}
        }
        if self.header.shtype != types::SectionType::NOBITS {
            self.header.size = self.size(eh) as u64;
        }
        Ok(())
    }

    pub fn from_reader<T>(
        &mut self,
        mut io: T,
        linked: Option<&Section>,
        eh: &Header,
    ) -> Result<(), Error> where T: Read + Seek {
        match self.content {
            SectionContent::Unloaded => {},
            _ => return Ok(()),
        };
        if self.header.shtype == types::SectionType::NOBITS {
            self.content = SectionContent::None;
            return Ok(());
        };
        io.seek(SeekFrom::Start(self.header.offset))?;
        let mut bb = vec![0; self.header.size as usize];
        io.read_exact(&mut bb)?;
        let linked = linked.map(|s|&s.content);
        self.content = match self.header.shtype {
            types::SectionType::NOBITS => {
                unreachable!();
            },
            types::SectionType::STRTAB => {
                let io = bb.as_slice();
                Strtab::from_reader(io, linked, eh)?
            }
            types::SectionType::RELA => {
                let io = bb.as_slice();
                Relocation::from_reader(io, linked, eh)?
            }
            types::SectionType::SYMTAB | types::SectionType::DYNSYM => {
                let io = bb.as_slice();
                Symbol::from_reader(io, linked, eh)?
            }
            types::SectionType::DYNAMIC => {
                let io = bb.as_slice();
                Dynamic::from_reader(io, linked, eh)?
            }
            _ => {
                SectionContent::Raw(bb)
            }
        };
        Ok(())
    }


    pub fn to_writer<R>(
        &self,
        mut io: R,
        eh: &Header,
    ) -> Result<(), Error> where R: Write + Seek {
        match self.content {
            SectionContent::Unloaded => return Ok(()),
            _ => {},
        };
        io.seek(SeekFrom::Start(self.header.offset))?;

        let rs = match &self.content {
            &SectionContent::Unloaded => {
                return Err(Error::WritingUnloadedSection);
            },
            &SectionContent::Relocations(ref vv) => {
                let mut rs = 0;
                for v in vv {
                    rs += v.to_writer(&mut io, eh)?;
                }
                rs
            }
            &SectionContent::Symbols(ref vv) => {
                let mut rs = 0;
                for v in vv {
                    rs += v.to_writer(&mut io, eh)?;
                }
                rs
            }
            &SectionContent::Dynamic(ref vv) => {
                let mut rs = 0;
                for v in vv {
                    rs += v.to_writer(&mut io, eh)?;
                }
                rs
            }
            &SectionContent::Strtab(ref v) => {
                v.to_writer(&mut io, eh)?
            }
            &SectionContent::None => {
                0
            },
            &SectionContent::Raw(ref raw) => {
                io.write(&raw)?
            }
        };

        assert_eq!(
            io.seek(SeekFrom::Current(0))?,
            self.header.offset + self.content.size(eh) as u64,
            "writing {} with header.size {} and content.size {} returned a written size {}",
            String::from_utf8_lossy(&self.name),
            self.content.size(eh),
            self.header.size,
            rs
            );

        Ok(())
    }

}