| // 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<<n - 1) << deltaShift |
| deltaShift += n |
| } |
| op := f.op + Op(delta) |
| |
| // Special case: BKPT encodes with condition but cannot have one. |
| if op&^15 == BKPT_EQ && op != BKPT { |
| continue Search |
| } |
| |
| var args Args |
| for j, aop := range f.args { |
| if aop == 0 { |
| break |
| } |
| arg := decodeArg(aop, x) |
| if arg == nil { // cannot decode argument |
| continue Search |
| } |
| args[j] = arg |
| } |
| |
| decoderCover[i] = true |
| |
| inst = Inst{ |
| Op: op, |
| Args: args, |
| Enc: x, |
| Len: 4, |
| } |
| priority = f.priority |
| continue Search |
| } |
| if inst.Op != 0 { |
| return inst, nil |
| } |
| return Inst{}, errUnknown |
| } |
| |
| // An instArg describes the encoding of a single argument. |
| // In the names used for arguments, _p_ means +, _m_ means -, |
| // _pm_ means ± (usually keyed by the U bit). |
| // The _W suffix indicates a general addressing mode based on the P and W bits. |
| // The _offset and _postindex suffixes force the given addressing mode. |
| // The rest should be somewhat self-explanatory, at least given |
| // the decodeArg function. |
| type instArg uint8 |
| |
| const ( |
| _ instArg = iota |
| arg_APSR |
| arg_FPSCR |
| arg_Dn_half |
| arg_R1_0 |
| arg_R1_12 |
| arg_R2_0 |
| arg_R2_12 |
| arg_R_0 |
| arg_R_12 |
| arg_R_12_nzcv |
| arg_R_16 |
| arg_R_16_WB |
| arg_R_8 |
| arg_R_rotate |
| arg_R_shift_R |
| arg_R_shift_imm |
| arg_SP |
| arg_Sd |
| arg_Sd_Dd |
| arg_Dd_Sd |
| arg_Sm |
| arg_Sm_Dm |
| arg_Sn |
| arg_Sn_Dn |
| arg_const |
| arg_endian |
| arg_fbits |
| arg_fp_0 |
| arg_imm24 |
| arg_imm5 |
| arg_imm5_32 |
| arg_imm5_nz |
| arg_imm_12at8_4at0 |
| arg_imm_4at16_12at0 |
| arg_imm_vfp |
| arg_label24 |
| arg_label24H |
| arg_label_m_12 |
| arg_label_p_12 |
| arg_label_pm_12 |
| arg_label_pm_4_4 |
| arg_lsb_width |
| arg_mem_R |
| arg_mem_R_pm_R_W |
| arg_mem_R_pm_R_postindex |
| arg_mem_R_pm_R_shift_imm_W |
| arg_mem_R_pm_R_shift_imm_offset |
| arg_mem_R_pm_R_shift_imm_postindex |
| arg_mem_R_pm_imm12_W |
| arg_mem_R_pm_imm12_offset |
| arg_mem_R_pm_imm12_postindex |
| arg_mem_R_pm_imm8_W |
| arg_mem_R_pm_imm8_postindex |
| arg_mem_R_pm_imm8at0_offset |
| arg_option |
| arg_registers |
| arg_registers1 |
| arg_registers2 |
| arg_satimm4 |
| arg_satimm5 |
| arg_satimm4m1 |
| arg_satimm5m1 |
| arg_widthm1 |
| ) |
| |
| // decodeArg decodes the arg described by aop from the instruction bits x. |
| // It returns nil if x cannot be decoded according to aop. |
| func decodeArg(aop instArg, x uint32) Arg { |
| switch aop { |
| default: |
| return nil |
| |
| case arg_APSR: |
| return APSR |
| case arg_FPSCR: |
| return FPSCR |
| |
| case arg_R_0: |
| return Reg(x & (1<<4 - 1)) |
| case arg_R_8: |
| return Reg((x >> 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 [<Rn>],+/-<Rm> like [<Rn>,+/-<Rm>{,<shift>}]{!} |
| // 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 [<Rn>,+/-<Rm>]{!} like [<Rn>,+/-<Rm>{,<shift>}]{!} |
| // 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 [<Rn>],+/-<Rm>{,<shift>} like [<Rn>,+/-<Rm>{,<shift>}]{!} |
| // 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 [<Rn>],+/-<Rm>{,<shift>} like [<Rn>,+/-<Rm>{,<shift>}]{!} |
| // 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 [<Rn>,#+/-<imm12>] like [<Rn>{,#+/-<imm12>}]{!} |
| // 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 [<Rn>],#+/-<imm12> like [<Rn>{,#+/-<imm12>}]{!} |
| // 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 [<Rn>],#+/-<imm8> like [<Rn>{,#+/-<imm8>}]{!} |
| // 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) |
| } |