From 4f0cb26cacd5bf7c42850407bfab29bdba5986e4 Mon Sep 17 00:00:00 2001 From: iximeow Date: Mon, 18 Feb 2019 18:06:02 -0800 Subject: fix decode issue with stm/ldm/b, add tests, add display impl --- Cargo.toml | 8 +- src/armv7.rs | 1157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test.rs | 394 ++++++++++++++++++++ 3 files changed, 1557 insertions(+), 2 deletions(-) create mode 100644 src/armv7.rs create mode 100644 test/test.rs diff --git a/Cargo.toml b/Cargo.toml index 93c69b3..d9f2b9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "yaxpeax-pic24" +name = "yaxpeax-arm" version = "0.0.1" authors = [ "iximeow " ] license = "0BSD" repository = "" description = """ -A rust pic24 decoder +A rust arm decoder """ [dependencies] @@ -15,3 +15,7 @@ yaxpeax-arch = { path = "../../yaxpeax-arch" } [[test]] name = "test" path = "test/test.rs" + +[[bench]] +name = "bench" +path = "test/test.rs" diff --git a/src/armv7.rs b/src/armv7.rs new file mode 100644 index 0000000..d65c8ae --- /dev/null +++ b/src/armv7.rs @@ -0,0 +1,1157 @@ +use std::fmt::{Display, Formatter}; + +use yaxpeax_arch::{Arch, Decodable, LengthedInstruction}; + +impl Display for Opcode { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + match self { + Opcode::Incomplete(word) => { write!(f, "incomplete: {:#x}", word) }, + Opcode::Invalid => { write!(f, "invalid") }, + Opcode::POP => { write!(f, "pop") }, + Opcode::B => { write!(f, "b") }, + Opcode::BL => { write!(f, "bl") }, + Opcode::BLX => { write!(f, "blx") }, + Opcode::BX => { write!(f, "bx") }, + Opcode::BXJ => { write!(f, "bxj") }, + Opcode::AND => { write!(f, "and") }, + Opcode::EOR => { write!(f, "eor") }, + Opcode::SUB => { write!(f, "sub") }, + Opcode::RSB => { write!(f, "rsb") }, + Opcode::ADD => { write!(f, "add") }, + Opcode::ADC => { write!(f, "adc") }, + Opcode::SBC => { write!(f, "sbc") }, + Opcode::RSC => { write!(f, "rsc") }, + Opcode::TST => { write!(f, "tst") }, + Opcode::TEQ => { write!(f, "teq") }, + Opcode::CMP => { write!(f, "cmp") }, + Opcode::CMN => { write!(f, "cmn") }, + Opcode::ORR => { write!(f, "orr") }, + Opcode::MOV => { write!(f, "mov") }, + Opcode::BIC => { write!(f, "bic") }, + Opcode::MVN => { write!(f, "mvn") }, + Opcode::LSL => { write!(f, "lsl") }, + Opcode::LSR => { write!(f, "lsr") }, + Opcode::ASR => { write!(f, "asr") }, + Opcode::RRX => { write!(f, "rrx") }, + Opcode::ROR => { write!(f, "ror") }, + Opcode::ADR => { write!(f, "adr") }, + Opcode::LDREXH => { write!(f, "ldrexh") }, + Opcode::STREXH => { write!(f, "strexh") }, + Opcode::LDREXB => { write!(f, "ldrexb") }, + Opcode::STREXB => { write!(f, "strexb") }, + Opcode::LDREXD => { write!(f, "ldrexd") }, + Opcode::STREXD => { write!(f, "strexd") }, + Opcode::LDREX => { write!(f, "ldrex") }, + Opcode::STREX => { write!(f, "strex") }, + Opcode::LDM(false, false, _, _) => { write!(f, "ldmda") }, + Opcode::LDM(false, true, _, _) => { write!(f, "ldmdb") }, + Opcode::LDM(true, false, _, _) => { write!(f, "ldm") }, + Opcode::LDM(true, true, _, _) => { write!(f, "ldmia") }, + Opcode::STM(false, false, _, _) => { write!(f, "stmda") }, + Opcode::STM(false, true, _, _) => { write!(f, "stmdb") }, + Opcode::STM(true, false, _, _) => { write!(f, "stm") }, + Opcode::STM(true, true, _, _) => { write!(f, "stmia") }, + Opcode::LDR(_, _, _) => { write!(f, "ldr") }, + Opcode::STR(_, _, _) => { write!(f, "str") }, + Opcode::LDRB(_, _, _) => { write!(f, "ldrb") }, + Opcode::STRB(_, _, _) => { write!(f, "strb") }, + Opcode::LDRT(_) => { write!(f, "ldrt") }, + Opcode::STRT(_) => { write!(f, "strt") }, + Opcode::LDRBT(_) => { write!(f, "ldrbt") }, + Opcode::STRBT(_) => { write!(f, "strbt") }, + Opcode::SWP => { write!(f, "swp") }, + Opcode::SWPB => { write!(f, "swpb") }, + Opcode::MUL => { write!(f, "mul") }, + Opcode::MLA => { write!(f, "mla") }, + Opcode::UMAAL => { write!(f, "umaal") }, + Opcode::MLS => { write!(f, "mls") }, + Opcode::UMULL => { write!(f, "umull") }, + Opcode::UMLAL => { write!(f, "umlal") }, + Opcode::SMULL => { write!(f, "smull") }, + Opcode::SMLAL => { write!(f, "smlal") } + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Opcode { + Incomplete(u32), + Invalid, + POP, + B, + BL, + BLX, + BX, + BXJ, + AND, + EOR, + SUB, + RSB, + ADD, + ADC, + SBC, + RSC, + TST, + TEQ, + CMP, + CMN, + ORR, + MOV, + BIC, + MVN, + LSL, + LSR, + ASR, + RRX, + ROR, + ADR, + LDREXH, + STREXH, + LDREXB, + STREXB, + LDREXD, + STREXD, + LDREX, + STREX, + LDM(bool, bool, bool, bool), + STM(bool, bool, bool, bool), + LDR(bool, bool, bool), + STR(bool, bool, bool), + LDRB(bool, bool, bool), + STRB(bool, bool, bool), + LDRT(bool), + STRT(bool), + LDRBT(bool), + STRBT(bool), + SWP, + SWPB, + MUL, + MLA, + UMAAL, + MLS, + UMULL, + UMLAL, + SMULL, + SMLAL +} + +static DATA_PROCESSING_OPCODES: [Opcode; 16] = [ + Opcode::AND, + Opcode::EOR, + Opcode::SUB, + Opcode::RSB, + Opcode::ADD, + Opcode::ADC, + Opcode::SBC, + Opcode::RSC, + Opcode::TST, + Opcode::TEQ, + Opcode::CMP, + Opcode::CMN, + Opcode::ORR, + Opcode::MOV, + Opcode::BIC, + Opcode::MVN +]; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum ShiftSpec { + Immediate(u8), + Register(u8) +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Operands { + RegisterList(u16), + TwoOperand(u8, u8), + RegImm(u8, u32), + RegRegList(u8, u16), + TwoRegImm(u8, u8, u32), + ThreeOperand(u8, u8, u8), + ThreeOperandImm(u8, u8, u16), + ThreeOperandWithShift(u8, u8, u8, ShiftSpec), + MulThreeRegs(u8, u8, u8), + MulFourRegs(u8, u8, u8, u8), + BranchOffset(i32) +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Instruction { + pub condition: ConditionCode, + pub opcode: Opcode, + pub operands: Operands, + pub s: bool +} + +impl Instruction { + pub fn blank() -> Instruction { + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::Invalid, + operands: Operands::BranchOffset(0), + s: false + } + } + pub fn set_S(&mut self, value: bool) { + self.s = value; + } + pub fn S(&self) -> bool { self.s } +} + +impl Display for Instruction { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + fn format_reg_list(f: &mut Formatter, mut list: u16) -> Result<(), std::fmt::Error> { + write!(f, "{{"); + let mut i = 0; + let mut tail = false; + while i < 16 { + let present = (list & 1) == 1; + if present { + if tail { + write!(f, ", ")?; + } else { + tail = true; + } + write!(f, "{}", reg_name(i)); + } + i += 1; + list >>= 1; + } + write!(f, "}}") + } + + fn format_shift(f: &mut Formatter, Rm: u8, shift: ShiftSpec) -> Result<(), std::fmt::Error> { + fn shift_tpe_to_str(tpe: u8) -> &'static str { + match tpe { + 0b00 => "lsl", + 0b01 => "lsr", + 0b10 => "asr", + 0b11 => "ror", + _ => { unreachable!(); } + } + } + match shift { + ShiftSpec::Immediate(0) => { + write!(f, "{}", reg_name(Rm)) + }, + ShiftSpec::Immediate(v) => { + let tpe = v & 0x3; + let imm = v >> 2; + write!(f, "{}, {} {}", reg_name(Rm), shift_tpe_to_str(tpe), imm) + }, + ShiftSpec::Register(v) => { + let tpe = v & 0x3; + let Rs = v >> 2; + write!(f, "{}, {} {}", reg_name(Rm), shift_tpe_to_str(tpe), reg_name(Rs)) + }, + } + } + + fn format_reg_shift_mem(f: &mut Formatter, Rd: u8, Rm: u8, shift: ShiftSpec, add: bool, pre: bool, wback: bool) -> Result<(), std::fmt::Error> { + let op = if add { "" } else { "-" }; + + match (pre, wback) { + (true, true) => { + write!(f, "[{}, {}", reg_name(Rd), op)?; + format_shift(f, Rm, shift)?; + write!(f, "]!") + }, + (true, false) => { + write!(f, "[{}, {}", reg_name(Rd), op)?; + format_shift(f, Rm, shift)?; + write!(f, "]") + }, + (false, true) => { + unreachable!("I don't know how to render an operand with pre==false and wback==true, this seems like it should be LDRT"); + }, + (false, false) => { + write!(f, "[{}], {}", reg_name(Rd), op)?; + format_shift(f, Rm, shift) + } + } + } + + fn format_reg_imm_mem(f: &mut Formatter, Rn: u8, imm: u32, add: bool, pre: bool, wback: bool) -> Result<(), std::fmt::Error> { + if imm != 0 { + let op = if add { "" } else { "-" }; + + match (pre, wback) { + (true, true) => { + write!(f, "[{}, #{}{:#x}]!", reg_name(Rn), op, imm * 4) + }, + (true, false) => { + write!(f, "[{}, #{}{:#x}]", reg_name(Rn), op, imm * 4) + }, + (false, true) => { + unreachable!("I don't know how to render an operand with pre==false and wback==true, this seems like it should be LDRT"); + }, + (false, false) => { + write!(f, "[{}], #{}{:#x}", reg_name(Rn), op, imm * 4) + } + } + } else { + match (pre, wback) { + (true, true) => { + write!(f, "[{}]!", reg_name(Rn)) + }, + (true, false) => { + write!(f, "[{}]", reg_name(Rn)) + }, + (false, true) => { + unreachable!("I don't know how to render an operand with pre==false and wback==true, this seems like it should be LDRT"); + }, + (false, false) => { + write!(f, "[{}]", reg_name(Rn)) + } + } + } + } + fn reg_name(num: u8) -> &'static str { + match num { + 0 => "r0", + 1 => "r1", + 2 => "r2", + 3 => "r3", + 4 => "r4", + 5 => "r5", + 6 => "r6", + 7 => "r7", + 8 => "r8", + 9 => "sb", + 10 => "r10", + 11 => "fp", + 12 => "ip", + 13 => "sp", + 14 => "lr", + 15 => "pc", + _ => { unreachable!(); } + } + } + match self.opcode { + Opcode::LDR(true, false, false) => { + match self.operands { + Operands::TwoRegImm(13, Rt, 4) => { + return write!(f, "pop{} {{{}}}", self.condition, reg_name(Rt)); + }, + _ => {} + } + }, + Opcode::STR(false, true, true) => { + match self.operands { + Operands::TwoRegImm(13, Rt, 4) => { + return write!(f, "push{} {{{}}}", self.condition, reg_name(Rt)); + }, + _ => {} + } + }, + Opcode::LDM(true, false, true, _usermode) => { + // TODO: what indicates usermode in the ARM syntax? + match self.operands { + Operands::RegRegList(13, list) => { + write!(f, "pop{} ", self.condition)?; + return format_reg_list(f, list); + } + _ => {} + } + } + Opcode::STM(false, true, true, _usermode) => { + // TODO: what indicates usermode in the ARM syntax? + match self.operands { + Operands::RegRegList(13, list) => { + write!(f, "push{} ", self.condition)?; + return format_reg_list(f, list); + } + _ => {} + } + } + _ => {} + } + + match self.opcode { + Opcode::LDR(add, pre, wback) | + Opcode::STR(add, pre, wback) | + Opcode::STRB(add, pre, wback) | + Opcode::LDRB(add, pre, wback) => { + match self.operands { + Operands::TwoRegImm(Rn, Rt, imm) => { + write!( + f, "{}{} {}, ", + self.opcode, + self.condition, + reg_name(Rt), + )?; + return format_reg_imm_mem(f, Rn, imm, add, pre, wback); + } + // TODO: this might not be necessary + Operands::RegImm(Rt, imm) => { + write!( + f, "{}{} {}, ", + self.opcode, + self.condition, + reg_name(Rt) + )?; + return format_reg_imm_mem(f, 15, imm, add, pre, wback); + }, + Operands::ThreeOperandWithShift(Rd, Rn, Rm, shift) => { + write!( + f, "{}{} {}, ", + self.opcode, + self.condition, + reg_name(Rn) + )?; + return format_reg_shift_mem(f, Rd, Rm, shift, add, pre, wback); + } + _ => { unreachable!(); } + } + } + Opcode::STM(add, pre, wback, usermode) | + Opcode::LDM(add, pre, wback, usermode) => { + match self.operands { + Operands::RegRegList(Rr, list) => { + write!( + f, "{}{} {}{}, ", + self.opcode, + self.condition, + reg_name(Rr), + if wback { "!" } else { "" } + )?; + return format_reg_list(f, list); + }, + _ => { unreachable!(); } + } + }, + Opcode::Incomplete(word) => { + write!(f, "incomplete: {:#x}", word) + }, + _ => { + write!(f, "{}{}", self.opcode, self.condition)?; + match self.operands { + Operands::RegisterList(list) => { + write!(f, " ")?; + format_reg_list(f, list)?; + }, + Operands::TwoOperand(a, b) => { + write!(f, " {}, {}", reg_name(a), reg_name(b))?; + }, + Operands::RegImm(a, imm) => { + write!(f, " {}, {:#x}", reg_name(a), imm * 4)?; + }, + Operands::RegRegList(r, list) => { + write!(f, " {}, ", reg_name(r))?; + format_reg_list(f, list)?; + }, + Operands::TwoRegImm(a, b, imm) => { + write!(f, " ")?; + }, + Operands::ThreeOperand(a, b, c) => { + write!(f, " {}, {}, {}", reg_name(a), reg_name(b), reg_name(c))?; + }, + Operands::ThreeOperandImm(a, b, imm) => { + write!(f, " ")?; + }, + Operands::ThreeOperandWithShift(a, b, c, shift) => { + write!(f, " {}, {}, ", reg_name(a), reg_name(b))?; + format_shift(f, c, shift)?; + }, + Operands::MulThreeRegs(a, b, c) => { + write!(f, " {}, {}, {}", reg_name(a), reg_name(b), reg_name(c))?; + }, + Operands::MulFourRegs(a, b, c, d) => { + write!(f, " ")?; + }, + Operands::BranchOffset(imm) => { + if imm < 0 { + write!(f, " $-{:#x}", (-imm) * 4)?; + } else { + write!(f, " $+{:#x}", imm * 4)?; + } + } + }; + Ok(()) + } + } + } +} + +impl LengthedInstruction for Instruction { + type Unit = ::Address; + fn min_size() -> Self::Unit { + 4 + } + fn len(&self) -> Self::Unit { + 4 + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum ConditionCode { + EQ, + NE, + HS, + LO, + MI, + PL, + VS, + VC, + HI, + LS, + GE, + LT, + GT, + LE, + AL +} + +impl Display for ConditionCode { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + match self { + ConditionCode::EQ => write!(f, "eq"), + ConditionCode::NE => write!(f, "ne"), + ConditionCode::HS => write!(f, "hs"), + ConditionCode::LO => write!(f, "lo"), + ConditionCode::MI => write!(f, "mi"), + ConditionCode::PL => write!(f, "pl"), + ConditionCode::VS => write!(f, "vs"), + ConditionCode::VC => write!(f, "vc"), + ConditionCode::HI => write!(f, "hi"), + ConditionCode::LS => write!(f, "ls"), + ConditionCode::GE => write!(f, "ge"), + ConditionCode::LT => write!(f, "lt"), + ConditionCode::GT => write!(f, "gt"), + ConditionCode::LE => write!(f, "le"), + ConditionCode::AL => Ok(()) + } + } +} + +impl ConditionCode { + pub fn build(value: u8) -> ConditionCode { + match value { + 0b0000 => ConditionCode::EQ, + 0b0001 => ConditionCode::NE, + 0b0010 => ConditionCode::HS, + 0b0011 => ConditionCode::LO, + 0b0100 => ConditionCode::MI, + 0b0101 => ConditionCode::PL, + 0b0110 => ConditionCode::VS, + 0b0111 => ConditionCode::VC, + 0b1000 => ConditionCode::HI, + 0b1001 => ConditionCode::LS, + 0b1010 => ConditionCode::GE, + 0b1011 => ConditionCode::LT, + 0b1100 => ConditionCode::GT, + 0b1101 => ConditionCode::LE, + 0b1110 => ConditionCode::AL, + _ => unsafe { + // this means the argument `value` must never be outside [0,15] + // which itself means this function shouldn't be public + unreachable!(); + } + } + } +} + +impl Decodable for Instruction { + fn decode<'a, T: IntoIterator>(bytes: T) -> Option { + let mut blank = Instruction::blank(); + match blank.decode_into(bytes) { + Some(_) => Some(blank), + None => None + } + } + fn decode_into<'a, T: IntoIterator>(&mut self, bytes: T) -> Option<()> { + fn read_word<'a, T: IntoIterator>(bytes: T) -> Option { + let mut iter = bytes.into_iter(); + let instr: u32 = + ((*iter.next()? as u32) ) | + ((*iter.next()? as u32) << 8 ) | + ((*iter.next()? as u32) << 16) | + ((*iter.next()? as u32) << 24); + + Some(instr) + } + + let word = match read_word(bytes) { + Some(word) => word, + None => { return None; } + }; + + let (cond, opc_upper) = { + let top_byte = word >> 24; + ( + ((top_byte >> 4) & 0xf) as u8, + ((top_byte >> 1) & 0x7) as u8 + ) + }; + + if cond == 0b1111 { + // do unconditional instruction stuff + self.condition = ConditionCode::AL; + self.opcode = Opcode::Incomplete(word); + return Some(()); + } else { + self.condition = ConditionCode::build(cond); + } + + // distinction at this point is on page A5-192 + match opc_upper { + 0b000 => { + // the instruction looks like + // |c o n d|0 0 0|x x x x|x|x x x x|x x x x|x x x x x|x x|x|x x x x| + let (s, opcode) = { + let part = word >> 20; + ( + (part & 0x01) == 1, + ((part >> 1) & 0x0f) as u8 + ) + }; + + if (word & 0b10010000) == 0b10010000 { + // the instruction looks like + // |c o n d|0 0 0|x x x x|x|x x x x|x x x x|x x x x 1|x x|1|x x x x| + // which is a category of multiplies and extra load/store + if (word & 0x0f0000f0) == 0x00000090 { + // |c o n d|0 0 0 0|x x x x x x x x x x x x x x x x|1 0 0 1|x x x x| + // Multiply instruction extension space + // (page A5-200) + let op = ((word >> 20) & 0x0f) as u8; + let s = (op & 1) == 1; + let op = op >> 1; + let R = [ + (word & 0x0f) as u8, + ((word >> 8) & 0x0f) as u8, + ((word >> 12) & 0x0f) as u8, + ((word >> 16) & 0x0f) as u8 + ]; + self.set_S(s); + match op { + 0b000 => { + self.opcode = Opcode::MUL; + self.operands = Operands::MulThreeRegs(R[3], R[0], R[1]); + }, + 0b001 => { + self.opcode = Opcode::MLA; + self.operands = Operands::MulFourRegs(R[3], R[0], R[1], R[2]); + }, + 0b010 => { + if s { + self.opcode = Opcode::Invalid; + return None; + } + self.opcode = Opcode::UMAAL; + self.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + }, + 0b011 => { + if s { + self.opcode = Opcode::Invalid; + return None; + } + self.opcode = Opcode::MLS; + self.operands = Operands::MulFourRegs(R[3], R[0], R[1], R[2]); + } + 0b100 => { + self.opcode = Opcode::UMULL; + self.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + } + 0b101 => { + self.opcode = Opcode::UMLAL; + self.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + } + 0b110 => { + self.opcode = Opcode::SMULL; + self.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + } + 0b111 => { + self.opcode = Opcode::SMLAL; + self.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + } + _ => { unreachable!(format!("mul upcode: {:x}", op)) } + } + } else { + // |c o n d|0 0 0 u|x x x x x x x x x x x x x x x x|1 u u 1|x x x x| + // with at least one of u being 1 + // misc instructions + let (flags, Rn, Rd, HiOffset, op, LoOffset) = { + let LoOffset = (word & 0x0f) as u8; + let word = word >> 5; + let op = (word & 0x3) as u8; + let word = word >> 3; + let HiOffset = (word & 0x0f) as u8; + let word = word >> 4; + let Rd = (word & 0x0f) as u8; + let word = word >> 4; + let Rn = (word & 0x0f) as u8; + let word = word >> 4; + let flags = (word & 0x1f) as u8; + (flags, Rn, Rd, HiOffset, op, LoOffset) + }; + println!("{:032b}", word); + println!(" {:05b}|{:04b}|{:04b}|{:04b}|1{:02b}1|{:04b}", flags, Rn, Rd, HiOffset, op, LoOffset); + match op { + 0x00 => { + // |c o n d|0 0 0 1|x x x x x x x x x x x x x x x x|1 0 0 1|x x x x| + // this is swp or {ld,st}ex, conditional on bit 23 + match flags { + 0b10000 => { + self.opcode = Opcode::SWP; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + }, + 0b10001 | 0b10010 | 0b10011 => { + self.opcode = Opcode::Invalid; + return None; + } + 0b10100 => { + self.opcode = Opcode::SWPB; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + }, + 0b10101 | 0b10110 | 0b10111 => { + self.opcode = Opcode::Invalid; + return None; + } + 0b11000 => { + self.opcode = Opcode::STREX; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + 0b11001 => { + self.opcode = Opcode::LDREX; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + 0b11010 => { + self.opcode = Opcode::STREXD; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + 0b11011 => { + self.opcode = Opcode::LDREXD; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + 0b11100 => { + self.opcode = Opcode::STREXB; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + 0b11101 => { + self.opcode = Opcode::LDREXB; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + 0b11110 => { + self.opcode = Opcode::STREXH; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + 0b11111 => { + self.opcode = Opcode::LDREXH; + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + _ => { + /* + * This is unreachable because we have checked op is b1001, + * meaning the high bit of flags *MUST* be 1. + * + * high bit and mid-bits of op all being 0 was checked + * before reaching here. + */ + unreachable!(format!("load/store flags: {:x}", flags)); + } + } + } + 0x01 => { + // |c o n d|0 0 0 x|x x x x x x x x x x x x x x x x|1 0 1 1|x x x x| + // page A5-201 + self.opcode = Opcode::Incomplete(word); + return Some(()); + match flags { + 0b00010 => { + // self.opcode = Opcode::STRHT_sub; + self.opcode = Opcode::Incomplete(word); + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + 0b01010 => { + // self.opcode = Opcode::STRHT_add; + self.opcode = Opcode::Incomplete(word); + self.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + } + 0b00110 => { + // self.opcode = Opcode::STRHT_sub; + self.opcode = Opcode::Incomplete(word); + let imm = ((HiOffset << 4) as u16 | LoOffset as u16); + self.operands = Operands::ThreeOperandImm(Rn, Rd, imm); + } + 0b01110 => { + // self.opcode = Opcode::STRHT_add; + self.opcode = Opcode::Incomplete(word); + let imm = ((HiOffset << 4) as u16 | LoOffset as u16); + self.operands = Operands::ThreeOperandImm(Rn, Rd, imm); + } + _ => { + unreachable!(); + } + } + } + 0x10 => { + // |c o n d|0 0 0 x|x x x x x x x x x x x x x x x x|1 1 0 1|x x x x| + // page A5-201 + self.opcode = Opcode::Incomplete(word); + return Some(()); + } + 0x11 => { + // |c o n d|0 0 0 x|x x x x x x x x x x x x x x x x|1 1 1 1|x x x x| + // page A5-201 + self.opcode = Opcode::Incomplete(word); + return Some(()); + } + _ => { unreachable!(); } + } + } + } else { + // we know this is data processing with imm or reg shift, OR + // misc instructions in Figure A3-4 + + if s == false && opcode >= 0b1000 && opcode < 0b1100 { + // the instruction looks like + // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x x|x x|x|x x x x| + // misc instructions (page A5-194) + self.opcode = Opcode::Incomplete(word); + return Some(()); + } else { + if opcode >= 16 { + unreachable!(); + } + self.opcode = DATA_PROCESSING_OPCODES[opcode as usize]; + self.set_S(s); + + // at this point we know this is a data processing instruction + // either immediate shift or register shift + if word & 0b00010000 == 0 { + // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x x|x x|0|x x x x| + // interpret the operands as + // | Rn | Rd | shift amount | shift | 0 | Rm | + let (Rn, Rd, shift_spec, Rm) = { + let Rm = (word & 0x0f) as u8; + let word = word >> 5; + let shift_spec = (word & 0x7f) as u8; + let word = word >> 7; + let Rd = (word & 0x0f) as u8; + let Rn = ((word >> 4) & 0x0f) as u8; + (Rn, Rd, shift_spec, Rm) + }; + + if shift_spec == 0 { + if (0b1101 & opcode) == 0b1101 { + // MOV or MVN + self.operands = Operands::TwoOperand(Rd, Rm); + } else { + self.operands = Operands::ThreeOperand(Rd, Rn, Rm); + } + } else { + /* + * TODO: look at how this interacts with mov and mvn + */ + self.operands = Operands::ThreeOperandWithShift(Rd, Rn, Rm, ShiftSpec::Immediate(shift_spec)); + } + } else { + // known 0 because it and bit 5 are not both 1 --v + // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x 0|x x|1|x x x x| + // interpret the operands as + // | Rn | Rd | Rs | 0 | shift | 1 | Rm | + let (Rn, Rd, shift_spec, Rm) = { + let Rm = (word & 0x0f) as u8; + let word = word >> 5; + let shift_spec = (word & 0x7f) as u8; + let word = word >> 7; + let Rd = (word & 0x0f) as u8; + let Rn = ((word >> 4) & 0x0f) as u8; + (Rn, Rd, shift_spec, Rm) + }; + // page A5-200 indicates that saturating add and subtract should be + // here? + if (0b1101 & opcode) == 0b1101 { + // these are all invalid + self.opcode = Opcode::Invalid; + return None; + } else { + self.operands = Operands::ThreeOperandWithShift(Rd, Rn, Rm, ShiftSpec::Register(shift_spec)); + } + } + } + } + }, + 0b001 => { + // the instruction looks like + // |c o n d|0 0 1|x x x x|x|x x x x|x x x x|x x x x x|x x|x|x x x x| + // bottom part of table A5-2 on page A5-194 + let (s, opcode) = { + let part = word >> 20; + ( + (part & 0x01) == 1, + ((part >> 1) & 0x0f) as u8 + ) + }; + if s == false && opcode >= 0b1000 && opcode < 0b1100 { + // the instruction looks like + // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x x|x x|x|x x x x| + // misc instructions (page A5-194) + self.opcode = Opcode::Incomplete(word); + return Some(()); + } else { + if opcode >= 16 { + unreachable!(); + } + self.opcode = DATA_PROCESSING_OPCODES[opcode as usize]; + self.set_S(s); + + let (Rn, imm) = { + let imm = word & 0x0000ffff; + let word = word >> 16; + ((word & 0x0f) as u8, imm) + }; + if (opcode == 0b0010 || opcode == 0b0100) && Rn == 0b1111 { + self.opcode = Opcode::ADR; + } + match opcode { + 0b1101 => { + self.operands = Operands::RegImm( + ((word >> 12) & 0xf) as u8, + (word & 0x0fff) as u32 + ); + } + _ => { + self.operands = Operands::RegImm(Rn, imm); + } + } + + } + /* ... */ + } + 0b010 => { + let Rn = ((word >> 16) & 0x0f) as u8; + let op = ((word >> 20) & 0x1f) as u8; + let add = (op & 0b01000) != 0; + let (imm, Rt) = { + ((word & 0x0fff) as u16, ((word >> 12) & 0x0f) as u8) + }; + if (op & 0b10010) == 0b00010 { + let op = op & 0b00111; + // |c o n d|0 1 0|0 x x 1 x|x x x x x x x x x x x x x x x|x|x x x x| + /* + 0x010 -> STRT + 0x011 -> LDRT + 0x110 -> STRBT + 0x111 -> LDRBT + */ + self.opcode = match op { + 0b010 => Opcode::STRT(add), + 0b011 => Opcode::LDRT(add), + 0b110 => Opcode::STRBT(add), + 0b111 => Opcode::LDRBT(add), + _ => { unreachable!(); } + }; + } else { + /* + xx0x0 not 0x010 -> STR (imm) + xx0x1 not 0x011 -> LDR (imm) + xx1x0 not 0x110 -> STRB (imm) + xx1x1 not 0x111 -> LDRB (imm) + */ + let pre = (op & 0b10000) != 0; + let wback = (op & 0b00010) != 0; + let op = op & 0b00101; + self.opcode = match op { + 0b000 => Opcode::STR(add, pre, wback), + 0b001 => { + if Rn == 0b1111 { + self.operands = Operands::RegImm(Rt, imm.into()); + self.opcode = Opcode::LDR(add, pre, wback); + return Some(()); + } + Opcode::LDR(add, pre, wback) + }, + 0b100 => Opcode::STRB(add, pre, wback), + 0b101 => { + if Rn == 0b1111 { + self.operands = Operands::RegImm(Rt, imm.into()); + self.opcode = Opcode::LDRB(add, pre, wback); + return Some(()); + } + Opcode::LDRB(add, pre, wback) + }, + _ => { unreachable!(); } + }; + } + self.operands = Operands::TwoRegImm(Rn, Rt, imm.into()); + }, + 0b011 => { + // page A5-192 to distinguish the following: + // check for media instructions, and if not, load/store word and unsigned byte + if (word & 0x00000010) != 0 { + // |c o n d|0 1 1|x x x x|x|x x x x|x x x x|x x x x x|x x|1|x x x x| + // using language from A5-206: A == 1 and B == 1 + // so this is media instructions (A5-207) + } else { + // |c o n d|0 1 1|x x x x|x|x x x x|x x x x|x x x x x|x x|0|x x x x| + // instructions here are A == 1, B == 0 in A5-206 + let op = ((word >> 20) & 0x1f) as u8; + + let add = (op & 0b01000) != 0; + /* + xx0x0 not 0x010 -> STR (register) + 0x010 -> STRT + xx0x1 not 0x011 -> LDR (register) + 0x011 -> LDRT + xx1x0 not 0x110 -> STRB (register) + 0x110 -> STRBT + xx1x1 not 0x111 -> LDRB (register) + 0x111 -> LDRBT + */ + let Rn = ((word >> 16) & 0x0f) as u8; + if (op & 0b10010) == 0b00010 { + let op = op & 0b00111; + // |c o n d|0 1 1|0 x x 1 x|x x x x x x x x x x x x x x x|0|x x x x| + /* + 0x010 -> STRT + 0x011 -> LDRT + 0x110 -> STRBT + 0x111 -> LDRBT + */ + self.opcode = match op { + 0b010 => Opcode::STRT(add), + 0b011 => Opcode::LDRT(add), + 0b110 => Opcode::STRBT(add), + 0b111 => Opcode::LDRBT(add), + _ => { unreachable!(); } + }; + } else { + /* + xx0x0 not 0x010 -> STR (imm) + xx0x1 not 0x011 -> LDR (imm) + xx1x0 not 0x110 -> STRB (imm) + xx1x1 not 0x111 -> LDRB (imm) + */ + let pre = (op & 0b10000) != 0; + let wback = (op & 0b00010) != 0; + let op = op & 0b00101; + self.opcode = match op { + 0b000 => Opcode::STR(add, pre, wback), + 0b001 => Opcode::LDR(add, pre, wback), + 0b100 => Opcode::STRB(add, pre, wback), + 0b101 => Opcode::LDRB(add, pre, wback), + _ => { unreachable!(); } + }; + } + let (Rt, Rm, shift) = { + let Rm = (word & 0xf) as u8; + let word = word >> 5; + let shift = (word & 0x7f) as u8; + let word = word >> 7; + let Rt = (word & 0xf) as u8; + (Rt, Rm, shift) + }; + self.operands = Operands::ThreeOperandWithShift(Rn, Rt, Rm, ShiftSpec::Immediate(shift)); + } + return Some(()); + }, + 0b100 | 0b101 => { + // branch, branch with link, and block data transfer + // page A5-212 + let op = (word >> 20) & 0x3f; + if op < 0b100000 { + let wback = (op & 0b000010) != 0; + let add = (op & 0b001000) != 0; + let pre = (op & 0b010000) != 0; + let usermode = (op & 0b000100) != 0; + self.opcode = if (op & 1) == 0 { + Opcode::STM(add, pre, wback, usermode) + } else { + Opcode::LDM(add, pre, wback, usermode) + }; + self.operands = Operands::RegRegList( + ((word >> 16) & 0xf) as u8, + (word & 0xffff) as u16 + ); + } else if op < 0b110000 { + // 10xxxx + // the + 1 is to compensate for an architecturally-defined initial offset + self.opcode = Opcode::B; + self.operands = Operands::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32); + } else { + // 11xxxx + // the + 1 is to compensate for an architecturally-defined initial offset + self.opcode = Opcode::BL; + self.operands = Operands::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32); + } + }, + 0b110 | 0b111 => { + // coprocessor instructions and supervisor call + // page A5-213 + self.opcode = Opcode::Incomplete(word); + return Some(()); + }, + _ => { unreachable!(); } + } + Some(()) + } +} + +pub struct ARMv7; +impl Arch for ARMv7 { + type Address = u32; + type Instruction = Instruction; + type Operand = Operands; +} + + +/* + * tests: (armv7?) + * [0x00, 0x00, 0x90, 0xe0] + * adds r0, r0, r0 + * + * [0x00, 0x00, 0x82, 0xe0] + * add r0, r2, r0 + * + * [0x00, 0x00, 0x88, 0xe0] + * add r0, r8, r0 + * + * [0x00, 0x01, 0x80, 0xe0] + * add r0, r0, r0, lsl 2 + * + * [0x00, 0x80, 0x00, 0x00] + * andeq r8, r0, r0 + * + * [0xc0, 0x80, 0x20, 0x00] + * eoreq r8, r0, r0, asr 1 + * + * [0x00, 0x00, 0xa2, 0xe1] + * mov r0, r0 + * + * [0x00, 0x00, 0xaf, 0xe1] + * mov r0, r0 + * + * [0x10, 0x00, 0xaf, 0xe1] + * invalid + * + * [0x01, 0x00, 0xaf, 0xe1] + * mov r0, r1 + * + * [0x00, 0x01, 0xaf, 0xe1] + * invalid + * + * [0x00, 0x00, 0xa0, 0xe1] + * mov r0, r0 + * + * [0x00, 0x01, 0xa0, 0xe1] + * lsl r0, r0, 2 + * # is this equivalent to mov r0, r0<<2? + * 0180afe1 invalid + * 018076e1 cmn r6, r1 + * 015096e1 orrs r5, r6, r1 + * 018086e1 orr r8, r6, r1 + * 0180a6e1 invalid + * 0180d6e1 bics r8, r6, r1 + * 0180d0e1 bics r8, r0, r1 + * 8110d0e1 bics r1, r0, r1, lsl 1 + * 1200dfe1 bics r0, pc, r2, lsl r0 + * f110d0e1 ldrsh r1, [r0, 1] + * 0101a0e1 lsl r0, r1, 2 + * 0110a0e1 mov r1, r1 + * 0111a0e1 lsl r1, r1, 2 + * 4110a0e1 asr r1, r1, 0x20 + * 2110a0e1 lsr r1, r1, 0x20 + * + */ diff --git a/test/test.rs b/test/test.rs new file mode 100644 index 0000000..56acbb0 --- /dev/null +++ b/test/test.rs @@ -0,0 +1,394 @@ +#![feature(test)] + +extern crate test; + +extern crate yaxpeax_arch; +extern crate yaxpeax_arm; + +use yaxpeax_arch::{Decodable, LengthedInstruction}; +use yaxpeax_arm::armv7::{ARMv7, Instruction, ConditionCode, Operands, Opcode, ShiftSpec}; + +fn test_decode(data: [u8; 4], expected: Instruction) { + let mut instr = Instruction::blank(); + instr.decode_into(&data); + assert!( + instr == expected, + "decode error for {:02x}{:02x}{:02x}{:02x}:\n decoded: {:?}\n expected: {:?}\n", + data[0], data[1], data[2], data[3], + instr, expected + ); +} + +fn test_display(data: [u8; 4], expected: &'static str) { + let mut instr = Instruction::blank(); + instr.decode_into(&data); + let text = format!("{}", instr); + assert!( + text == expected, + "display error for {:02x}{:02x}{:02x}{:02x}:\n decoded: {:?}\n displayed: {}\n expected: {}\n", + data[0], data[1], data[2], data[3], + instr, + text, expected + ); +} + +#[test] +fn test_decode_str_ldr() { + test_decode( + [0x24, 0xc0, 0x9f, 0xe5], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::LDR(true, true, false), + operands: Operands::RegImm(12, 0x24), + s: false + } + ); + test_decode( + [0x10, 0x00, 0x9f, 0xe5], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::LDR(true, true, false), + operands: Operands::RegImm(0, 0x10), + s: false + } + ); + test_decode( + [0x04, 0x20, 0x2d, 0xe5], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::STR(false, true, true), + operands: Operands::TwoRegImm(13, 2, 4), + s: false + } + ); + test_decode( + [0x04, 0x00, 0x2d, 0xe5], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::STR(false, true, true), + operands: Operands::TwoRegImm(13, 0, 4), + s: false + } + ); + test_decode( + [0x14, 0x30, 0x9f, 0xe5], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::LDR(true, true, false), + operands: Operands::RegImm(3, 0x14), + s: false + } + ); + test_decode( + [0x14, 0x20, 0x9f, 0xe5], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::LDR(true, true, false), + operands: Operands::RegImm(2, 0x14), + s: false + } + ); +} + +#[test] +fn test_decode_pop() { + test_decode( + [0x04, 0x10, 0x9d, 0xe4], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::LDR(true, false, false), + operands: Operands::TwoRegImm(13, 1, 4), + s: false + } + ); + test_display( + [0x04, 0x10, 0x9d, 0xe4], + "pop {r1}" + ); + test_decode( + [0xf0, 0x40, 0x2d, 0xe9], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::STM(false, true, true, false), + operands: Operands::RegRegList(13, 16624), + s: false + } + ); + test_display( + [0xf0, 0x40, 0x2d, 0xe9], + "push {r4, r5, r6, r7, lr}" + ); + test_decode( + [0xf0, 0x80, 0xbd, 0x18], + Instruction { + condition: ConditionCode::NE, + opcode: Opcode::LDM(true, false, true, false), + operands: Operands::RegRegList(13, 33008), + s: false + } + ); + test_display( + [0xf0, 0x80, 0xbd, 0x18], + "popne {r4, r5, r6, r7, pc}" + ); +} + +#[test] +fn test_decode_mov() { + test_decode( + [0x0d, 0x20, 0xa0, 0xe1], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::MOV, + operands: Operands::TwoOperand(2, 13), + s: false + } + ); + test_decode( + [0x00, 0xb0, 0xa0, 0xe3], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::MOV, + operands: Operands::RegImm(11, 0), + s: false + } + ); +} + +#[test] +fn test_decode_arithmetic() { + test_decode( + [0x03, 0x30, 0x8f, 0xe0], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::ADD, + operands: Operands::ThreeOperand(3, 15, 3), + s: false + } + ); + test_decode( + [0x03, 0x30, 0x66, 0xe0], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::RSB, + operands: Operands::ThreeOperand(3, 6, 3), + s: false + } + ); + test_decode( + [0x43, 0x31, 0xa0, 0xe1], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::MOV, + operands: Operands::ThreeOperandWithShift(3, 0, 3, ShiftSpec::Immediate(10)), + s: false + } + ); + test_decode( + [0x01, 0x50, 0x43, 0xe2], + Instruction { + condition: ConditionCode::AL, + opcode: Opcode::SUB, + operands: Operands::RegImm(3, 20481), + s: false + } + ); +} + +#[test] +fn test_decode_mul() { + test_decode( + [0x9c, 0x7d, 0x0b, 0x00], + Instruction { + condition: ConditionCode::EQ, + opcode: Opcode::MUL, + operands: Operands::MulThreeRegs(11, 12, 13), + s: false + } + ); + test_decode( + [0x90, 0x79, 0x09, 0x00], + Instruction { + condition: ConditionCode::EQ, + opcode: Opcode::MUL, + operands: Operands::MulThreeRegs(9, 0, 9), + s: false + } + ); + test_decode( + [0x94, 0x79, 0x09, 0x00], + Instruction { + condition: ConditionCode::EQ, + opcode: Opcode::MUL, + operands: Operands::MulThreeRegs(9, 4, 9), + s: false + } + ); +} + +static instruction_bytes: [u8; 4 * 60] = [ + 0x24, 0xc0, 0x9f, 0xe5, + 0x00, 0xb0, 0xa0, 0xe3, + 0x04, 0x10, 0x9d, 0xe4, + 0x0d, 0x20, 0xa0, 0xe1, + 0x04, 0x20, 0x2d, 0xe5, + 0x04, 0x00, 0x2d, 0xe5, + 0x10, 0x00, 0x9f, 0xe5, + 0x10, 0x30, 0x9f, 0xe5, + 0x04, 0xc0, 0x2d, 0xe5, + 0x4b, 0xfe, 0xff, 0xeb, + 0xd5, 0xfd, 0xff, 0xeb, + 0x90, 0x79, 0x09, 0x00, + 0x64, 0xd0, 0x01, 0x00, + 0x94, 0x79, 0x09, 0x00, + 0x14, 0x30, 0x9f, 0xe5, + 0x14, 0x20, 0x9f, 0xe5, + 0x03, 0x30, 0x8f, 0xe0, + 0x02, 0x10, 0x93, 0xe7, + 0x00, 0x00, 0x51, 0xe3, + 0x0e, 0xf0, 0xa0, 0x01, + 0x01, 0xfe, 0xff, 0xea, + 0x58, 0x75, 0x09, 0x00, + 0xec, 0x02, 0x00, 0x00, + 0xf0, 0x40, 0x2d, 0xe9, + 0x54, 0x70, 0x9f, 0xe5, + 0x00, 0x30, 0xd7, 0xe5, + 0x00, 0x00, 0x53, 0xe3, + 0xf0, 0x80, 0xbd, 0x18, + 0x48, 0x60, 0x9f, 0xe5, + 0x48, 0x30, 0x9f, 0xe5, + 0x48, 0x40, 0x9f, 0xe5, + 0x03, 0x30, 0x66, 0xe0, + 0x43, 0x31, 0xa0, 0xe1, + 0x00, 0x20, 0x94, 0xe5, + 0x01, 0x50, 0x43, 0xe2, + 0x05, 0x00, 0x52, 0xe1, + 0x06, 0x00, 0x00, 0x2a, + 0x01, 0x30, 0x82, 0xe2, + 0x00, 0x30, 0x84, 0xe5, + 0x0f, 0xe0, 0xa0, 0xe1, + 0x03, 0xf1, 0x96, 0xe7, + 0x00, 0x20, 0x94, 0xe5, + 0x05, 0x00, 0x52, 0xe1, + 0xf8, 0xff, 0xff, 0x3a, + 0x01, 0x30, 0xa0, 0xe3, + 0x00, 0x30, 0xc7, 0xe5, + 0xf0, 0x80, 0xbd, 0xe8, + 0x9c, 0x7d, 0x0b, 0x00, + 0xa0, 0x33, 0x0b, 0x00, + 0xa4, 0x33, 0x0b, 0x00, + 0xa0, 0x7d, 0x0b, 0x00, + 0x04, 0xe0, 0x2d, 0xe5, + 0x04, 0xf0, 0x9d, 0xe4, + 0x24, 0x00, 0x9f, 0xe5, + 0x00, 0x30, 0x90, 0xe5, + 0x00, 0x00, 0x53, 0xe3, + 0x04, 0xe0, 0x2d, 0xe5, + 0x04, 0xf0, 0x9d, 0x04, + 0x14, 0x30, 0x9f, 0xe5, + 0x00, 0x00, 0x53, 0xe3 + ]; + + +#[test] +fn test_decode_span() { + let mut i = 0u32; + while i < instruction_bytes.len() as u32 { + let mut instr = Instruction::blank(); + instr.decode_into(&instruction_bytes[(i as usize)..]); + println!( + "Decoded {:02x}{:02x}{:02x}{:02x}: {}", //{:?}\n {}", + instruction_bytes[i as usize], + instruction_bytes[i as usize + 1], + instruction_bytes[i as usize + 2], + instruction_bytes[i as usize + 3], +// instr, + instr); + i += instr.len(); + } + panic!("done"); +} +/* + * from debian 5.0.10 bash 3.2-4_arm + * 0x0001bee4 24c09fe5 ldr ip, sym.__libc_csu_fini + * 0x0001bee8 00b0a0e3 mov fp, 0 + * 0x0001beec 04109de4 pop {r1} + * 0x0001bef0 0d20a0e1 mov r2, sp + * 0x0001bef4 04202de5 str r2, [sp, -4]! + * 0x0001bef8 04002de5 str r0, [sp, -4]! + * 0x0001befc 10009fe5 ldr r0, sym.main + * 0x0001bf00 10309fe5 ldr r3, sym.__libc_csu_init + * 0x0001bf04 04c02de5 str ip, [sp, -4]! + * 0x0001bf08 4bfeffeb bl sym.imp.__libc_start_main + * 0x0001bf0c d5fdffeb bl sym.imp.abort + * 0x0001bf10 90790900 muleq sb, r0, sb + * 0x0001bf14 64d00100 andeq sp, r1, r4, rrx + * 0x0001bf18 94790900 muleq sb, r4, sb + * 0x0001bf1c 14309fe5 ldr r3, [0x0001bf38] + * 0x0001bf20 14209fe5 ldr r2, [0x0001bf3c] + * 0x0001bf24 03308fe0 add r3, pc, r3 + * 0x0001bf28 021093e7 ldr r1, [r3, r2] + * 0x0001bf2c 000051e3 cmp r1, 0 + * 0x0001bf30 0ef0a001 moveq pc, lr + * 0x0001bf34 01feffea b loc.imp.__gmon_start__ + * 0x0001bf38 58750900 andeq r7, sb, r8, asr r5 + * 0x0001bf3c ec020000 andeq r0, r0, ip, ror 5 + * 0x0001bf40 f0402de9 push {r4, r5, r6, r7, lr} + * 0x0001bf44 54709fe5 ldr r7, [0x0001bfa0] + * 0x0001bf48 0030d7e5 ldrb r3, [r7] + * 0x0001bf4c 000053e3 cmp r3, 0 + * 0x0001bf50 f080bd18 popne {r4, r5, r6, r7, pc} + * 0x0001bf54 48609fe5 ldr r6, [0x0001bfa4] + * 0x0001bf58 48309fe5 ldr r3, [0x0001bfa8] + * 0x0001bf5c 48409fe5 ldr r4, [0x0001bfac] + * 0x0001bf60 033066e0 rsb r3, r6, r3 + * 0x0001bf64 4331a0e1 asr r3, r3, 2 + * 0x0001bf68 002094e5 ldr r2, [r4] + * 0x0001bf6c 015043e2 sub r5, r3, 1 + * 0x0001bf70 050052e1 cmp r2, r5 + * 0x0001bf74 0600002a bhs 0x1bf94 + * 0x0001bf78 013082e2 add r3, r2, 1 + * 0x0001bf7c 003084e5 str r3, [r4] + * 0x0001bf80 0fe0a0e1 mov lr, pc + * 0x0001bf84 03f196e7 ldr pc, [r6, r3, lsl 2] + * 0x0001bf88 002094e5 ldr r2, [r4] + * 0x0001bf8c 050052e1 cmp r2, r5 + * 0x0001bf90 f8ffff3a blo 0x1bf78 + * 0x0001bf94 0130a0e3 mov r3, 1 + * 0x0001bf98 0030c7e5 strb r3, [r7] + * 0x0001bf9c f080bde8 pop {r4, r5, r6, r7, pc} + * 0x0001bfa0 9c7d0b00 muleq fp, ip, sp + * 0x0001bfa4 a0330b00 andeq r3, fp, r0, lsr 7 + * 0x0001bfa8 a4330b00 andeq r3, fp, r4, lsr 7 + * 0x0001bfac a07d0b00 andeq r7, fp, r0, lsr 27 + * 0x0001bfb0 04e02de5 str lr, [sp, -4]! + * 0x0001bfb4 04f09de4 pop {pc} + * 0x0001bfb8 24009fe5 ldr r0, [0x0001bfe4] + * 0x0001bfbc 003090e5 ldr r3, [r0] + * 0x0001bfc0 000053e3 cmp r3, 0 + * 0x0001bfc4 04e02de5 str lr, [sp, -4]! + * 0x0001bfc8 04f09d04 popeq {pc} + * 0x0001bfcc 14309fe5 ldr r3, [0x0001bfe8] + * 0x0001bfd0 000053e3 cmp r3, 0 + */ + +use test::Bencher; +#[bench] +pub fn bench_60000_instrs(b: &mut Bencher) { + b.iter(|| { + for i in (0..1000) { + let mut iter = instruction_bytes.iter(); + let mut result = Instruction::blank(); + loop { + match result.decode_into(&mut iter) { + Some(result) => { + test::black_box(&result); + }, + None => { + break; + } + } + } + } + }); +} -- cgit v1.1