diff options
| author | Joe Rozner <joe@deadbytes.net> | 2024-08-20 21:55:05 -0700 |
|---|---|---|
| committer | Alexander Taylor <alex@vector35.com> | 2024-08-22 19:35:30 -0400 |
| commit | f038c7c9a9646c2a126d693eab7d297cd25565e3 (patch) | |
| tree | 51b1366dabb78fad2e2a7cc0518b3604620f66d8 /arch/msp430/src | |
| parent | 2a9d07d7fefe5a6d7f7a482397060a96782142ac (diff) | |
Add msp430 architecture
Diffstat (limited to 'arch/msp430/src')
| -rw-r--r-- | arch/msp430/src/architecture.rs | 763 | ||||
| -rw-r--r-- | arch/msp430/src/flag.rs | 153 | ||||
| -rw-r--r-- | arch/msp430/src/lib.rs | 55 | ||||
| -rw-r--r-- | arch/msp430/src/lift.rs | 670 | ||||
| -rw-r--r-- | arch/msp430/src/register.rs | 130 |
5 files changed, 1771 insertions, 0 deletions
diff --git a/arch/msp430/src/architecture.rs b/arch/msp430/src/architecture.rs new file mode 100644 index 00000000..ab392a38 --- /dev/null +++ b/arch/msp430/src/architecture.rs @@ -0,0 +1,763 @@ +use crate::flag::{Flag, FlagClass, FlagGroup, FlagWrite}; +use crate::lift::lift_instruction; +use crate::register::Register; + +use binaryninja::{ + architecture::{ + Architecture, BranchInfo, CoreArchitecture, CustomArchitectureHandle, FlagCondition, + InstructionInfo, UnusedIntrinsic, UnusedRegisterStack, UnusedRegisterStackInfo, + }, + disassembly::{InstructionTextToken, InstructionTextTokenContents}, + llil::{LiftedExpr, Lifter}, + Endianness, +}; + +use msp430_asm::{ + emulate::Emulated, instruction::Instruction, jxx::Jxx, operand::Operand, + single_operand::SingleOperand, two_operand::TwoOperand, +}; + +use log::error; + +const MIN_MNEMONIC: usize = 9; + +pub struct Msp430 { + handle: CoreArchitecture, + custom_handle: CustomArchitectureHandle<Msp430>, +} + +impl Msp430 { + pub fn new(handle: CoreArchitecture, custom_handle: CustomArchitectureHandle<Msp430>) -> Self { + Msp430 { + handle, + custom_handle, + } + } +} + +impl Architecture for Msp430 { + type Handle = CustomArchitectureHandle<Self>; + type RegisterStackInfo = UnusedRegisterStackInfo<Self::Register>; + type RegisterStack = UnusedRegisterStack<Self::Register>; + type Register = Register; + type RegisterInfo = Register; + type Flag = Flag; + type FlagWrite = FlagWrite; + type FlagClass = FlagClass; + type FlagGroup = FlagGroup; + type Intrinsic = UnusedIntrinsic; + + fn endianness(&self) -> Endianness { + Endianness::LittleEndian + } + + fn address_size(&self) -> usize { + 2 // 16 bit + } + + fn default_integer_size(&self) -> usize { + 2 // 16 bit integers + } + + fn instruction_alignment(&self) -> usize { + 2 + } + + fn max_instr_len(&self) -> usize { + 6 + } + + fn opcode_display_len(&self) -> usize { + self.max_instr_len() + } + + fn associated_arch_by_addr(&self, _addr: &mut u64) -> CoreArchitecture { + self.handle + } + + fn instruction_info(&self, data: &[u8], addr: u64) -> Option<InstructionInfo> { + match msp430_asm::decode(data) { + Ok(inst) => { + let mut info = InstructionInfo::new(inst.size(), 0); + + match inst { + Instruction::Jnz(inst) => { + info.add_branch( + BranchInfo::True(offset_to_absolute(addr, inst.offset())), + Some(self.handle), + ); + info.add_branch( + BranchInfo::False(addr + inst.size() as u64), + Some(self.handle), + ); + } + Instruction::Jz(inst) => { + info.add_branch( + BranchInfo::True(offset_to_absolute(addr, inst.offset())), + Some(self.handle), + ); + info.add_branch( + BranchInfo::False(addr + inst.size() as u64), + Some(self.handle), + ); + } + Instruction::Jlo(inst) => { + info.add_branch( + BranchInfo::True(offset_to_absolute(addr, inst.offset())), + Some(self.handle), + ); + info.add_branch( + BranchInfo::False(addr + inst.size() as u64), + Some(self.handle), + ); + } + Instruction::Jc(inst) => { + info.add_branch( + BranchInfo::True(offset_to_absolute(addr, inst.offset())), + Some(self.handle), + ); + info.add_branch( + BranchInfo::False(addr + inst.size() as u64), + Some(self.handle), + ); + } + Instruction::Jn(inst) => { + info.add_branch( + BranchInfo::True(offset_to_absolute(addr, inst.offset())), + Some(self.handle), + ); + info.add_branch( + BranchInfo::False(addr + inst.size() as u64), + Some(self.handle), + ); + } + Instruction::Jge(inst) => { + info.add_branch( + BranchInfo::True(offset_to_absolute(addr, inst.offset())), + Some(self.handle), + ); + info.add_branch( + BranchInfo::False(addr + inst.size() as u64), + Some(self.handle), + ); + } + Instruction::Jl(inst) => { + info.add_branch( + BranchInfo::True(offset_to_absolute(addr, inst.offset())), + Some(self.handle), + ); + info.add_branch( + BranchInfo::False(addr + inst.size() as u64), + Some(self.handle), + ); + } + Instruction::Jmp(inst) => { + info.add_branch( + BranchInfo::Unconditional(offset_to_absolute(addr, inst.offset())), + Some(self.handle), + ); + } + Instruction::Br(inst) => match inst.destination() { + Some(Operand::RegisterDirect(_)) => { + info.add_branch(BranchInfo::Indirect, Some(self.handle)) + } + Some(Operand::Indexed(_)) => { + info.add_branch(BranchInfo::Indirect, Some(self.handle)) + } + Some(Operand::Absolute(value)) => info.add_branch( + BranchInfo::Unconditional(*value as u64), + Some(self.handle), + ), + Some(Operand::Symbolic(offset)) => info.add_branch( + BranchInfo::Unconditional((addr as i64 + *offset as i64) as u64), + Some(self.handle), + ), + Some(Operand::Immediate(addr)) => info + .add_branch(BranchInfo::Unconditional(*addr as u64), Some(self.handle)), + Some(Operand::Constant(_)) => { + info.add_branch(BranchInfo::Unconditional(addr), Some(self.handle)) + } + Some(Operand::RegisterIndirect(_)) + | Some(Operand::RegisterIndirectAutoIncrement(_)) => { + info.add_branch(BranchInfo::Indirect, Some(self.handle)) + } + None => {} + }, + Instruction::Call(inst) => match inst.source() { + Operand::RegisterDirect(_) => { + info.add_branch(BranchInfo::Indirect, Some(self.handle)) + } + Operand::Indexed(_) => { + info.add_branch(BranchInfo::Indirect, Some(self.handle)) + } + Operand::Absolute(value) => { + info.add_branch(BranchInfo::Call(*value as u64), Some(self.handle)) + } + Operand::Symbolic(offset) => info.add_branch( + BranchInfo::Call((addr as i64 + *offset as i64) as u64), + Some(self.handle), + ), + Operand::Immediate(addr) => { + info.add_branch(BranchInfo::Call(*addr as u64), Some(self.handle)) + } + Operand::Constant(_) => { + info.add_branch(BranchInfo::Call(addr), Some(self.handle)) + } + Operand::RegisterIndirect(_) + | Operand::RegisterIndirectAutoIncrement(_) => { + info.add_branch(BranchInfo::Indirect, Some(self.handle)) + } + }, + Instruction::Reti(_) => { + info.add_branch(BranchInfo::FunctionReturn, Some(self.handle)); + } + Instruction::Ret(_) => { + info.add_branch(BranchInfo::FunctionReturn, Some(self.handle)); + } + _ => {} + } + + Some(info) + } + Err(_) => None, + } + } + + fn instruction_text( + &self, + data: &[u8], + addr: u64, + ) -> Option<(usize, Vec<InstructionTextToken>)> { + match msp430_asm::decode(data) { + Ok(inst) => { + let tokens = generate_tokens(&inst, addr); + if tokens.is_empty() { + None + } else { + Some((inst.size(), tokens)) + } + } + Err(_) => None, + } + } + + fn instruction_llil( + &self, + data: &[u8], + addr: u64, + il: &mut Lifter<Self>, + ) -> Option<(usize, bool)> { + match msp430_asm::decode(data) { + Ok(inst) => { + lift_instruction(&inst, addr, il); + Some((inst.size(), true)) + } + Err(_) => None, + } + } + + fn flags_required_for_flag_condition( + &self, + condition: FlagCondition, + _class: Option<Self::FlagClass>, + ) -> Vec<Self::Flag> { + match condition { + FlagCondition::LLFC_UGE => vec![Flag::C], + FlagCondition::LLFC_ULT => vec![Flag::C], + FlagCondition::LLFC_SGE => vec![Flag::N, Flag::V], + FlagCondition::LLFC_SLT => vec![Flag::N, Flag::V], + FlagCondition::LLFC_E => vec![Flag::Z], + FlagCondition::LLFC_NE => vec![Flag::Z], + FlagCondition::LLFC_NEG => vec![Flag::N], + FlagCondition::LLFC_POS => vec![Flag::N], + _ => vec![], + } + } + + fn flag_group_llil<'a>( + &self, + _group: Self::FlagGroup, + _il: &'a mut Lifter<Self>, + ) -> Option<LiftedExpr<'a, Self>> { + None + } + + fn registers_all(&self) -> Vec<Self::Register> { + vec![ + Register::Pc, + Register::Sp, + Register::Sr, + Register::Cg, + Register::R4, + Register::R5, + Register::R6, + Register::R7, + Register::R8, + Register::R9, + Register::R10, + Register::R11, + Register::R12, + Register::R13, + Register::R14, + Register::R15, + ] + } + + fn registers_full_width(&self) -> Vec<Self::Register> { + vec![ + Register::Pc, + Register::Sp, + Register::Sr, + Register::Cg, + Register::R4, + Register::R5, + Register::R6, + Register::R7, + Register::R8, + Register::R9, + Register::R10, + Register::R11, + Register::R12, + Register::R13, + Register::R14, + Register::R15, + ] + } + + fn registers_global(&self) -> Vec<Self::Register> { + Vec::new() + } + + fn registers_system(&self) -> Vec<Self::Register> { + Vec::new() + } + + fn flags(&self) -> Vec<Self::Flag> { + vec![Flag::C, Flag::Z, Flag::N, Flag::V] + } + + fn flag_write_types(&self) -> Vec<Self::FlagWrite> { + vec![ + FlagWrite::All, + FlagWrite::Nz, + FlagWrite::Nvz, + FlagWrite::Cnz, + ] + } + + fn flag_classes(&self) -> Vec<Self::FlagClass> { + Vec::new() + } + + fn flag_groups(&self) -> Vec<Self::FlagGroup> { + Vec::new() + } + + fn stack_pointer_reg(&self) -> Option<Self::Register> { + Some(Register::Sp) + } + + fn link_reg(&self) -> Option<Self::Register> { + None + } + + fn register_from_id(&self, id: u32) -> Option<Self::Register> { + match id.try_into() { + Ok(register) => Some(register), + Err(_) => None, + } + } + + fn flag_from_id(&self, id: u32) -> Option<Self::Flag> { + match id.try_into() { + Ok(flag) => Some(flag), + Err(_) => { + error!("invalid flag id {}", id); + None + } + } + } + + fn flag_write_from_id(&self, id: u32) -> Option<Self::FlagWrite> { + match id.try_into() { + Ok(flag_write) => Some(flag_write), + Err(_) => { + error!("invalid flag write id {}", id); + None + } + } + } + + fn flag_class_from_id(&self, _: u32) -> Option<Self::FlagClass> { + None + } + + fn flag_group_from_id(&self, _: u32) -> Option<Self::FlagGroup> { + None + } + + fn handle(&self) -> Self::Handle { + self.custom_handle + } +} + +impl AsRef<CoreArchitecture> for Msp430 { + fn as_ref(&self) -> &CoreArchitecture { + &self.handle + } +} + +fn generate_tokens(inst: &Instruction, addr: u64) -> Vec<InstructionTextToken> { + match inst { + Instruction::Rrc(inst) => generate_single_operand_tokens(inst, addr, false), + Instruction::Swpb(inst) => generate_single_operand_tokens(inst, addr, false), + Instruction::Rra(inst) => generate_single_operand_tokens(inst, addr, false), + Instruction::Sxt(inst) => generate_single_operand_tokens(inst, addr, false), + Instruction::Push(inst) => generate_single_operand_tokens(inst, addr, false), + Instruction::Call(inst) => generate_single_operand_tokens(inst, addr, true), + Instruction::Reti(_) => vec![InstructionTextToken::new( + "reti", + InstructionTextTokenContents::Instruction, + )], + + // Jxx instructions + Instruction::Jnz(inst) => generate_jxx_tokens(inst, addr), + Instruction::Jz(inst) => generate_jxx_tokens(inst, addr), + Instruction::Jlo(inst) => generate_jxx_tokens(inst, addr), + Instruction::Jc(inst) => generate_jxx_tokens(inst, addr), + Instruction::Jn(inst) => generate_jxx_tokens(inst, addr), + Instruction::Jge(inst) => generate_jxx_tokens(inst, addr), + Instruction::Jl(inst) => generate_jxx_tokens(inst, addr), + Instruction::Jmp(inst) => generate_jxx_tokens(inst, addr), + + // two operand instructions + Instruction::Mov(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Add(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Addc(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Subc(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Sub(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Cmp(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Dadd(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Bit(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Bic(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Bis(inst) => generate_two_operand_tokens(inst, addr), + Instruction::Xor(inst) => generate_two_operand_tokens(inst, addr), + Instruction::And(inst) => generate_two_operand_tokens(inst, addr), + + // emulated + Instruction::Adc(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Br(inst) => generate_emulated_tokens(inst, addr, true), + Instruction::Clr(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Clrc(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Clrn(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Clrz(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Dadc(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Dec(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Decd(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Dint(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Eint(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Inc(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Incd(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Inv(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Nop(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Pop(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Ret(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Rla(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Rlc(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Sbc(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Setc(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Setn(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Setz(inst) => generate_emulated_tokens(inst, addr, false), + Instruction::Tst(inst) => generate_emulated_tokens(inst, addr, false), + } +} + +fn generate_single_operand_tokens( + inst: &impl SingleOperand, + addr: u64, + call: bool, +) -> Vec<InstructionTextToken> { + let mut res = vec![InstructionTextToken::new( + inst.mnemonic(), + InstructionTextTokenContents::Instruction, + )]; + + if inst.mnemonic().len() < MIN_MNEMONIC { + let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); + res.push(InstructionTextToken::new( + &padding, + InstructionTextTokenContents::Text, + )) + } + + res.extend_from_slice(&generate_operand_tokens(inst.source(), addr, call)); + + res +} + +fn generate_jxx_tokens(inst: &impl Jxx, addr: u64) -> Vec<InstructionTextToken> { + let fixed_addr = offset_to_absolute(addr, inst.offset()); + + let mut res = vec![InstructionTextToken::new( + inst.mnemonic(), + InstructionTextTokenContents::Instruction, + )]; + + if inst.mnemonic().len() < MIN_MNEMONIC { + let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); + res.push(InstructionTextToken::new( + &padding, + InstructionTextTokenContents::Text, + )) + } + + res.push(InstructionTextToken::new( + &format!("0x{fixed_addr:4x}"), + InstructionTextTokenContents::CodeRelativeAddress(fixed_addr), + )); + + res +} + +fn generate_two_operand_tokens(inst: &impl TwoOperand, addr: u64) -> Vec<InstructionTextToken> { + let mut res = vec![InstructionTextToken::new( + inst.mnemonic(), + InstructionTextTokenContents::Instruction, + )]; + + if inst.mnemonic().len() < MIN_MNEMONIC { + let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); + res.push(InstructionTextToken::new( + &padding, + InstructionTextTokenContents::Text, + )) + } + + res.extend_from_slice(&generate_operand_tokens(inst.source(), addr, false)); + res.push(InstructionTextToken::new( + ", ", + InstructionTextTokenContents::OperandSeparator, + )); + res.extend_from_slice(&generate_operand_tokens(inst.destination(), addr, false)); + + res +} + +fn generate_emulated_tokens( + inst: &impl Emulated, + addr: u64, + call: bool, +) -> Vec<InstructionTextToken> { + let mut res = vec![InstructionTextToken::new( + inst.mnemonic(), + InstructionTextTokenContents::Instruction, + )]; + + if inst.mnemonic().len() < MIN_MNEMONIC { + let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); + res.push(InstructionTextToken::new( + &padding, + InstructionTextTokenContents::Text, + )) + } + + if inst.destination().is_some() { + res.extend_from_slice(&generate_operand_tokens( + &inst.destination().unwrap(), + addr, + call, + )) + } + + res +} + +fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec<InstructionTextToken> { + match source { + Operand::RegisterDirect(r) => match r { + 0 => vec![InstructionTextToken::new( + "pc", + InstructionTextTokenContents::Register, + )], + 1 => vec![InstructionTextToken::new( + "sp", + InstructionTextTokenContents::Register, + )], + 2 => vec![InstructionTextToken::new( + "sr", + InstructionTextTokenContents::Register, + )], + 3 => vec![InstructionTextToken::new( + "cg", + InstructionTextTokenContents::Register, + )], + _ => vec![InstructionTextToken::new( + &format!("r{r}"), + InstructionTextTokenContents::Register, + )], + }, + Operand::Indexed((r, i)) => match r { + 0 => { + let num_text = if *i >= 0 { + format!("{i:#x}") + } else { + format!("-{:#x}", -i) + }; + vec![ + InstructionTextToken::new( + &num_text, + InstructionTextTokenContents::Integer(*i as u64), + ), + InstructionTextToken::new("(", InstructionTextTokenContents::Text), + InstructionTextToken::new("pc", InstructionTextTokenContents::Register), + InstructionTextToken::new(")", InstructionTextTokenContents::Text), + ] + } + 1 => { + let num_text = if *i >= 0 { + format!("{i:#x}") + } else { + format!("-{:#x}", -i) + }; + vec![ + InstructionTextToken::new( + &num_text, + InstructionTextTokenContents::Integer(*i as u64), + ), + InstructionTextToken::new("(", InstructionTextTokenContents::Text), + InstructionTextToken::new("sp", InstructionTextTokenContents::Register), + InstructionTextToken::new(")", InstructionTextTokenContents::Text), + ] + } + 2 => { + let num_text = if *i >= 0 { + &format!("{i:#x}") + } else { + &format!("-{:#x}", -i) + }; + vec![ + InstructionTextToken::new( + &num_text, + InstructionTextTokenContents::Integer(*i as u64), + ), + InstructionTextToken::new("(", InstructionTextTokenContents::Text), + InstructionTextToken::new("sr", InstructionTextTokenContents::Register), + InstructionTextToken::new(")", InstructionTextTokenContents::Text), + ] + } + 3 => { + let num_text = if *i >= 0 { + format!("{i:#x}") + } else { + format!("-{:#x}", -i) + }; + vec![ + InstructionTextToken::new( + &num_text, + InstructionTextTokenContents::Integer(*i as u64), + ), + InstructionTextToken::new("(", InstructionTextTokenContents::Text), + InstructionTextToken::new("cg", InstructionTextTokenContents::Register), + InstructionTextToken::new(")", InstructionTextTokenContents::Text), + ] + } + _ => { + let num_text = if *i >= 0 { + format!("{i:#x}") + } else { + format!("-{:#x}", -i) + }; + vec![ + InstructionTextToken::new( + &num_text, + InstructionTextTokenContents::Integer(*i as u64), + ), + InstructionTextToken::new("(", InstructionTextTokenContents::Text), + InstructionTextToken::new( + &format!("r{r}"), + InstructionTextTokenContents::Register, + ), + InstructionTextToken::new(")", InstructionTextTokenContents::Text), + ] + } + }, + Operand::RegisterIndirect(r) => { + let r_text = if *r == 1 { + "sp".into() + } else { + format!("r{r}") + }; + + vec![ + InstructionTextToken::new("@", InstructionTextTokenContents::Text), + InstructionTextToken::new(&r_text, InstructionTextTokenContents::Register), + ] + } + Operand::RegisterIndirectAutoIncrement(r) => { + let r_text = if *r == 1 { + "sp".into() + } else { + format!("r{r}") + }; + + vec![ + InstructionTextToken::new("@", InstructionTextTokenContents::Text), + InstructionTextToken::new(&r_text, InstructionTextTokenContents::Register), + InstructionTextToken::new("+", InstructionTextTokenContents::Text), + ] + } + Operand::Symbolic(i) => { + let val = (addr as i64 + *i as i64) as u64; + vec![InstructionTextToken::new( + &format!("{val:#x}"), + InstructionTextTokenContents::CodeRelativeAddress(val), + )] + } + Operand::Immediate(i) => { + if call { + vec![InstructionTextToken::new( + &format!("{i:#x}"), + InstructionTextTokenContents::CodeRelativeAddress(*i as u64), + )] + } else { + vec![InstructionTextToken::new( + &format!("{i:#x}"), + InstructionTextTokenContents::PossibleAddress(*i as u64), + )] + } + } + Operand::Absolute(a) => { + if call { + vec![InstructionTextToken::new( + &format!("{a:#x}"), + InstructionTextTokenContents::CodeRelativeAddress(*a as u64), + )] + } else { + vec![InstructionTextToken::new( + &format!("{a:#x}"), + InstructionTextTokenContents::PossibleAddress(*a as u64), + )] + } + } + Operand::Constant(i) => { + let num_text = if *i >= 0 { + format!("{i:#x}") + } else { + format!("-{:#x}", -i) + }; + + vec![ + InstructionTextToken::new("#", InstructionTextTokenContents::Text), + InstructionTextToken::new( + &num_text, + InstructionTextTokenContents::Integer(*i as u64), + ), + ] + } + } +} + +pub(crate) fn offset_to_absolute(addr: u64, offset: i16) -> u64 { + // add + 2 to addr to get past the jxx instruction which is always 2 bytes + ((addr + 2) as i64 + ((offset * 2) as i64)) as u64 +} diff --git a/arch/msp430/src/flag.rs b/arch/msp430/src/flag.rs new file mode 100644 index 00000000..115fe866 --- /dev/null +++ b/arch/msp430/src/flag.rs @@ -0,0 +1,153 @@ +use binaryninja::architecture; +use binaryninja::architecture::FlagRole; + +use std::borrow::Cow; +use std::collections::HashMap; + +// NOTE: GIE, CPUOFF, OSCOFF, SG0, and SG1 not implemented as it's not clear how they would be used +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Flag { + C, + Z, + N, + V, +} + +impl architecture::Flag for Flag { + type FlagClass = FlagClass; + + fn name(&self) -> Cow<str> { + match self { + Self::C => "c".into(), + Self::Z => "z".into(), + Self::N => "n".into(), + Self::V => "v".into(), + } + } + + fn role(&self, _class: Option<Self::FlagClass>) -> architecture::FlagRole { + match self { + Self::C => FlagRole::CarryFlagRole, + Self::Z => FlagRole::ZeroFlagRole, + Self::N => FlagRole::NegativeSignFlagRole, + Self::V => FlagRole::OverflowFlagRole, + } + } + + fn id(&self) -> u32 { + match self { + Self::C => 0, + Self::Z => 1, + Self::N => 2, + Self::V => 8, + } + } +} + +impl TryFrom<u32> for Flag { + type Error = (); + fn try_from(flag: u32) -> Result<Self, Self::Error> { + match flag { + 0 => Ok(Self::C), + 1 => Ok(Self::Z), + 2 => Ok(Self::N), + 8 => Ok(Self::V), + _ => Err(()), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct FlagClass {} + +impl architecture::FlagClass for FlagClass { + fn name(&self) -> Cow<str> { + unimplemented!() + } + + fn id(&self) -> u32 { + unimplemented!() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum FlagGroup {} + +impl architecture::FlagGroup for FlagGroup { + type FlagType = Flag; + type FlagClass = FlagClass; + + fn name(&self) -> Cow<str> { + unimplemented!() + } + + fn id(&self) -> u32 { + unimplemented!() + } + + fn flags_required(&self) -> Vec<Self::FlagType> { + unimplemented!() + } + + fn flag_conditions(&self) -> HashMap<Self::FlagClass, architecture::FlagCondition> { + unimplemented!() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum FlagWrite { + All, + Nz, + Nvz, + Cnz, +} + +impl architecture::FlagWrite for FlagWrite { + type FlagType = Flag; + type FlagClass = FlagClass; + + fn name(&self) -> Cow<str> { + match self { + Self::All => "*".into(), + Self::Nz => "nz".into(), + Self::Nvz => "nvz".into(), + Self::Cnz => "cnz".into(), + } + } + + fn class(&self) -> Option<Self::FlagClass> { + None + } + + fn id(&self) -> u32 { + match self { + Self::All => 1, + Self::Nz => 2, + Self::Nvz => 3, + Self::Cnz => 4, + } + } + + fn flags_written(&self) -> Vec<Self::FlagType> { + match self { + Self::All => vec![Flag::C, Flag::N, Flag::V, Flag::Z], + Self::Nz => vec![Flag::N, Flag::Z], + Self::Nvz => vec![Flag::N, Flag::V, Flag::Z], + Self::Cnz => vec![Flag::C, Flag::N, Flag::Z], + } + } +} + +impl TryFrom<u32> for FlagWrite { + type Error = (); + + fn try_from(value: u32) -> Result<Self, Self::Error> { + match value { + 1 => Ok(Self::All), + 2 => Ok(Self::Nz), + 3 => Ok(Self::Nvz), + 4 => Ok(Self::Cnz), + _ => Err(()), + } + } +} diff --git a/arch/msp430/src/lib.rs b/arch/msp430/src/lib.rs new file mode 100644 index 00000000..ffa89bad --- /dev/null +++ b/arch/msp430/src/lib.rs @@ -0,0 +1,55 @@ +extern crate binaryninja; +extern crate log; +extern crate msp430_asm; + +use binaryninja::{ + architecture::ArchitectureExt, + callingconvention, + custombinaryview::{BinaryViewType, BinaryViewTypeExt}, + Endianness, +}; + +mod architecture; +mod flag; +mod lift; +mod register; + +use architecture::Msp430; + +#[no_mangle] +#[allow(non_snake_case)] +pub extern "C" fn CorePluginInit() -> bool { + binaryninja::logger::init(log::LevelFilter::Info).unwrap(); + let arch = binaryninja::architecture::register_architecture( + "msp430", + |custom_handle, handle| Msp430::new(handle, custom_handle), + ); + + // we may need to introduce additional calling conventions here to + // support additional ABIs. MSPGCC's calling convention (what + // microcorruption seems to use) has some differences between the EABI + // calling convention though GCC has since added support for the EABI + // calling convention according to + // https://www.ti.com/lit/an/slaa664/slaa664.pdf?ts=1613210655081. MSPGCC + // appears to be a legacy calling convention while EABI is the newer + // standardized one that is compatible with TI's compiler + let default = callingconvention::ConventionBuilder::new(arch) + .is_eligible_for_heuristics(true) + .int_arg_registers(&["r15", "r14", "r13", "r12"]) + .return_int_reg("r15") + .return_hi_int_reg("r14") + .register("default"); + callingconvention::ConventionBuilder::new(arch) + .is_eligible_for_heuristics(true) + .return_int_reg("r15") + .return_hi_int_reg("r14") + .register("stack"); + + arch.set_default_calling_convention(&default); + + if let Ok(bv) = BinaryViewType::by_name("ELF") { + bv.register_arch(105, Endianness::LittleEndian, arch); + } + + true +} diff --git a/arch/msp430/src/lift.rs b/arch/msp430/src/lift.rs new file mode 100644 index 00000000..85d204e1 --- /dev/null +++ b/arch/msp430/src/lift.rs @@ -0,0 +1,670 @@ +use crate::architecture::offset_to_absolute; +use crate::flag::{Flag, FlagWrite}; +use crate::register::Register; +use crate::Msp430; + +use binaryninja::{ + architecture::FlagCondition, + llil::{Label, LiftedNonSSA, Lifter, Mutable, NonSSA}, +}; + +use msp430_asm::emulate::Emulated; +use msp430_asm::instruction::Instruction; +use msp430_asm::jxx::Jxx; +use msp430_asm::operand::{Operand, OperandWidth}; +use msp430_asm::single_operand::SingleOperand; +use msp430_asm::two_operand::TwoOperand; + +use log::info; + +macro_rules! auto_increment { + ($src:expr, $il:ident) => { + if let Operand::RegisterIndirectAutoIncrement(r) = $src { + $il.set_reg( + 2, + Register::try_from(*r as u32).unwrap(), + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, 2), + ), + ) + .append(); + } + }; +} + +macro_rules! one_operand { + ($source:expr, $il:ident, $op:ident) => { + match $source { + Operand::RegisterDirect(r) => $il + .set_reg(2, Register::try_from(*r as u32).unwrap(), $op) + .append(), + Operand::Indexed((r, offset)) => $il + .store( + 2, + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, *offset as u64), + ), + $op, + ) + .append(), + Operand::Symbolic(offset) => $il + .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op) + .append(), + Operand::Absolute(val) => $il.store(2, $il.const_ptr(*val as u64), $op).append(), + Operand::Immediate(_) => $op.append(), + Operand::RegisterIndirect(r) => $il + .store(2, $il.reg(2, Register::try_from(*r as u32).unwrap()), $op) + .append(), + Operand::RegisterIndirectAutoIncrement(r) => { + $il.store(2, $il.reg(2, Register::try_from(*r as u32).unwrap()), $op) + .append(); + $il.set_reg( + 2, + Register::try_from(*r as u32).unwrap(), + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, 2), + ), + ) + .append() + } + _ => { + unreachable!() + } + }; + }; +} + +macro_rules! two_operand { + ($destination:expr, $il:ident, $op:ident) => { + match $destination { + Operand::RegisterDirect(r) => $il + .set_reg(2, Register::try_from(*r as u32).unwrap(), $op) + .append(), + Operand::Indexed((r, offset)) => $il + .store( + 2, + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, *offset as u64), + ), + $op, + ) + .append(), + Operand::Symbolic(offset) => $il + .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op) + .append(), + Operand::Absolute(val) => $il.store(2, $il.const_ptr(*val as u64), $op).append(), + _ => { + unreachable!() + } + }; + }; +} + +macro_rules! emulated { + ($inst:ident, $il:ident, $op:ident) => { + match $inst.destination() { + Some(Operand::RegisterDirect(r)) => $il + .set_reg(2, Register::try_from(*r as u32).unwrap(), $op) + .append(), + Some(Operand::Indexed((r, offset))) => $il + .store( + 2, + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, *offset as u64), + ), + $op, + ) + .append(), + Some(Operand::Symbolic(offset)) => $il + .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op) + .append(), + Some(Operand::Absolute(val)) => $il.store(2, $il.const_ptr(*val as u64), $op).append(), + _ => { + unreachable!() + } + }; + }; +} + +macro_rules! conditional_jump { + ($addr:ident, $inst:ident, $cond:ident, $il:ident) => { + let true_addr = offset_to_absolute($addr, $inst.offset()); + let false_addr = $addr + $inst.size() as u64; + let mut new_true = Label::new(); + let mut new_false = Label::new(); + + let true_label = $il.label_for_address(true_addr); + let false_label = $il.label_for_address(false_addr); + + $il.if_expr( + $cond, + true_label.unwrap_or_else(|| &new_true), + false_label.unwrap_or_else(|| &new_false), + ) + .append(); + + if true_label.is_none() { + $il.mark_label(&mut new_true); + } + + $il.goto(true_label.unwrap_or_else(|| &new_true)).append(); + + if false_label.is_none() { + $il.mark_label(&mut new_false); + } + }; +} + +pub(crate) fn lift_instruction(inst: &Instruction, addr: u64, il: &Lifter<Msp430>) { + match inst { + Instruction::Rrc(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = il.const_int(size, 1); + let dest = lift_source_operand(inst.source(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.rrc(size, dest, src).with_flag_write(FlagWrite::All)) + } + Some(OperandWidth::Word) | None => { + il.rrc(size, dest, src).with_flag_write(FlagWrite::All) + } + }; + one_operand!(inst.source(), il, op); + } + Instruction::Swpb(inst) => { + let src = lift_source_operand(inst.source(), 2, il); + let op = il.rol(2, src, il.const_int(2, 8)); + one_operand!(inst.source(), il, op); + } + Instruction::Rra(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = il.const_int(size, 1); + let dest = lift_source_operand(inst.source(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.ror(size, dest, src).with_flag_write(FlagWrite::Cnz)) + } + Some(OperandWidth::Word) | None => { + il.ror(size, dest, src).with_flag_write(FlagWrite::Cnz) + } + }; + one_operand!(inst.source(), il, op); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + } + Instruction::Sxt(inst) => { + // source is always 1 byte and instruction is always 2 bytes for sxt because we're sign + // extending the low byte into the high and the result is always 2 bytes + let src = lift_source_operand(inst.source(), 1, il); + let op = il.sx(2, src).with_flag_write(FlagWrite::Nz); + one_operand!(inst.source(), il, op); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + } + Instruction::Push(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = lift_source_operand(inst.source(), size, il); + il.push(2, src).append(); + auto_increment!(inst.source(), il); + } + Instruction::Call(inst) => { + // TODO: verify the special autoincrement behavior Josh implemented? + let src = if let Operand::Immediate(src) = inst.source() { + il.const_ptr(*src as u64) + } else { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + + lift_source_operand(inst.source(), size, il) + }; + il.call(src).append(); + auto_increment!(inst.source(), il); + } + Instruction::Reti(_) => { + il.set_reg(2, Register::Sr, il.pop(2)) + .with_flag_write(FlagWrite::All) + .append(); + il.ret(il.pop(2)).append(); + } + + // Jxx instructions + Instruction::Jnz(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_NE); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jz(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_E); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jlo(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_ULT); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jc(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_UGE); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jn(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_NEG); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jge(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_SGE); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jl(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_SLT); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jmp(inst) => { + let fixed_addr = offset_to_absolute(addr, inst.offset()); + let label = il.label_for_address(fixed_addr); + match label { + Some(label) => { + il.goto(label).append(); + } + None => { + il.jump(il.const_ptr(fixed_addr)).append(); + } + } + } + + // two operand instructions + Instruction::Mov(inst) => { + let size = width_to_size(inst.operand_width()); + let src = match inst.operand_width() { + OperandWidth::Byte => il + .sx(2, lift_source_operand(inst.source(), size, il)) + .build(), + OperandWidth::Word => lift_source_operand(inst.source(), size, il), + }; + two_operand!(inst.destination(), il, src); + auto_increment!(inst.source(), il); + } + Instruction::Add(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => { + il.sx(2, il.add(size, src, dest).with_flag_write(FlagWrite::All)) + } + OperandWidth::Word => il.add(size, src, dest).with_flag_write(FlagWrite::All), + }; + two_operand!(inst.destination(), il, op); + auto_increment!(inst.source(), il); + } + Instruction::Addc(_) => { + il.unimplemented().append(); + } + Instruction::Subc(_) => { + il.unimplemented().append(); + } + Instruction::Sub(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => { + il.sx(2, il.sub(size, src, dest).with_flag_write(FlagWrite::All)) + } + OperandWidth::Word => il.sub(size, src, dest).with_flag_write(FlagWrite::All), + }; + two_operand!(inst.destination(), il, op); + auto_increment!(inst.source(), il); + } + Instruction::Cmp(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + il.sub(size, dest, src) + .with_flag_write(FlagWrite::All) + .append(); + auto_increment!(inst.source(), il); + } + Instruction::Dadd(_) => { + il.unimplemented().append(); + } + Instruction::Bit(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + il.and(size, src, dest) + .with_flag_write(FlagWrite::Nz) + .append(); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + auto_increment!(inst.source(), il); + } + Instruction::Bic(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => il.sx(2, il.and(size, il.not(size, src), dest)), + OperandWidth::Word => il.and(size, il.not(size, src), dest), + }; + two_operand!(inst.destination(), il, op); + auto_increment!(inst.source(), il); + } + Instruction::Bis(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => il.sx(2, il.or(size, src, dest)), + OperandWidth::Word => il.or(size, src, dest), + }; + two_operand!(inst.destination(), il, op); + auto_increment!(inst.source(), il); + } + Instruction::Xor(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => { + il.sx(2, il.xor(size, src, dest).with_flag_write(FlagWrite::Nvz)) + } + OperandWidth::Word => il.xor(size, src, dest).with_flag_write(FlagWrite::Nvz), + }; + two_operand!(inst.destination(), il, op); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + auto_increment!(inst.source(), il); + } + Instruction::And(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => { + il.sx(2, il.and(size, src, dest).with_flag_write(FlagWrite::Nz)) + } + OperandWidth::Word => il.and(size, src, dest).with_flag_write(FlagWrite::Nz), + }; + two_operand!(inst.destination(), il, op); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + auto_increment!(inst.source(), il); + } + + // emulated + Instruction::Adc(_) => { + il.unimplemented().append(); + } + Instruction::Br(inst) => { + let dest = if let Some(Operand::Immediate(dest)) = inst.destination() { + if let Some(label) = il.label_for_address(*dest as u64) { + il.goto(label).append(); + return; + } else { + il.const_ptr(*dest as u64) + } + } else { + lift_source_operand(&inst.destination().unwrap(), 2, il) + }; + + il.jump(dest).append(); + } + Instruction::Clr(inst) => { + let op = il.const_int(2, 0); + emulated!(inst, il, op); + } + Instruction::Clrc(_) => { + // TODO: should we lift clearing the C bit in the SR register as well? + il.set_flag(Flag::C, il.const_int(0, 0)).append(); + } + Instruction::Clrn(_) => { + // TODO: should we lift clearing the N bit in the SR register as well? + il.set_flag(Flag::N, il.const_int(0, 0)).append(); + } + Instruction::Clrz(_) => { + // TODO: should we lift clearing the Z bit in the SR register as well? + il.set_flag(Flag::Z, il.const_int(0, 0)).append(); + } + Instruction::Dadc(_) => { + il.unimplemented().append(); + } + Instruction::Dec(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => il.sx( + 2, + il.sub(size, dest, il.const_int(size, 1)) + .with_flag_write(FlagWrite::All), + ), + Some(OperandWidth::Word) | None => il + .sub(size, dest, il.const_int(size, 1)) + .with_flag_write(FlagWrite::All), + }; + emulated!(inst, il, op); + } + Instruction::Decd(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => il.sx( + 2, + il.sub(size, dest, il.const_int(size, 2)) + .with_flag_write(FlagWrite::All), + ), + Some(OperandWidth::Word) | None => il + .sub(size, dest, il.const_int(size, 2)) + .with_flag_write(FlagWrite::All), + }; + emulated!(inst, il, op); + } + Instruction::Dint(_) => { + // If GIE flag is ever exposed this should clear it + } + Instruction::Eint(_) => { + // If GIE flag is ever exposed this should set it + } + Instruction::Inc(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => il.sx( + 2, + il.add(size, dest, il.const_int(size, 1)) + .with_flag_write(FlagWrite::All), + ), + Some(OperandWidth::Word) | None => il + .add(size, dest, il.const_int(size, 1)) + .with_flag_write(FlagWrite::All), + }; + emulated!(inst, il, op); + } + Instruction::Incd(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => il.sx( + 2, + il.add(size, dest, il.const_int(size, 2)) + .with_flag_write(FlagWrite::All), + ), + Some(OperandWidth::Word) | None => il + .add(size, dest, il.const_int(size, 2)) + .with_flag_write(FlagWrite::All), + }; + emulated!(inst, il, op); + } + Instruction::Inv(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.not(size, dest).with_flag_write(FlagWrite::Nvz)) + } + Some(OperandWidth::Word) | None => { + il.not(size, dest).with_flag_write(FlagWrite::Nvz) + } + }; + emulated!(inst, il, op); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + } + Instruction::Nop(_) => { + il.nop().append(); + } + Instruction::Pop(inst) => { + if let Some(Operand::RegisterDirect(r)) = inst.destination() { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + il.set_reg(size, Register::try_from(*r as u32).unwrap(), il.pop(2)) + .append(); + } else { + info!("pop: invalid destination operand"); + } + } + Instruction::Ret(_) => { + il.ret(il.pop(2)).append(); + } + Instruction::Rla(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = il.const_int(size, 1); + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.rol(size, dest, src).with_flag_write(FlagWrite::All)) + } + Some(OperandWidth::Word) | None => { + il.rol(size, dest, src).with_flag_write(FlagWrite::All) + } + }; + emulated!(inst, il, op); + } + Instruction::Rlc(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = il.const_int(size, 1); + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.rlc(size, dest, src).with_flag_write(FlagWrite::All)) + } + Some(OperandWidth::Word) | None => { + il.rlc(size, dest, src).with_flag_write(FlagWrite::All) + } + }; + emulated!(inst, il, op); + } + Instruction::Sbc(_) => { + il.unimplemented().append(); + } + Instruction::Setc(_) => { + // TODO: should we lift setting the C bit in the SR register as well? + il.set_flag(Flag::C, il.const_int(0, 1)).append(); + } + Instruction::Setn(_) => { + // TODO: should we lift setting the N bit in the SR register as well? + il.set_flag(Flag::N, il.const_int(0, 1)).append(); + } + Instruction::Setz(_) => { + // TODO: should we lift setting the Z bit in the SR register as well? + il.set_flag(Flag::Z, il.const_int(0, 1)).append(); + } + Instruction::Tst(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + il.sub(size, dest, il.const_int(size, 0)) + .with_flag_write(FlagWrite::Nz) + .append(); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + il.set_flag(Flag::C, il.const_int(0, 1)).append(); + } + } +} + +fn lift_source_operand<'a>( + operand: &Operand, + size: usize, + il: &'a Lifter<Msp430>, +) -> binaryninja::llil::Expression< + 'a, + Msp430, + Mutable, + NonSSA<LiftedNonSSA>, + binaryninja::llil::ValueExpr, +> { + match operand { + Operand::RegisterDirect(r) => il.reg(size, Register::try_from(*r as u32).unwrap()), + Operand::Indexed((r, offset)) => il + .load( + size, + il.add( + 2, + il.reg(2, Register::try_from(*r as u32).unwrap()), + il.const_int(2, *offset as u64), + ), + ) + .build(), + // should we add offset to addr here rather than lifting to the register since we know where PC is? + Operand::Symbolic(offset) => il + .load( + size, + il.add(2, il.reg(2, Register::Pc), il.const_int(2, *offset as u64)), + ) + .build(), + Operand::Absolute(addr) => il.load(size, il.const_ptr(*addr as u64)).build(), + // these are the same, we need to autoincrement in a separate il instruction + Operand::RegisterIndirect(r) | Operand::RegisterIndirectAutoIncrement(r) => il + .load(size, il.reg(2, Register::try_from(*r as u32).unwrap())) + .build(), + Operand::Immediate(val) => il.const_int(size, *val as u64), + Operand::Constant(val) => il.const_int(size, *val as u64), + } +} + +fn width_to_size(width: &OperandWidth) -> usize { + match width { + OperandWidth::Byte => 1, + OperandWidth::Word => 2, + } +} diff --git a/arch/msp430/src/register.rs b/arch/msp430/src/register.rs new file mode 100644 index 00000000..20a5dff8 --- /dev/null +++ b/arch/msp430/src/register.rs @@ -0,0 +1,130 @@ +use binaryninja::architecture; +use binaryninja::architecture::ImplicitRegisterExtend; + +use std::borrow::Cow; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Register { + Pc, + Sp, + Sr, + Cg, + R4, + R5, + R6, + R7, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, +} + +impl TryFrom<u32> for Register { + type Error = (); + + fn try_from(id: u32) -> Result<Self, Self::Error> { + // TODO: we should return separate errors if the id is between 0x7fff_ffff and 0xffff_ffff + // vs outside of that range. Temporary registers have have the high bit set which we + // shouldn't get, unless there is a bug in core. An id that isn't within that range but we + // don't handle is a bug in the architecture. + match id { + 0 => Ok(Self::Pc), + 1 => Ok(Self::Sp), + 2 => Ok(Self::Sr), + 3 => Ok(Self::Cg), + 4 => Ok(Self::R4), + 5 => Ok(Self::R5), + 6 => Ok(Self::R6), + 7 => Ok(Self::R7), + 8 => Ok(Self::R8), + 9 => Ok(Self::R9), + 10 => Ok(Self::R10), + 11 => Ok(Self::R11), + 12 => Ok(Self::R12), + 13 => Ok(Self::R13), + 14 => Ok(Self::R14), + 15 => Ok(Self::R15), + _ => Err(()), + } + } +} + +impl architecture::Register for Register { + type InfoType = Self; + + fn name(&self) -> Cow<'_, str> { + match self { + Self::Pc => "pc".into(), + Self::Sp => "sp".into(), + Self::Sr => "sr".into(), + Self::Cg => "cg".into(), + Self::R4 + | Self::R5 + | Self::R6 + | Self::R7 + | Self::R8 + | Self::R9 + | Self::R10 + | Self::R11 + | Self::R12 + | Self::R13 + | Self::R14 + | Self::R15 => format!("r{}", self.id()).into(), + } + } + + fn info(&self) -> Self::InfoType { + *self + } + + fn id(&self) -> u32 { + match self { + Self::Pc => 0, + Self::Sp => 1, + Self::Sr => 2, + Self::Cg => 3, + Self::R4 => 4, + Self::R5 => 5, + Self::R6 => 6, + Self::R7 => 7, + Self::R8 => 8, + Self::R9 => 9, + Self::R10 => 10, + Self::R11 => 11, + Self::R12 => 12, + Self::R13 => 13, + Self::R14 => 14, + Self::R15 => 15, + } + } +} + +impl architecture::RegisterInfo for Register { + type RegType = Self; + + fn parent(&self) -> Option<Self::RegType> { + None + } + + fn size(&self) -> usize { + 2 + } + + fn offset(&self) -> usize { + 0 + } + + fn implicit_extend(&self) -> ImplicitRegisterExtend { + ImplicitRegisterExtend::NoExtend + } +} + +impl From<Register> for binaryninja::llil::Register<Register> { + fn from(register: Register) -> Self { + binaryninja::llil::Register::ArchReg(register) + } +} |
