charm 0.0.1

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

use super::instruction::*;
use super::operand::*;
use crate::error::Result;
use std::collections::HashMap;

pub(crate) mod encoder;

/// Main encoder object.
pub struct Encoder {
    data: Vec<u8>,
    it_block: Option<ItBlock>,
}

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

    /// Encodes one instruction.
    pub fn encode(&mut self, instr: &Instruction) -> Result<usize> {
        self.check_condition(instr)?;
        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)
    }

    /// Evaluates the condition and encodes the instruction based on the current encoder's state.
    pub fn check_condition(&mut self, instruction: &Instruction) -> Result<()> {
        match instruction.condition() {
            ConditionalInstruction::ItBlock(cond, it_cond_1, it_cond_2, it_cond_3) => {
                if self.it_block.is_some() {
                    todo!()
                }
                let states = [ItCondition::If, it_cond_1, it_cond_2, it_cond_3];
                let count = states.iter().fold(0, |acc, c| {
                    acc + if *c == ItCondition::None { 0 } else { 1 }
                });
                self.it_block = Some(ItBlock {
                    condition: cond,
                    states,
                    index: 0,
                    count,
                });
            }
            ConditionalInstruction::Condition(index, _last_only, _settable) => {
                // We know this can be a conditional instruction, so we retrieve the condition
                // attached to the mnemonic and transform it into a generic condition we can
                // compare with IT block condition (if any).
                let condition = instruction.get_op(index).as_mnemonic_condition()?;
                let condition: Condition = (*condition).into();
                if let Some(it_block) = &mut self.it_block {
                    // If the decoder currently is inside an IT block, we check the condition at
                    // the current index to check whether or not it matches the condition encoded
                    // inside the instruction.
                    match it_block.states[it_block.index] {
                        ItCondition::None => todo!(),
                        ItCondition::If => {
                            if condition != it_block.condition {
                                todo!()
                            }
                        }
                        ItCondition::Else => {
                            if condition != it_block.condition.opposite() {
                                todo!()
                            }
                        }
                    }
                    it_block.index += 1;
                }
                // else if !settable && condition != Condition::Al {
                //     // If we're not inside an IT block, but the instruction can be conditional, we
                //     // make sure that it's an `Al` condition, otherwise we return an error.
                //     todo!()
                // }
            }
            ConditionalInstruction::None => {
                // if self.it_block.is_some() {
                //     return Err(EncoderError::Unknown)?;
                // }
            }
        }
        // Once we reached the end of the IT block conditions list, we can remove it.
        let _ = self.it_block.take_if(|b| b.index == b.count);
        Ok(())
    }

    /// 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 as u32;
            for element in block.instructions.iter_mut() {
                match element {
                    InstructionBlockElement::Label(label) => {
                        if labels.insert(*label, pc as u64).is_some() {
                            todo!()
                        }
                    }
                    InstructionBlockElement::Instruction(instruction) => {
                        instruction.pc = pc;
                        pc += instruction.size as u32;
                    }
                }
            }
        }

        // 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: u32,
    pub data: Vec<u8>,
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::core::t32::config::*;
    use crate::core::t32::consts::*;
    use crate::core::t32::formatter::*;
    use rhexdump::rhexdump;

    #[test]
    pub fn blocks() {
        let instructions: Vec<InstructionBlockElement> = vec![
            1.into(),
            Instruction::with_encoding_4(
                Code::AND_r_T2,
                Encoding::Alt2,
                MnemonicCondition::Al,
                Register::R0,
                Register::R1,
                Register::R2,
            )
            .unwrap()
            .into(),
            Instruction::with_3(
                Code::ADR_T2,
                MnemonicCondition::Al,
                Register::R0,
                Label::LabelName(1),
            )
            .unwrap()
            .into(),
            Instruction::with_3(
                Code::ADR_T1,
                MnemonicCondition::Al,
                Register::R0,
                Label::LabelName(3),
            )
            .unwrap()
            .into(),
            2.into(),
        ];
        let instructions2: Vec<InstructionBlockElement> = vec![
            3.into(),
            Instruction::with_4(
                Code::IT_T1,
                ItCondition::Else,
                ItCondition::None,
                ItCondition::None,
                Condition::Eq,
            )
            .unwrap()
            .into(),
            Instruction::with_3(
                Code::ADR_T2,
                MnemonicCondition::Al,
                Register::R0,
                Label::LabelName(2),
            )
            .unwrap()
            .into(),
            Instruction::with_3(
                Code::ADR_T1,
                MnemonicCondition::Al,
                Register::R0,
                Label::LabelName(4),
            )
            .unwrap()
            .into(),
            4.into(),
            Instruction::with_3(
                Code::ADR_T2,
                MnemonicCondition::Al,
                Register::R0,
                Label::LabelName(3),
            )
            .unwrap()
            .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(0xdead0200, instructions2);
        let encoder_block = EncoderBlock::new(vec![block, block2]);
        let block = encoder_block.encode().unwrap();
        rhexdump!(&block[0].data, 0xdead0000);
        rhexdump!(&block[1].data, 0xdead0200);

        let config = ConfigLLVM::new();
        let mut insn = Instruction::with_3(
            Code::ADR_T1,
            MnemonicCondition::Al,
            Register::R0,
            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);
    }
}