From 72109c2385c7b0940072f31fc3bcbeed68006060 Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 6 Dec 2020 11:37:19 -0800 Subject: fix more incomplete cases, report arm instruction length properly --- src/armv7.rs | 27 +++++++++++++++-- src/armv7/thumb.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 102 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/armv7.rs b/src/armv7.rs index 91b65bd..73530fb 100644 --- a/src/armv7.rs +++ b/src/armv7.rs @@ -1313,8 +1313,14 @@ pub struct Instruction { pub condition: ConditionCode, pub opcode: Opcode, pub operands: [Operand; 4], + /// does this instruction update flags, while variants that do not update flags exist? pub s: bool, + /// is this a 32-bit thumb instruction? + pub wide: bool, + /// and if it is a 32-bit thumb instruction, should the .w suffix be shown? pub thumb_w: bool, + /// and generally speaking, was this just a thumb-encoded instruction? + pub thumb: bool, } #[derive(Debug, PartialEq)] @@ -1363,6 +1369,8 @@ impl Default for Instruction { operands: [Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing], s: false, thumb_w: false, + wide: false, + thumb: false, } } } @@ -1376,6 +1384,14 @@ impl Instruction { self.thumb_w = value; } pub fn w(&self) -> bool { self.thumb_w } + pub(crate) fn set_wide(&mut self, value: bool) { + self.wide = value; + } + pub fn wide(&self) -> bool { self.wide } + pub(crate) fn set_thumb(&mut self, value: bool) { + self.thumb = value; + } + pub fn thumb(&self) -> bool { self.thumb } } fn format_reg_list>(f: &mut T, mut list: u16, colors: &Y) -> Result<(), fmt::Error> { @@ -1485,10 +1501,15 @@ impl Display for Instruction { impl LengthedInstruction for Instruction { type Unit = AddressDiff<::Address>; fn min_size() -> Self::Unit { + // TODO: this is contingent on the decoder mode... AddressDiff::from_const(4) } fn len(&self) -> Self::Unit { - AddressDiff::from_const(4) + if self.thumb && !self.wide { + AddressDiff::from_const(2) + } else { + AddressDiff::from_const(4) + } } } @@ -1777,10 +1798,12 @@ impl Decoder for InstDecoder { type Error = DecodeError; fn decode_into>(&self, inst: &mut Instruction, bytes: T) -> Result<(), Self::Error> { + inst.set_w(false); + inst.set_wide(false); if self.thumb { return thumb::decode_into(&self, inst, bytes); } else { - inst.set_w(false); + inst.set_thumb(false); } fn read_word>(bytes: T) -> Result { diff --git a/src/armv7/thumb.rs b/src/armv7/thumb.rs index dc2b0e9..6028e7b 100644 --- a/src/armv7/thumb.rs +++ b/src/armv7/thumb.rs @@ -99,7 +99,12 @@ fn DecodeImmShift(reg: u8, ty: u8, imm5: u8) -> RegShift { #[allow(non_snake_case)] pub fn decode_into>(decoder: &InstDecoder, inst: &mut Instruction, bytes: T) -> Result<(), DecodeError> { - inst.set_w(false); + // these are cleared in `armv7::InstDecoder::decode_into`. + // they must be reset when switching out of thumb decoding or decoding a new thumb instruction, + // which that `decode_into` is the entrypoint for in all cases. + // inst.set_w(false); + // inst.set_wide(false); + inst.set_thumb(true); let mut iter = bytes.into_iter(); let instr: u16 = ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u16) ) | @@ -113,6 +118,7 @@ pub fn decode_into>(decoder: &InstDecoder, inst: &mut I // `A6.1 Thumb instruction set encoding` if opword >= 0b11101 { inst.set_w(true); + inst.set_wide(true); // 32b instruction - `A6-228, 32-bit Thumb instruction encoding` // opword low bits 01, 10, and 11 correspond to `op1` in table `A6-9` @@ -709,7 +715,7 @@ pub fn decode_into>(decoder: &InstDecoder, inst: &mut I let rm = lower2[0..4].load::(); let shift = RegShift::from_raw( - 0b00000 | // reg-imm shift. TODO: probably need to change the const + 0b00000 | // reg-imm shift rm as u16 | (imm2 << 7) | (imm3 << 9) | tp << 5 @@ -760,31 +766,92 @@ pub fn decode_into>(decoder: &InstDecoder, inst: &mut I let tp = (lower >> 4) & 0b11; let imm2 = (lower >> 6) & 0b11; let imm3 = (lower >> 12) & 0b111; + let imm5 = (imm3 << 2) | imm2; match tp { 0b00 => { - if imm2 | imm3 == 0 { + if imm5 == 0 { // `MOV (register, Thumb)` (`A8-487`) - return Err(DecodeError::Incomplete); + // encoding T3 + inst.set_w(true); + let rm = lower2[..4].load::(); + let rd = lower2[8..12].load::(); + inst.opcode = Opcode::MOV; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; } else { // `LSL (immediate)` (`A8-469`) - return Err(DecodeError::Incomplete); + // encoding T2 + inst.set_w(true); + let rm = lower2[..4].load::(); + let rd = lower2[8..12].load::(); + inst.opcode = Opcode::LSL; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm12(imm5), + Operand::Nothing, + ]; } }, 0b01 => { // `LSR (immediate)` (`A8-473`) - return Err(DecodeError::Incomplete); + // encoding T2 + inst.set_w(true); + let rm = lower2[..4].load::(); + let rd = lower2[8..12].load::(); + inst.opcode = Opcode::LSR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm12(imm5), + Operand::Nothing, + ]; } 0b10 => { // `ASR (immediate)` (`A8-328`) - return Err(DecodeError::Incomplete); + // encoding T2 + inst.set_w(true); + let rm = lower2[..4].load::(); + let rd = lower2[8..12].load::(); + inst.opcode = Opcode::ASR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm12(imm5), + Operand::Nothing, + ]; } 0b11 => { - if imm2 | imm3 == 0 { + if imm5 == 0 { // `RRX` (`A8-573`) - return Err(DecodeError::Incomplete); + // encoding T1 + inst.set_w(false); + let rm = lower2[..4].load::(); + let rd = lower2[8..12].load::(); + inst.opcode = Opcode::RRX; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; } else { // `ROR (immediate)` (`A8-569`) - return Err(DecodeError::Incomplete); + // encoding T1 + inst.set_w(false); + let rm = lower2[..4].load::(); + let rd = lower2[8..12].load::(); + inst.opcode = Opcode::ASR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm12(imm5), + Operand::Nothing, + ]; } } _ => { -- cgit v1.1