From 436c65e51f7e14201f902c57fb6a069860126477 Mon Sep 17 00:00:00 2001 From: iximeow Date: Fri, 13 Dec 2019 14:39:47 -0800 Subject: vex --- src/lib.rs | 499 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 461 insertions(+), 38 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index 24f11f8..bfb01de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ extern crate serde; extern crate yaxpeax_arch; extern crate termion; +mod vex; mod display; use std::hint::unreachable_unchecked; @@ -362,8 +363,8 @@ impl Operand { fn operand_size() { assert_eq!(std::mem::size_of::(), 1); assert_eq!(std::mem::size_of::(), 2); - assert_eq!(std::mem::size_of::(), 4); - assert_eq!(std::mem::size_of::(), 40); + // assert_eq!(std::mem::size_of::(), 4); + // assert_eq!(std::mem::size_of::(), 40); } #[allow(non_camel_case_types)] @@ -374,6 +375,7 @@ pub enum RegisterBank { CR, DR, S, EIP, RIP, EFlags, RFlags, // Control reg, Debug reg, Selector, ... X, Y, Z, // XMM, YMM, ZMM ST, MM, // ST, MM regs (x87, mmx) + K, // AVX512 mask registers } #[allow(non_camel_case_types)] #[cfg(not(feature="use-serde"))] @@ -383,6 +385,7 @@ pub enum RegisterBank { CR, DR, S, EIP, RIP, EFlags, RFlags, // Control reg, Debug reg, Selector, ... X, Y, Z, // XMM, YMM, ZMM ST, MM, // ST, MM regs (x87, mmx) + K, // AVX512 mask registers } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] @@ -390,6 +393,15 @@ pub enum Segment { DS = 0, CS, ES, FS, GS, SS } +const BMI1: [Opcode; 6] = [ + Opcode::ANDN, + Opcode::BEXTR, + Opcode::BLSI, + Opcode::BLSMSK, + Opcode::BLSR, + Opcode::TZCNT, +]; + #[allow(non_camel_case_types)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Opcode { @@ -409,6 +421,7 @@ pub enum Opcode { BTR, BSF, BSR, + TZCNT, MOVSS, ADDSS, SUBSS, @@ -617,6 +630,12 @@ pub enum Opcode { RSQRTSS, RCPSS, + ANDN, + BEXTR, + BLSI, + BLSMSK, + BLSR, + ADDPS, ANDNPS, ANDPS, @@ -726,6 +745,280 @@ pub enum Opcode { VMREAD, VMWRITE, XORPS, + + VMOVDDUP, + VPSHUFLW, + VHADDPS, + VHSUBPS, + VADDSUBPS, + VCVTPD2DQ, + VLDDQU, + + VADDPD, + VADDPS, + VADDSUBPD, + VAESDEC, + VAESDECLAST, + VAESENC, + VAESENCLAST, + VAESIMC, + VAESKEYGENASSIST, + VBLENDPD, + VBLENDPS, + VBLENDVPD, + VBLENDVPS, + VBROADCASTF128, + VBROADCASTI128, + VBROADCASTSD, + VBROADCASTSS, + VCMPPD, + VCMPPS, + VCVTDQ2PD, + VCVTDQ2PS, + VCVTPD2PS, + VCVTPH2PS, + VCVTPS2DQ, + VCVTPS2PD, + VCVTPS2PH, + VCVTTPD2DQ, + VCVTTPS2DQ, + VDIVPD, + VDIVPS, + VDPPD, + VDPPS, + VEXTRACTF128, + VEXTRACTI128, + VEXTRACTPS, + VFMADD132PD, + VFMADD132PS, + VFMADD213PD, + VFMADD213PS, + VFMADD231PD, + VFMADD231PS, + VFMADDSUB132PD, + VFMADDSUB132PS, + VFMADDSUB213PD, + VFMADDSUB213PS, + VFMADDSUB231PD, + VFMADDSUB231PS, + VFMSUB132PD, + VFMSUB132PS, + VFMSUB213PD, + VFMSUB213PS, + VFMSUB231PD, + VFMSUB231PS, + VFMSUBADD132PD, + VFMSUBADD132PS, + VFMSUBADD213PD, + VFMSUBADD213PS, + VFMSUBADD231PD, + VFMSUBADD231PS, + VFNMADD132PD, + VFNMADD132PS, + VFNMADD213PD, + VFNMADD213PS, + VFNMADD231PD, + VFNMADD231PS, + VFNMSUB132PD, + VFNMSUB132PS, + VFNMSUB213PD, + VFNMSUB213PS, + VFNMSUB231PD, + VFNMSUB231PS, + VGATHERDPD, + VGATHERDPS, + VGATHERQPD, + VGATHERQPS, + VHADDPD, + VHSUBPD, + VINSERTF128, + VINSERTI128, + VINSERTPS, + VMASKMOVDQU, + VMASKMOVPD, + VMASKMOVPS, + VMAXPD, + VMAXPS, + VMINPD, + VMINPS, + VMOVAPD, + VMOVAPS, + VMOVD, + VMOVDQA, + VMOVDQU, + VMOVHLPS, + VMOVHPD, + VMOVHPS, + VMOVLHPS, + VMOVLPD, + VMOVLPS, + VMOVMSKPD, + VMOVMSKPS, + VMOVNTDQ, + VMOVNTDQA, + VMOVNTPD, + VMOVNTPS, + VMOVQ, + VMOVSHDUP, + VMOVSLDUP, + VMOVUPD, + VMOVUPS, + VMPSADBW, + VMULPD, + VMULPS, + VPABSB, + VPABSD, + VPABSW, + VPACKSSDW, + VPACKSSWB, + VPACKUSWB, + VPADDB, + VPADDD, + VPADDQ, + VPADDSB, + VPADDSW, + VPADDUSB, + VPADDUSW, + VPADDW, + VPALIGNR, + VPAND, + VPANDN, + VPAVGB, + VPAVGW, + VPBLENDD, + VPBLENDVB, + VPBLENDW, + VPBROADCASTB, + VPBROADCASTD, + VPBROADCASTQ, + VPBROADCASTW, + VPCLMULQDQ, + VPCMPEQB, + VPCMPEQD, + VPCMPEQQ, + VPCMPEQW, + VPCMPGTB, + VPCMPGTD, + VPCMPGTQ, + VPCMPGTW, + VPCMPISTRI, + VPCMPISTRM, + VPERM2F128, + VPERM2I128, + VPERMD, + VPERMILPD, + VPERMILPS, + VPERMPD, + VPERMPS, + VPERMQ, + VPEXTRB, + VPEXTRD, + VPEXTRQ, + VPEXTRW, + VPGATHERDD, + VPGATHERDQ, + VPGATHERQD, + VPGATHERQQ, + VPHADDD, + VPHADDSW, + VPHADDW, + VPHMINPOSUW, + VPHSUBD, + VPHSUBSW, + VPHSUBW, + VPINSRB, + VPINSRD, + VPINSRQ, + VPINSRW, + VPMADDUBSW, + VPMADDWD, + VPMASKMOVD, + VPMASKMOVQ, + VPMAXSB, + VPMAXSD, + VPMAXSW, + VPMAXUD, + VPMINSD, + VPMINUD, + VPMOVMSKB, + VPMOVSXBD, + VPMOVSXBQ, + VPMOVSXBW, + VPMOVSXDQ, + VPMOVSXWD, + VPMOVSXWQ, + VPMOVZXBD, + VPMOVZXBQ, + VPMOVZXBW, + VPMOVZXDQ, + VPMOVZXWD, + VPMOVZXWQ, + VPMULDQ, + VPMULHRSW, + VPMULHUW, + VPMULHW, + VPMULLD, + VPMULLW, + VPMULUDQ, + VPOR, + VPSADBW, + VPSHUFB, + VPSHUFD, + VPSIGNB, + VPSIGND, + VPSIGNW, + VPSLLD, + VPSLLDQ, + VPSLLQ, + VPSLLVD, + VPSLLVQ, + VPSLLW, + VPSRAD, + VPSRAVD, + VPSRAW, + VPSRLD, + VPSRLDQ, + VPSRLQ, + VPSRLVD, + VPSRLVQ, + VPSRLW, + VPSUBB, + VPSUBD, + VPSUBQ, + VPSUBSB, + VPSUBSW, + VPSUBUSB, + VPSUBUSW, + VPSUBW, + VPTEST, + VPUNPCKHBW, + VPUNPCKHDQ, + VPUNPCKHQDQ, + VPUNPCKHWD, + VPUNPCKLBW, + VPUNPCKLDQ, + VPUNPCKLQDQ, + VPUNPCKLWD, + VPXOR, + VRCPPS, + VROUNDPD, + VROUNDPS, + VRSQRTPS, + VSHUFPD, + VSHUFPS, + VSQRTPD, + VSQRTPS, + VSUBPD, + VSUBPS, + VTESTPD, + VTESTPS, + VUNPCKHPD, + VUNPCKHPS, + VUNPCKLPD, + VUNPCKLPS, + VXORPD, + VXORPS, + VZEROUPPER, } #[derive(Debug)] pub struct Instruction { @@ -733,6 +1026,7 @@ pub struct Instruction { modrm_rrr: RegSpec, modrm_mmm: RegSpec, // doubles as sib_base sib_index: RegSpec, + vex_reg: RegSpec, scale: u8, length: u8, operand_count: u8, @@ -807,7 +1101,63 @@ impl LengthedInstruction for Instruction { } } +#[derive(PartialEq)] pub struct InstDecoder { + // extensions tracked here: + // 0. SSE3 + // 1. SSSE3 + // 2. monitor (intel-only?) + // 3. vmx (some atom chips still lack it) + // 4. fma3 (intel haswell/broadwell+, amd piledriver+) + // 5. cmpxchg16b (some amd are missingt this one) + // 6. sse4.1 + // 7. sse4.2 + // 8. movbe + // 9. popcnt (independent of BMI) + // 10. aesni + // 11. xsave (xsave, xrestor, xsetbv, xgetbv) + // 12. rdrand (intel ivybridge+, amd ..??) + // 13. sgx (eadd, eblock, ecreate, edbgrd, edbgwr, einit, eldb, eldu, epa, eremove, etrace, + // ewb, eenter, eexit, egetkey, ereport, eresume) + // 14. bmi1 (intel haswell+, amd jaguar+) + // 15. avx2 (intel haswell+, amd excavator+) + // 16. bmi2 (intel ?, amd ?) + // 17. invpcid + // 18. mpx + // 19. avx512_f + // 20. avx512_dq + // 21. rdseed + // 22. adx + // 23. avx512_fma + // 24. pcommit + // 25. clflushopt + // 26. clwb + // 27. avx512_pf + // 28. avx512_er + // 29. avx512_cd + // 30. sha + // 31. avx512_bw + // 32. avx512_vl + // 33. prefetchwt1 + // 34. avx512_vbmi + // 35. avx512_vbmi2 + // 36. gfni (galois field instructions) + // 37. vaes + // 38. vpclmulqdq + // 39. avx_vnni + // 40. avx512_bitalg + // 41. avx512_vpopcntdq + // 42. avx512_4vnniw + // 43. avx512_4fmaps + // 44. cx8 // cmpxchg8 - is this actually optional in x86_64? + // 45. syscall // syscall/sysret - actually optional in x86_64? + // 46. rdtscp // actually optional in x86_64? + // 47. abm (lzcnt, popcnt) + // 48. sse4a + // 49. 3dnowprefetch // actually optional? + // 50. xop + // 51. skinit + // 52. tbm flags: u64, } @@ -816,11 +1166,34 @@ impl InstDecoder { /// /// Pedantic and only decodes what the spec says is well-defined, rejecting undefined sequences /// and any instructions defined by extensions. - fn minimal() -> Self { + pub fn minimal() -> Self { InstDecoder { flags: 0, } } + + fn bmi1(&self) -> bool { + self.flags & (1 << 13) != 0 + } + + pub fn without_bmi1(mut self) -> Self { + self.flags &= (!(1 << 13)); + self + } + + fn revise_instruction(&self, inst: &mut Instruction) -> Result<(), ()> { + match inst.opcode { + Opcode::TZCNT => { + if !self.bmi1() { + // tzcnt is only supported if bmi1 is enabled. without bmi1, this decodes as + // bsf. + inst.opcode = Opcode::BSF; + } + } + _ => {} + } + Ok(()) + } } impl Default for InstDecoder { @@ -838,13 +1211,13 @@ impl Default for InstDecoder { impl Decoder for InstDecoder { fn decode>(&self, bytes: T) -> Option { let mut instr = Instruction::invalid(); - match decode_one(bytes, &mut instr) { + match decode_one(self, bytes, &mut instr) { Some(_) => Some(instr), None => None } } fn decode_into>(&self, instr: &mut Instruction, bytes: T) -> Option<()> { - decode_one(bytes, instr) + decode_one(self, bytes, instr) } } @@ -938,6 +1311,7 @@ impl Instruction { modrm_rrr: RegSpec::rax(), modrm_mmm: RegSpec::rax(), // doubles as sib_base sib_index: RegSpec::rax(), + vex_reg: RegSpec::rax(), scale: 0, length: 0, disp: 0, @@ -1002,16 +1376,26 @@ impl Instruction { #[derive(Debug, Copy, Clone)] pub struct Prefixes { bits: u8, - rep_prefix: RepPrefix, rex: PrefixRex, segment: Segment, } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum RepPrefix { - None = 0, - E = 1, - NE = 2, +#[derive(Debug, Copy, Clone)] +pub struct PrefixVex { + bits: u8, +} + +impl PrefixVex { + #[inline] + fn b(&self) -> bool { (self.bits & 0x01) == 0x01 } + #[inline] + fn x(&self) -> bool { (self.bits & 0x02) == 0x02 } + #[inline] + fn r(&self) -> bool { (self.bits & 0x04) == 0x04 } + #[inline] + fn w(&self) -> bool { (self.bits & 0x08) == 0x08 } + #[inline] + fn l(&self) -> bool { (self.bits & 0x10) == 0x10 } } #[derive(Debug, Copy, Clone)] @@ -1024,7 +1408,6 @@ impl Prefixes { fn new(bits: u8) -> Prefixes { Prefixes { bits: bits, - rep_prefix: RepPrefix::None, rex: PrefixRex { bits: 0 }, segment: Segment::DS, } @@ -1034,11 +1417,11 @@ impl Prefixes { #[inline] fn set_rep(&mut self) { self.bits = (self.bits & 0xcf) | 0x10 } #[inline] - fn repz(&self) -> bool { self.bits & 0x30 == 0x20 } + pub fn repz(&self) -> bool { self.bits & 0x30 == 0x20 } #[inline] fn set_repz(&mut self) { self.bits = (self.bits & 0xcf) | 0x20 } #[inline] - fn repnz(&self) -> bool { self.bits & 0x30 == 0x30 } + pub fn repnz(&self) -> bool { self.bits & 0x30 == 0x30 } #[inline] fn set_repnz(&mut self) { self.bits = (self.bits & 0xcf) | 0x30 } #[inline] @@ -1050,14 +1433,6 @@ impl Prefixes { #[inline] fn set_address_size(&mut self) { self.bits = self.bits | 0x2 } #[inline] - pub fn repne(&self) -> bool { self.rep_prefix == RepPrefix::NE } - #[inline] - fn set_repne(&mut self) { self.rep_prefix = RepPrefix::NE; } - #[inline] - pub fn repe(&self) -> bool { self.rep_prefix == RepPrefix::E } - #[inline] - fn set_repe(&mut self) { self.rep_prefix = RepPrefix::E; } - #[inline] pub fn set_lock(&mut self) { self.bits |= 0x4 } #[inline] pub fn lock(&self) -> bool { self.bits & 0x4 == 4 } @@ -1088,14 +1463,37 @@ impl Prefixes { #[inline] fn rex(&self) -> &PrefixRex { &self.rex } #[inline] - fn rex_mut(&mut self) -> &mut PrefixRex { &mut self.rex } + fn vex(&self) -> PrefixVex { PrefixVex { bits: self.rex.bits } } + + #[inline] + fn rex_from(&mut self, bits: u8) { + self.rex.bits = bits; + } + + #[inline] + fn vex_from_c5(&mut self, bits: u8) { + // collect rex bits + let r = bits & 0x80; + let wrxb = (r >> 5) ^ 0x04; + let l = (bits & 0x04) << 2; + let synthetic_rex = wrxb | l | 0x80; + self.rex.from(synthetic_rex); + } + + #[inline] + fn vex_from_c4(&mut self, high: u8, low: u8) { + let w = low & 0x80; + let rxb = (high >> 5) ^ 0x07; + let wrxb = rxb | w >> 4; + let l = (low & 0x04) << 2; + let synthetic_rex = wrxb | l | 0x80; + self.rex.from(synthetic_rex); + } } impl PrefixRex { #[inline] - fn present(&self) -> bool { (self.bits & 0x10) == 0x10 } - #[inline] - fn set_present(&mut self) { self.bits |= 0x10; } + fn present(&self) -> bool { (self.bits & 0xc0) == 0x40 } #[inline] fn b(&self) -> bool { (self.bits & 0x01) == 0x01 } #[inline] @@ -1106,8 +1504,7 @@ impl PrefixRex { fn w(&self) -> bool { (self.bits & 0x08) == 0x08 } #[inline] fn from(&mut self, prefix: u8) { - self.bits = prefix & 0x0f; - self.set_present(); + self.bits = prefix; } } @@ -1138,6 +1535,10 @@ pub enum OperandCode { AX_Xv, DX_AX, E_G_xmm, + E_G_ymm, + E_G_zmm, + G_E_ymm, + G_E_zmm, Ev_Ivs, Ew_Sw, Fw, @@ -2371,7 +2772,7 @@ const OPCODE_0F_MAP: [OpcodeRecord; 256] = [ OpcodeRecord(Interpretation::Instruction(Opcode::UD2E), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0x0fba), OpcodeRecord(Interpretation::Instruction(Opcode::BTC), OperandCode::Gv_Ev), - OpcodeRecord(Interpretation::Instruction(Opcode::BSF), OperandCode::Gv_Ev), + OpcodeRecord(Interpretation::Instruction(Opcode::TZCNT), OperandCode::Gv_Ev), OpcodeRecord(Interpretation::Instruction(Opcode::BSR), OperandCode::Gv_Ev), OpcodeRecord(Interpretation::Instruction(Opcode::MOVSX_b), OperandCode::Gv_Eb), OpcodeRecord(Interpretation::Instruction(Opcode::MOVSX_w), OperandCode::Gv_Ew), @@ -2675,8 +3076,8 @@ const OPCODES: [OpcodeRecord; 256] = [ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xc1_Ev_Ib), OpcodeRecord(Interpretation::Instruction(Opcode::RETURN), OperandCode::Iw), OpcodeRecord(Interpretation::Instruction(Opcode::RETURN), OperandCode::Nothing), - OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), - OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), + OpcodeRecord(Interpretation::Prefix, OperandCode::Nothing), + OpcodeRecord(Interpretation::Prefix, OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::MOV), OperandCode::ModRM_0xc6_Eb_Ib), OpcodeRecord(Interpretation::Instruction(Opcode::MOV), OperandCode::ModRM_0xc7_Ev_Iv), OpcodeRecord(Interpretation::Instruction(Opcode::ENTER), OperandCode::Iw_Ib), @@ -2767,7 +3168,7 @@ const OPCODES: [OpcodeRecord; 256] = [ ]; #[allow(non_snake_case)] -fn read_E>(bytes_iter: &mut T, instr: &mut Instruction, modrm: u8, width: u8, length: &mut u8) -> Result { +pub(crate) fn read_E>(bytes_iter: &mut T, instr: &mut Instruction, modrm: u8, width: u8, length: &mut u8) -> Result { let bank = width_to_gp_reg_bank(width, instr.prefixes.rex().present()); if modrm >= 0b11000000 { read_modrm_reg(instr, modrm, bank) @@ -2776,13 +3177,21 @@ fn read_E>(bytes_iter: &mut T, instr: &mut Instruction, mod } } #[allow(non_snake_case)] -fn read_E_xmm>(bytes_iter: &mut T, instr: &mut Instruction, modrm: u8, length: &mut u8) -> Result { +pub(crate) fn read_E_xmm>(bytes_iter: &mut T, instr: &mut Instruction, modrm: u8, length: &mut u8) -> Result { if modrm >= 0b11000000 { read_modrm_reg(instr, modrm, RegisterBank::X) } else { read_M(bytes_iter, instr, modrm, length) } } +#[allow(non_snake_case)] +pub(crate) fn read_E_ymm>(bytes_iter: &mut T, instr: &mut Instruction, modrm: u8, length: &mut u8) -> Result { + if modrm >= 0b11000000 { + read_modrm_reg(instr, modrm, RegisterBank::Y) + } else { + read_M(bytes_iter, instr, modrm, length) + } +} #[allow(non_snake_case)] fn read_modrm_reg(instr: &mut Instruction, modrm: u8, reg_bank: RegisterBank) -> Result { @@ -2942,7 +3351,7 @@ fn width_to_gp_reg_bank(width: u8, rex: bool) -> RegisterBank { } } -pub fn read_instr>(mut bytes_iter: T, instruction: &mut Instruction) -> Result<(), ()> { +pub fn read_instr>(decoder: &InstDecoder, mut bytes_iter: T, instruction: &mut Instruction) -> Result<(), ()> { let mut length = 0u8; let mut alternate_opcode_map: Option = None; // use std::intrinsics::unlikely; @@ -2971,7 +3380,7 @@ pub fn read_instr>(mut bytes_iter: T, instruction: &mut Ins length += 1; let record = OPCODES[b as usize]; if (b & 0xf0) == 0x40 { - prefixes.rex_mut().from(b); + prefixes.rex_from(b); } else if b == 0x0f { let record = match alternate_opcode_map { Some(opcode_map) => { @@ -3010,7 +3419,7 @@ pub fn read_instr>(mut bytes_iter: T, instruction: &mut Ins // prefix, but since 660f21 is not valid, the opcode is interpreted // as 0f21, where 66 is a prefix, which makes 41 not the last // prefix before the opcode, and it's discarded. - prefixes.rex_mut().from(0); + prefixes.rex_from(0); escapes_are_prefixes_actually(&mut prefixes, &mut alternate_opcode_map); match b { 0x26 => { @@ -3037,6 +3446,14 @@ pub fn read_instr>(mut bytes_iter: T, instruction: &mut Ins 0x67 => { prefixes.set_address_size(); }, + 0xc4 => { + instruction.prefixes = prefixes; + return vex::three_byte_vex(&mut bytes_iter, instruction, length); + } + 0xc5 => { + instruction.prefixes = prefixes; + return vex::two_byte_vex(&mut bytes_iter, instruction, length); + } 0xf0 => { prefixes.set_lock(); }, @@ -3064,6 +3481,12 @@ pub fn read_instr>(mut bytes_iter: T, instruction: &mut Ins instruction.prefixes = prefixes; read_operands(bytes_iter, instruction, record.1, &mut length)?; instruction.length = length; + + if decoder != &InstDecoder::default() { + // we might have to fix up or reject this instruction under whatever cpu features we need to + // pretend to have. + decoder.revise_instruction(instruction)?; + } Ok(()) } pub fn read_operands>(mut bytes_iter: T, instruction: &mut Instruction, operand_code: OperandCode, length: &mut u8) -> Result<(), ()> { @@ -4223,7 +4646,7 @@ fn unlikely_operands>(mut bytes_iter: T, instruction: &mut Ok(()) } -pub fn decode_one<'b, T: IntoIterator>(bytes: T, instr: &'b mut Instruction) -> Option<()> { +pub fn decode_one<'b, T: IntoIterator>(decoder: &InstDecoder, bytes: T, instr: &'b mut Instruction) -> Option<()> { instr.operands = [ OperandSpec::Nothing, OperandSpec::Nothing, @@ -4231,7 +4654,7 @@ pub fn decode_one<'b, T: IntoIterator>(bytes: T, instr: &'b mut Instruc OperandSpec::Nothing, ]; let mut bytes_iter = bytes.into_iter(); - read_instr(bytes_iter, instr).ok() + read_instr(decoder, bytes_iter, instr).ok() } /* match read_opcode(&mut bytes_iter, instr) { -- cgit v1.1