charm 0.0.1

ARM assembler & disassembler generated from the ARM exploration tools.
Documentation
//! Instructions encoder.

use std::collections::HashMap;

use super::instruction::*;
use crate::error::Result;

pub(crate) mod encoder;

/// Main encoder object.
#[derive(Clone, Debug)]
pub struct Encoder {
    data: Vec<u8>,
}

impl Encoder {
    /// Creates a new encoder.
    pub fn new() -> Self {
        Self { data: vec![] }
    }

    /// Encodes one instruction.
    pub fn encode(&mut self, instr: &Instruction) -> Result<usize> {
        encoder::encode(instr, &mut self.data)
    }

    /// Encodes an instruction block.
    pub(crate) fn encode_block(
        &mut self,
        instr: &mut Instruction,
        labels: &HashMap<u64, u64>,
    ) -> Result<usize> {
        encoder::encode_block(instr, &mut self.data, labels)
    }

    /// Consumes the encoder and returns the encoded bytes.
    pub fn take(self) -> Vec<u8> {
        self.data
    }
}

/// Type that represents an encoder block, which is a collection of
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub struct EncoderBlock {
    blocks: Vec<InstructionBlock>,
}

impl EncoderBlock {
    /// Creates a new encoder block from a vector of instruction blocks.
    pub fn new(instr_blocks: Vec<InstructionBlock>) -> Self {
        Self {
            blocks: instr_blocks,
        }
    }

    /// Encodes all instructions in the encoder block.
    pub fn encode(mut self) -> Result<Vec<EncoderBlockResult>> {
        // Find labels
        let mut labels = HashMap::new();
        for block in self.blocks.iter_mut() {
            let mut pc = block.pc;
            for element in block.instructions.iter_mut() {
                match element {
                    InstructionBlockElement::Label(label) => {
                        if labels.insert(*label, pc).is_some() {
                            todo!()
                        }
                    }
                    InstructionBlockElement::Instruction(instruction) => {
                        instruction.pc = pc;
                        pc += instruction.size as u64;
                    }
                }
            }
        }

        // Encode instructions
        self.blocks
            .into_iter()
            .map(|block| -> Result<EncoderBlockResult> {
                let mut encoder = Encoder::new();
                for element in block.instructions.into_iter() {
                    if let InstructionBlockElement::Instruction(mut instruction) = element {
                        let _ = encoder.encode_block(&mut instruction, &labels)?;
                    }
                }
                Ok(EncoderBlockResult {
                    pc: block.pc,
                    data: encoder.take(),
                })
            })
            .collect()
    }
}

/// Type that represents the result of an encoder block after being assembled.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Default)]
pub struct EncoderBlockResult {
    pub pc: u64,
    pub data: Vec<u8>,
}

#[cfg(test)]
mod test {
    use crate::core::a64::config::*;
    use crate::core::a64::consts::*;
    use crate::core::a64::formatter::*;
    use crate::core::a64::operand::*;

    use super::*;
    use rhexdump::rhexdump;

    #[test]
    pub fn blocks() {
        let instructions: Vec<InstructionBlockElement> = vec![
            Instruction::with_4(
                Code::ADD_64_addsub_imm,
                Register::X0,
                Register::X1,
                16u32,
                Extension::Lsl(12),
            )
            .unwrap()
            .into(),
            // Instruction::label(1),
            1.into(),
            Instruction::with_4(
                Code::ADD_64_addsub_imm,
                Register::X0,
                Register::X1,
                16u32,
                Extension::Lsl(12),
            )
            .unwrap()
            .into(),
            Instruction::with_4(
                Code::ADD_64_addsub_imm,
                Register::X0,
                Register::X1,
                16u32,
                Extension::Lsl(12),
            )
            .unwrap()
            .into(),
            Instruction::with_4(
                Code::ADD_64_addsub_imm,
                Register::X0,
                Register::X1,
                16u32,
                Extension::Lsl(12),
            )
            .unwrap()
            .into(),
            Instruction::with_2(Code::ADR_only_pcreladdr, Register::X0, Label::LabelName(3))
                .unwrap()
                .into(),
            Instruction::with_2(Code::ADR_only_pcreladdr, Register::X0, Label::LabelName(4))
                .unwrap()
                .into(),
            2.into(),
        ];
        let instructions2: Vec<InstructionBlockElement> = vec![
            Instruction::with_4(
                Code::ADD_64_addsub_imm,
                Register::X0,
                Register::X1,
                16u32,
                Extension::Lsl(12),
            )
            .unwrap()
            .into(),
            // Instruction::label(1),
            3.into(),
            Instruction::with_4(
                Code::ADD_64_addsub_imm,
                Register::X0,
                Register::X1,
                16u32,
                Extension::Lsl(12),
            )
            .unwrap()
            .into(),
            Instruction::with_4(
                Code::ADD_64_addsub_imm,
                Register::X0,
                Register::X1,
                16u32,
                Extension::Lsl(12),
            )
            .unwrap()
            .into(),
            Instruction::with_4(
                Code::ADD_64_addsub_imm,
                Register::X0,
                Register::X1,
                16u32,
                Extension::Lsl(12),
            )
            .unwrap()
            .into(),
            Instruction::with_2(Code::ADR_only_pcreladdr, Register::X0, Label::LabelName(1))
                .unwrap()
                .into(),
            Instruction::with_2(Code::ADR_only_pcreladdr, Register::X0, Label::LabelName(2))
                .unwrap()
                .into(),
            4.into(),
        ];
        // instructions[2].set_op(1, Register::X3).unwrap();
        // let mut block = InstructionBlock::new(0xdead0000);
        let block = InstructionBlock::with_instructions(0xdead0000, instructions);
        let block2 = InstructionBlock::with_instructions(0xdeaf0000, instructions2);
        let encoder_block = EncoderBlock::new(vec![block, block2]);
        let block = encoder_block.encode().unwrap();
        rhexdump!(&block[0].data, 0xdead0001);
        rhexdump!(&block[1].data, 0xdeaf0002);

        let config = ConfigLLVM::new();
        let mut insn =
            Instruction::with_2(Code::ADR_only_pcreladdr, Register::X0, Label::Label(24)).unwrap();
        let mut formatter = Fmt {};
        let mut output = String::new();
        insn.pc = 0xdead0000;
        formatter.format(&mut output, &config, &insn).unwrap();
        println!("{}", output);
    }
}