atom-macho 0.1.0

MachO file format primitives
Documentation
pub mod build_version;
pub mod dysymtab;
pub mod segment64;
pub mod source_version;
pub mod symtab;
pub mod unix_thread;
pub mod uuid;

pub use self::{
    build_version::{BuildToolVersion, BuildVersionCommand},
    dysymtab::DysymtabCommand,
    segment64::{Section64, SegmentCommand64},
    source_version::SourceVersionCommand,
    symtab::SymtabCommand,
    unix_thread::UnixThreadCommand,
    uuid::UuidCommand,
};

use crate::io::{Endian, ReadExt as _};
use byteorder::{BigEndian, LittleEndian, WriteBytesExt as _};
use std::io::{Read, Write};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LoadCommand {
    Segment64(SegmentCommand64, Vec<Section64>),
    Symtab(SymtabCommand),
    UnixThread(UnixThreadCommand),
    Dysymtab(DysymtabCommand),
    Uuid(UuidCommand),
    BuildVersion(BuildVersionCommand, Vec<BuildToolVersion>),
    SourceVersion(SourceVersionCommand),
    Unsupported(u32, Vec<u8>),
}

impl LoadCommand {
    pub fn cmd(&self) -> u32 {
        use LoadCommand as LC;

        match self {
            LC::Segment64(cmd, _) => cmd.cmd,
            LC::Symtab(cmd) => cmd.cmd,
            LC::UnixThread(cmd) => cmd.cmd,
            LC::Dysymtab(cmd) => cmd.cmd,
            LC::Uuid(cmd) => cmd.cmd,
            LC::BuildVersion(cmd, _) => cmd.cmd,
            LC::SourceVersion(cmd) => cmd.cmd,
            LC::Unsupported(cmd, _) => *cmd,
        }
    }

    pub fn cmd_size(&self) -> u32 {
        use LoadCommand as LC;

        match self {
            LC::Segment64(cmd, _) => cmd.cmdsize,
            LC::Symtab(cmd) => cmd.cmdsize,
            LC::UnixThread(cmd) => cmd.cmdsize,
            LC::Dysymtab(cmd) => cmd.cmdsize,
            LC::Uuid(cmd) => cmd.cmdsize,
            LC::BuildVersion(cmd, _) => cmd.cmdsize,
            LC::SourceVersion(cmd) => cmd.cmdsize,
            LC::Unsupported(_, data) => data.len() as u32 - 8,
        }
    }

    pub fn read_from_in<R: Read>(read: &mut R, endian: Endian) -> Self {
        use LoadCommand as LC;

        let cmd = read.read_u32_in(endian);

        let mut cmd_bytes = [0u8; 4];
        match endian {
            Endian::Little => (&mut cmd_bytes[..]).write_u32::<LittleEndian>(cmd).unwrap(),
            Endian::Big => (&mut cmd_bytes[..]).write_u32::<BigEndian>(cmd).unwrap(),
        };

        let mut read = cmd_bytes.chain(read);

        match cmd {
            SegmentCommand64::TYPE => {
                let cmd = SegmentCommand64::read_from_in(&mut read, endian);

                let mut sections = Vec::with_capacity(cmd.nsects as usize);
                for _ in 0..cmd.nsects {
                    sections.push(Section64::read_from_in(&mut read, endian));
                }

                LC::Segment64(cmd, sections)
            }
            SymtabCommand::TYPE => {
                let cmd = SymtabCommand::read_from_in(&mut read, endian);
                LC::Symtab(cmd)
            }
            UnixThreadCommand::TYPE => {
                let cmd = UnixThreadCommand::read_from_in(&mut read, endian);
                LC::UnixThread(cmd)
            }
            DysymtabCommand::TYPE => {
                let cmd = DysymtabCommand::read_from_in(&mut read, endian);
                LC::Dysymtab(cmd)
            }
            UuidCommand::TYPE => {
                let cmd = UuidCommand::read_from_in(&mut read, endian);
                LC::Uuid(cmd)
            }
            BuildVersionCommand::TYPE => {
                let cmd = BuildVersionCommand::read_from_in(&mut read, endian);

                let mut tools = Vec::with_capacity(cmd.ntools as usize);
                for _ in 0..cmd.ntools {
                    tools.push(BuildToolVersion::read_from_in(&mut read, endian));
                }
                LC::BuildVersion(cmd, tools)
            }
            SourceVersionCommand::TYPE => {
                let cmd = SourceVersionCommand::read_from_in(&mut read, endian);
                LC::SourceVersion(cmd)
            }
            _ => {
                let _cmd = read.read_u32_in(endian);
                let cmdsize = read.read_u32_in(endian) as usize;
                let mut data = Vec::with_capacity(cmdsize - 8);
                data.resize(cmdsize - 8, 0);
                read.read_exact(&mut data).unwrap();
                LC::Unsupported(cmd, data)
            }
        }
    }

    pub fn write_into<W: Write>(&self, write: &mut W) {
        use LoadCommand as LC;

        match self {
            LC::Segment64(cmd, sections) => {
                cmd.write_into(write);
                for section in sections.iter() {
                    section.write_into(write);
                }
            }
            LC::Symtab(cmd) => {
                cmd.write_into(write);
            }
            LC::UnixThread(cmd) => {
                cmd.write_into(write);
            }
            LC::Dysymtab(cmd) => {
                cmd.write_into(write);
            }
            LC::Uuid(cmd) => {
                cmd.write_into(write);
            }
            LC::BuildVersion(cmd, tools) => {
                cmd.write_into(write);
                for tool in tools.iter() {
                    tool.write_into(write);
                }
            }
            LC::SourceVersion(cmd) => {
                cmd.write_into(write);
            }
            LC::Unsupported(_, _) => {
                panic!("Unsupported LoadCommand is unwritable");
            }
        }
    }
}