// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package armasm import ( "encoding/binary" "fmt" ) // An instFormat describes the format of an instruction encoding. // An instruction with 32-bit value x matches the format if x&mask == value // and the condition matches. // The condition matches if x>>28 == 0xF && value>>28==0xF // or if x>>28 != 0xF and value>>28 == 0. // If x matches the format, then the rest of the fields describe how to interpret x. // The opBits describe bits that should be extracted from x and added to the opcode. // For example opBits = 0x1234 means that the value // (2 bits at offset 1) followed by (4 bits at offset 3) // should be added to op. // Finally the args describe how to decode the instruction arguments. // args is stored as a fixed-size array; if there are fewer than len(args) arguments, // args[i] == 0 marks the end of the argument list. type instFormat struct { mask uint32 value uint32 priority int8 op Op opBits uint64 args instArgs } type instArgs [4]instArg var ( errMode = fmt.Errorf("unsupported execution mode") errShort = fmt.Errorf("truncated instruction") errUnknown = fmt.Errorf("unknown instruction") ) var decoderCover []bool // Decode decodes the leading bytes in src as a single instruction. func Decode(src []byte, mode Mode) (inst Inst, err error) { if mode != ModeARM { return Inst{}, errMode } if len(src) < 4 { return Inst{}, errShort } if decoderCover == nil { decoderCover = make([]bool, len(instFormats)) } x := binary.LittleEndian.Uint32(src) // The instFormat table contains both conditional and unconditional instructions. // Considering only the top 4 bits, the conditional instructions use mask=0, value=0, // while the unconditional instructions use mask=f, value=f. // Prepare a version of x with the condition cleared to 0 in conditional instructions // and then assume mask=f during matching. const condMask = 0xf0000000 xNoCond := x if x&condMask != condMask { xNoCond &^= condMask } var priority int8 Search: for i := range instFormats { f := &instFormats[i] if xNoCond&(f.mask|condMask) != f.value || f.priority <= priority { continue } delta := uint32(0) deltaShift := uint(0) for opBits := f.opBits; opBits != 0; opBits >>= 16 { n := uint(opBits & 0xFF) off := uint((opBits >> 8) & 0xFF) delta |= (x >> off) & (1<> 8) & (1<<4 - 1)) case arg_R_12: return Reg((x >> 12) & (1<<4 - 1)) case arg_R_16: return Reg((x >> 16) & (1<<4 - 1)) case arg_R_12_nzcv: r := Reg((x >> 12) & (1<<4 - 1)) if r == R15 { return APSR_nzcv } return r case arg_R_16_WB: mode := AddrLDM if (x>>21)&1 != 0 { mode = AddrLDM_WB } return Mem{Base: Reg((x >> 16) & (1<<4 - 1)), Mode: mode} case arg_R_rotate: Rm := Reg(x & (1<<4 - 1)) typ, count := decodeShift(x) // ROR #0 here means ROR #0, but decodeShift rewrites to RRX #1. if typ == RotateRightExt { return Reg(Rm) } return RegShift{Rm, typ, uint8(count)} case arg_R_shift_R: Rm := Reg(x & (1<<4 - 1)) Rs := Reg((x >> 8) & (1<<4 - 1)) typ := Shift((x >> 5) & (1<<2 - 1)) return RegShiftReg{Rm, typ, Rs} case arg_R_shift_imm: Rm := Reg(x & (1<<4 - 1)) typ, count := decodeShift(x) if typ == ShiftLeft && count == 0 { return Reg(Rm) } return RegShift{Rm, typ, uint8(count)} case arg_R1_0: return Reg((x & (1<<4 - 1))) case arg_R1_12: return Reg(((x >> 12) & (1<<4 - 1))) case arg_R2_0: return Reg((x & (1<<4 - 1)) | 1) case arg_R2_12: return Reg(((x >> 12) & (1<<4 - 1)) | 1) case arg_SP: return SP case arg_Sd_Dd: v := (x >> 12) & (1<<4 - 1) vx := (x >> 22) & 1 sz := (x >> 8) & 1 if sz != 0 { return D0 + Reg(vx<<4+v) } else { return S0 + Reg(v<<1+vx) } case arg_Dd_Sd: return decodeArg(arg_Sd_Dd, x^(1<<8)) case arg_Sd: v := (x >> 12) & (1<<4 - 1) vx := (x >> 22) & 1 return S0 + Reg(v<<1+vx) case arg_Sm_Dm: v := (x >> 0) & (1<<4 - 1) vx := (x >> 5) & 1 sz := (x >> 8) & 1 if sz != 0 { return D0 + Reg(vx<<4+v) } else { return S0 + Reg(v<<1+vx) } case arg_Sm: v := (x >> 0) & (1<<4 - 1) vx := (x >> 5) & 1 return S0 + Reg(v<<1+vx) case arg_Dn_half: v := (x >> 16) & (1<<4 - 1) vx := (x >> 7) & 1 return RegX{D0 + Reg(vx<<4+v), int((x >> 21) & 1)} case arg_Sn_Dn: v := (x >> 16) & (1<<4 - 1) vx := (x >> 7) & 1 sz := (x >> 8) & 1 if sz != 0 { return D0 + Reg(vx<<4+v) } else { return S0 + Reg(v<<1+vx) } case arg_Sn: v := (x >> 16) & (1<<4 - 1) vx := (x >> 7) & 1 return S0 + Reg(v<<1+vx) case arg_const: v := x & (1<<8 - 1) rot := (x >> 8) & (1<<4 - 1) * 2 if rot > 0 && v&3 == 0 { // could rotate less return ImmAlt{uint8(v), uint8(rot)} } if rot >= 24 && ((v<<(32-rot))&0xFF)>>(32-rot) == v { // could wrap around to rot==0. return ImmAlt{uint8(v), uint8(rot)} } return Imm(v>>rot | v<<(32-rot)) case arg_endian: return Endian((x >> 9) & 1) case arg_fbits: return Imm((16 << ((x >> 7) & 1)) - ((x&(1<<4-1))<<1 | (x>>5)&1)) case arg_fp_0: return Imm(0) case arg_imm24: return Imm(x & (1<<24 - 1)) case arg_imm5: return Imm((x >> 7) & (1<<5 - 1)) case arg_imm5_32: x = (x >> 7) & (1<<5 - 1) if x == 0 { x = 32 } return Imm(x) case arg_imm5_nz: x = (x >> 7) & (1<<5 - 1) if x == 0 { return nil } return Imm(x) case arg_imm_4at16_12at0: return Imm((x>>16)&(1<<4-1)<<12 | x&(1<<12-1)) case arg_imm_12at8_4at0: return Imm((x>>8)&(1<<12-1)<<4 | x&(1<<4-1)) case arg_imm_vfp: x = (x>>16)&(1<<4-1)<<4 | x&(1<<4-1) return Imm(x) case arg_label24: imm := (x & (1<<24 - 1)) << 2 return PCRel(int32(imm<<6) >> 6) case arg_label24H: h := (x >> 24) & 1 imm := (x&(1<<24-1))<<2 | h<<1 return PCRel(int32(imm<<6) >> 6) case arg_label_m_12: d := int32(x & (1<<12 - 1)) return Mem{Base: PC, Mode: AddrOffset, Offset: int16(-d)} case arg_label_p_12: d := int32(x & (1<<12 - 1)) return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)} case arg_label_pm_12: d := int32(x & (1<<12 - 1)) u := (x >> 23) & 1 if u == 0 { d = -d } return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)} case arg_label_pm_4_4: d := int32((x>>8)&(1<<4-1)<<4 | x&(1<<4-1)) u := (x >> 23) & 1 if u == 0 { d = -d } return PCRel(d) case arg_lsb_width: lsb := (x >> 7) & (1<<5 - 1) msb := (x >> 16) & (1<<5 - 1) if msb < lsb || msb >= 32 { return nil } return Imm(msb + 1 - lsb) case arg_mem_R: Rn := Reg((x >> 16) & (1<<4 - 1)) return Mem{Base: Rn, Mode: AddrOffset} case arg_mem_R_pm_R_postindex: // Treat [],+/- like [,+/-{,}]{!} // by forcing shift bits to <<0 and P=0, W=0 (postindex=true). return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5|1<<24|1<<21)) case arg_mem_R_pm_R_W: // Treat [,+/-]{!} like [,+/-{,}]{!} // by forcing shift bits to <<0. return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5)) case arg_mem_R_pm_R_shift_imm_offset: // Treat [],+/-{,} like [,+/-{,}]{!} // by forcing P=1, W=0 (index=false, wback=false). return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<21)|1<<24) case arg_mem_R_pm_R_shift_imm_postindex: // Treat [],+/-{,} like [,+/-{,}]{!} // by forcing P=0, W=0 (postindex=true). return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<24|1<<21)) case arg_mem_R_pm_R_shift_imm_W: Rn := Reg((x >> 16) & (1<<4 - 1)) Rm := Reg(x & (1<<4 - 1)) typ, count := decodeShift(x) u := (x >> 23) & 1 w := (x >> 21) & 1 p := (x >> 24) & 1 if p == 0 && w == 1 { return nil } sign := int8(+1) if u == 0 { sign = -1 } mode := AddrMode(uint8(p<<1) | uint8(w^1)) return Mem{Base: Rn, Mode: mode, Sign: sign, Index: Rm, Shift: typ, Count: count} case arg_mem_R_pm_imm12_offset: // Treat [,#+/-] like [{,#+/-}]{!} // by forcing P=1, W=0 (index=false, wback=false). return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<21)|1<<24) case arg_mem_R_pm_imm12_postindex: // Treat [],#+/- like [{,#+/-}]{!} // by forcing P=0, W=0 (postindex=true). return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<24|1<<21)) case arg_mem_R_pm_imm12_W: Rn := Reg((x >> 16) & (1<<4 - 1)) u := (x >> 23) & 1 w := (x >> 21) & 1 p := (x >> 24) & 1 if p == 0 && w == 1 { return nil } sign := int8(+1) if u == 0 { sign = -1 } imm := int16(x & (1<<12 - 1)) mode := AddrMode(uint8(p<<1) | uint8(w^1)) return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm} case arg_mem_R_pm_imm8_postindex: // Treat [],#+/- like [{,#+/-}]{!} // by forcing P=0, W=0 (postindex=true). return decodeArg(arg_mem_R_pm_imm8_W, x&^(1<<24|1<<21)) case arg_mem_R_pm_imm8_W: Rn := Reg((x >> 16) & (1<<4 - 1)) u := (x >> 23) & 1 w := (x >> 21) & 1 p := (x >> 24) & 1 if p == 0 && w == 1 { return nil } sign := int8(+1) if u == 0 { sign = -1 } imm := int16((x>>8)&(1<<4-1)<<4 | x&(1<<4-1)) mode := AddrMode(uint8(p<<1) | uint8(w^1)) return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm} case arg_mem_R_pm_imm8at0_offset: Rn := Reg((x >> 16) & (1<<4 - 1)) u := (x >> 23) & 1 sign := int8(+1) if u == 0 { sign = -1 } imm := int16(x&(1<<8-1)) << 2 return Mem{Base: Rn, Mode: AddrOffset, Offset: int16(sign) * imm} case arg_option: return Imm(x & (1<<4 - 1)) case arg_registers: return RegList(x & (1<<16 - 1)) case arg_registers2: x &= 1<<16 - 1 n := 0 for i := 0; i < 16; i++ { if x>>uint(i)&1 != 0 { n++ } } if n < 2 { return nil } return RegList(x) case arg_registers1: Rt := (x >> 12) & (1<<4 - 1) return RegList(1 << Rt) case arg_satimm4: return Imm((x >> 16) & (1<<4 - 1)) case arg_satimm5: return Imm((x >> 16) & (1<<5 - 1)) case arg_satimm4m1: return Imm((x>>16)&(1<<4-1) + 1) case arg_satimm5m1: return Imm((x>>16)&(1<<5-1) + 1) case arg_widthm1: return Imm((x>>16)&(1<<5-1) + 1) } } // decodeShift decodes the shift-by-immediate encoded in x. func decodeShift(x uint32) (Shift, uint8) { count := (x >> 7) & (1<<5 - 1) typ := Shift((x >> 5) & (1<<2 - 1)) switch typ { case ShiftRight, ShiftRightSigned: if count == 0 { count = 32 } case RotateRight: if count == 0 { typ = RotateRightExt count = 1 } } return typ, uint8(count) }