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/architecture.rs | |
| parent | 2a9d07d7fefe5a6d7f7a482397060a96782142ac (diff) | |
Add msp430 architecture
Diffstat (limited to 'arch/msp430/src/architecture.rs')
| -rw-r--r-- | arch/msp430/src/architecture.rs | 763 |
1 files changed, 763 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 +} |
