From 9f2dfb856e89a8847ff2b1fc6ad95917093f3e28 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sun, 1 Jun 2014 18:53:59 -0400 Subject: [PATCH] cmd/objdump: add arm disassembler Fixes #7452. LGTM=minux, iant R=minux, iant CC=golang-codereviews https://golang.org/cl/104770046 --- src/cmd/objdump/Makefile | 5 + src/cmd/objdump/armasm.go | 10821 ++++++++++++++++++++++++++++++ src/cmd/objdump/main.go | 56 +- src/cmd/objdump/objdump_test.go | 3 +- 4 files changed, 10868 insertions(+), 17 deletions(-) create mode 100644 src/cmd/objdump/armasm.go diff --git a/src/cmd/objdump/Makefile b/src/cmd/objdump/Makefile index 40901909e6..1b66c26bab 100644 --- a/src/cmd/objdump/Makefile +++ b/src/cmd/objdump/Makefile @@ -1,5 +1,10 @@ +all: x86.go armasm.go + x86.go: bundle ./bundle -p main -x x86_ rsc.io/x86/x86asm | gofmt >x86.go +armasm.go: bundle + ./bundle -p main -x arm_ rsc.io/arm/armasm | gofmt >armasm.go + bundle: go build -o bundle code.google.com/p/rsc/cmd/bundle diff --git a/src/cmd/objdump/armasm.go b/src/cmd/objdump/armasm.go new file mode 100644 index 0000000000..764a3689e0 --- /dev/null +++ b/src/cmd/objdump/armasm.go @@ -0,0 +1,10821 @@ +// DO NOT EDIT. Generated by code.google.com/p/rsc/cmd/bundle +// bundle -p main -x arm_ rsc.io/arm/armasm + +/* decode.go */ + +// 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 main + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "strings" +) + +// 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 arm_instFormat struct { + mask uint32 + value uint32 + priority int8 + op arm_Op + opBits uint64 + args arm_instArgs +} + +type arm_instArgs [4]arm_instArg + +var ( + arm_errMode = fmt.Errorf("unsupported execution mode") + arm_errShort = fmt.Errorf("truncated instruction") + arm_errUnknown = fmt.Errorf("unknown instruction") +) + +var arm_decoderCover []bool + +// Decode decodes the leading bytes in src as a single instruction. +func arm_Decode(src []byte, mode arm_Mode) (inst arm_Inst, err error) { + if mode != arm_ModeARM { + return arm_Inst{}, arm_errMode + } + if len(src) < 4 { + return arm_Inst{}, arm_errShort + } + + if arm_decoderCover == nil { + arm_decoderCover = make([]bool, len(arm_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 arm_instFormats { + f := &arm_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 arm_arg_R_12: + return arm_Reg((x >> 12) & (1<<4 - 1)) + case arm_arg_R_16: + return arm_Reg((x >> 16) & (1<<4 - 1)) + + case arm_arg_R_12_nzcv: + r := arm_Reg((x >> 12) & (1<<4 - 1)) + if r == arm_R15 { + return arm_APSR_nzcv + } + return r + + case arm_arg_R_16_WB: + mode := arm_AddrLDM + if (x>>21)&1 != 0 { + mode = arm_AddrLDM_WB + } + return arm_Mem{Base: arm_Reg((x >> 16) & (1<<4 - 1)), Mode: mode} + + case arm_arg_R_rotate: + Rm := arm_Reg(x & (1<<4 - 1)) + typ, count := arm_decodeShift(x) + // ROR #0 here means ROR #0, but decodeShift rewrites to RRX #1. + if typ == arm_RotateRightExt { + return arm_Reg(Rm) + } + return arm_RegShift{Rm, typ, uint8(count)} + + case arm_arg_R_shift_R: + Rm := arm_Reg(x & (1<<4 - 1)) + Rs := arm_Reg((x >> 8) & (1<<4 - 1)) + typ := arm_Shift((x >> 5) & (1<<2 - 1)) + return arm_RegShiftReg{Rm, typ, Rs} + + case arm_arg_R_shift_imm: + Rm := arm_Reg(x & (1<<4 - 1)) + typ, count := arm_decodeShift(x) + if typ == arm_ShiftLeft && count == 0 { + return arm_Reg(Rm) + } + return arm_RegShift{Rm, typ, uint8(count)} + + case arm_arg_R1_0: + return arm_Reg((x & (1<<4 - 1))) + case arm_arg_R1_12: + return arm_Reg(((x >> 12) & (1<<4 - 1))) + case arm_arg_R2_0: + return arm_Reg((x & (1<<4 - 1)) | 1) + case arm_arg_R2_12: + return arm_Reg(((x >> 12) & (1<<4 - 1)) | 1) + + case arm_arg_SP: + return arm_SP + + case arm_arg_Sd_Dd: + v := (x >> 12) & (1<<4 - 1) + vx := (x >> 22) & 1 + sz := (x >> 8) & 1 + if sz != 0 { + return arm_D0 + arm_Reg(vx<<4+v) + } else { + return arm_S0 + arm_Reg(v<<1+vx) + } + + case arm_arg_Dd_Sd: + return arm_decodeArg(arm_arg_Sd_Dd, x^(1<<8)) + + case arm_arg_Sd: + v := (x >> 12) & (1<<4 - 1) + vx := (x >> 22) & 1 + return arm_S0 + arm_Reg(v<<1+vx) + + case arm_arg_Sm_Dm: + v := (x >> 0) & (1<<4 - 1) + vx := (x >> 5) & 1 + sz := (x >> 8) & 1 + if sz != 0 { + return arm_D0 + arm_Reg(vx<<4+v) + } else { + return arm_S0 + arm_Reg(v<<1+vx) + } + + case arm_arg_Sm: + v := (x >> 0) & (1<<4 - 1) + vx := (x >> 5) & 1 + return arm_S0 + arm_Reg(v<<1+vx) + + case arm_arg_Dn_half: + v := (x >> 16) & (1<<4 - 1) + vx := (x >> 7) & 1 + return arm_RegX{arm_D0 + arm_Reg(vx<<4+v), int((x >> 21) & 1)} + + case arm_arg_Sn_Dn: + v := (x >> 16) & (1<<4 - 1) + vx := (x >> 7) & 1 + sz := (x >> 8) & 1 + if sz != 0 { + return arm_D0 + arm_Reg(vx<<4+v) + } else { + return arm_S0 + arm_Reg(v<<1+vx) + } + + case arm_arg_Sn: + v := (x >> 16) & (1<<4 - 1) + vx := (x >> 7) & 1 + return arm_S0 + arm_Reg(v<<1+vx) + + case arm_arg_const: + v := x & (1<<8 - 1) + rot := (x >> 8) & (1<<4 - 1) * 2 + if rot > 0 && v&3 == 0 { + // could rotate less + return arm_ImmAlt{uint8(v), uint8(rot)} + } + if rot >= 24 && ((v<<(32-rot))&0xFF)>>(32-rot) == v { + // could wrap around to rot==0. + return arm_ImmAlt{uint8(v), uint8(rot)} + } + return arm_Imm(v>>rot | v<<(32-rot)) + + case arm_arg_endian: + return arm_Endian((x >> 9) & 1) + + case arm_arg_fbits: + return arm_Imm((16 << ((x >> 7) & 1)) - ((x&(1<<4-1))<<1 | (x>>5)&1)) + + case arm_arg_fp_0: + return arm_Imm(0) + + case arm_arg_imm24: + return arm_Imm(x & (1<<24 - 1)) + + case arm_arg_imm5: + return arm_Imm((x >> 7) & (1<<5 - 1)) + + case arm_arg_imm5_32: + x = (x >> 7) & (1<<5 - 1) + if x == 0 { + x = 32 + } + return arm_Imm(x) + + case arm_arg_imm5_nz: + x = (x >> 7) & (1<<5 - 1) + if x == 0 { + return nil + } + return arm_Imm(x) + + case arm_arg_imm_4at16_12at0: + return arm_Imm((x>>16)&(1<<4-1)<<12 | x&(1<<12-1)) + + case arm_arg_imm_12at8_4at0: + return arm_Imm((x>>8)&(1<<12-1)<<4 | x&(1<<4-1)) + + case arm_arg_imm_vfp: + x = (x>>16)&(1<<4-1)<<4 | x&(1<<4-1) + return arm_Imm(x) + + case arm_arg_label24: + imm := (x & (1<<24 - 1)) << 2 + return arm_PCRel(int32(imm<<6) >> 6) + + case arm_arg_label24H: + h := (x >> 24) & 1 + imm := (x&(1<<24-1))<<2 | h<<1 + return arm_PCRel(int32(imm<<6) >> 6) + + case arm_arg_label_m_12: + d := int32(x & (1<<12 - 1)) + return arm_Mem{Base: arm_PC, Mode: arm_AddrOffset, Offset: int16(-d)} + + case arm_arg_label_p_12: + d := int32(x & (1<<12 - 1)) + return arm_Mem{Base: arm_PC, Mode: arm_AddrOffset, Offset: int16(d)} + + case arm_arg_label_pm_12: + d := int32(x & (1<<12 - 1)) + u := (x >> 23) & 1 + if u == 0 { + d = -d + } + return arm_Mem{Base: arm_PC, Mode: arm_AddrOffset, Offset: int16(d)} + + case arm_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 arm_PCRel(d) + + case arm_arg_lsb_width: + lsb := (x >> 7) & (1<<5 - 1) + msb := (x >> 16) & (1<<5 - 1) + if msb < lsb || msb >= 32 { + return nil + } + return arm_Imm(msb + 1 - lsb) + + case arm_arg_mem_R: + Rn := arm_Reg((x >> 16) & (1<<4 - 1)) + return arm_Mem{Base: Rn, Mode: arm_AddrOffset} + + case arm_arg_mem_R_pm_R_postindex: + // Treat [],+/- like [,+/-{,}]{!} + // by forcing shift bits to <<0 and P=0, W=0 (postindex=true). + return arm_decodeArg(arm_arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5|1<<24|1<<21)) + + case arm_arg_mem_R_pm_R_W: + // Treat [,+/-]{!} like [,+/-{,}]{!} + // by forcing shift bits to <<0. + return arm_decodeArg(arm_arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5)) + + case arm_arg_mem_R_pm_R_shift_imm_offset: + // Treat [],+/-{,} like [,+/-{,}]{!} + // by forcing P=1, W=0 (index=false, wback=false). + return arm_decodeArg(arm_arg_mem_R_pm_R_shift_imm_W, x&^(1<<21)|1<<24) + + case arm_arg_mem_R_pm_R_shift_imm_postindex: + // Treat [],+/-{,} like [,+/-{,}]{!} + // by forcing P=0, W=0 (postindex=true). + return arm_decodeArg(arm_arg_mem_R_pm_R_shift_imm_W, x&^(1<<24|1<<21)) + + case arm_arg_mem_R_pm_R_shift_imm_W: + Rn := arm_Reg((x >> 16) & (1<<4 - 1)) + Rm := arm_Reg(x & (1<<4 - 1)) + typ, count := arm_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 := arm_AddrMode(uint8(p<<1) | uint8(w^1)) + return arm_Mem{Base: Rn, Mode: mode, Sign: sign, Index: Rm, Shift: typ, Count: count} + + case arm_arg_mem_R_pm_imm12_offset: + // Treat [,#+/-] like [{,#+/-}]{!} + // by forcing P=1, W=0 (index=false, wback=false). + return arm_decodeArg(arm_arg_mem_R_pm_imm12_W, x&^(1<<21)|1<<24) + + case arm_arg_mem_R_pm_imm12_postindex: + // Treat [],#+/- like [{,#+/-}]{!} + // by forcing P=0, W=0 (postindex=true). + return arm_decodeArg(arm_arg_mem_R_pm_imm12_W, x&^(1<<24|1<<21)) + + case arm_arg_mem_R_pm_imm12_W: + Rn := arm_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 := arm_AddrMode(uint8(p<<1) | uint8(w^1)) + return arm_Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm} + + case arm_arg_mem_R_pm_imm8_postindex: + // Treat [],#+/- like [{,#+/-}]{!} + // by forcing P=0, W=0 (postindex=true). + return arm_decodeArg(arm_arg_mem_R_pm_imm8_W, x&^(1<<24|1<<21)) + + case arm_arg_mem_R_pm_imm8_W: + Rn := arm_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 := arm_AddrMode(uint8(p<<1) | uint8(w^1)) + return arm_Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm} + + case arm_arg_mem_R_pm_imm8at0_offset: + Rn := arm_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 arm_Mem{Base: Rn, Mode: arm_AddrOffset, Offset: int16(sign) * imm} + + case arm_arg_option: + return arm_Imm(x & (1<<4 - 1)) + + case arm_arg_registers: + return arm_RegList(x & (1<<16 - 1)) + + case arm_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 arm_RegList(x) + + case arm_arg_registers1: + Rt := (x >> 12) & (1<<4 - 1) + return arm_RegList(1 << Rt) + + case arm_arg_satimm4: + return arm_Imm((x >> 16) & (1<<4 - 1)) + + case arm_arg_satimm5: + return arm_Imm((x >> 16) & (1<<5 - 1)) + + case arm_arg_satimm4m1: + return arm_Imm((x>>16)&(1<<4-1) + 1) + + case arm_arg_satimm5m1: + return arm_Imm((x>>16)&(1<<5-1) + 1) + + case arm_arg_widthm1: + return arm_Imm((x>>16)&(1<<5-1) + 1) + + } +} + +// decodeShift decodes the shift-by-immediate encoded in x. +func arm_decodeShift(x uint32) (arm_Shift, uint8) { + count := (x >> 7) & (1<<5 - 1) + typ := arm_Shift((x >> 5) & (1<<2 - 1)) + switch typ { + case arm_ShiftRight, arm_ShiftRightSigned: + if count == 0 { + count = 32 + } + case arm_RotateRight: + if count == 0 { + typ = arm_RotateRightExt + count = 1 + } + } + return typ, uint8(count) +} + +/* gnu.go */ + +// 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. + +var arm_saveDot = strings.NewReplacer( + ".F16", "_dot_F16", + ".F32", "_dot_F32", + ".F64", "_dot_F64", + ".S32", "_dot_S32", + ".U32", "_dot_U32", + ".FXS", "_dot_S", + ".FXU", "_dot_U", + ".32", "_dot_32", +) + +// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. +// This form typically matches the syntax defined in the ARM Reference Manual. +func arm_GNUSyntax(inst arm_Inst) string { + var buf bytes.Buffer + op := inst.Op.String() + op = arm_saveDot.Replace(op) + op = strings.Replace(op, ".", "", -1) + op = strings.Replace(op, "_dot_", ".", -1) + op = strings.ToLower(op) + buf.WriteString(op) + sep := " " + for i, arg := range inst.Args { + if arg == nil { + break + } + text := arm_gnuArg(&inst, i, arg) + if text == "" { + continue + } + buf.WriteString(sep) + sep = ", " + buf.WriteString(text) + } + return buf.String() +} + +func arm_gnuArg(inst *arm_Inst, argIndex int, arg arm_Arg) string { + switch inst.Op &^ 15 { + case arm_LDRD_EQ, arm_LDREXD_EQ, arm_STRD_EQ: + if argIndex == 1 { + // second argument in consecutive pair not printed + return "" + } + case arm_STREXD_EQ: + if argIndex == 2 { + // second argument in consecutive pair not printed + return "" + } + } + + switch arg := arg.(type) { + case arm_Imm: + switch inst.Op &^ 15 { + case arm_BKPT_EQ: + return fmt.Sprintf("%#04x", uint32(arg)) + case arm_SVC_EQ: + return fmt.Sprintf("%#08x", uint32(arg)) + } + return fmt.Sprintf("#%d", int32(arg)) + + case arm_ImmAlt: + return fmt.Sprintf("#%d, %d", arg.Val, arg.Rot) + + case arm_Mem: + R := arm_gnuArg(inst, -1, arg.Base) + X := "" + if arg.Sign != 0 { + X = "" + if arg.Sign < 0 { + X = "-" + } + X += arm_gnuArg(inst, -1, arg.Index) + if arg.Shift == arm_ShiftLeft && arg.Count == 0 { + // nothing + } else if arg.Shift == arm_RotateRightExt { + X += ", rrx" + } else { + X += fmt.Sprintf(", %s #%d", strings.ToLower(arg.Shift.String()), arg.Count) + } + } else { + X = fmt.Sprintf("#%d", arg.Offset) + } + + switch arg.Mode { + case arm_AddrOffset: + if X == "#0" { + return fmt.Sprintf("[%s]", R) + } + return fmt.Sprintf("[%s, %s]", R, X) + case arm_AddrPreIndex: + return fmt.Sprintf("[%s, %s]!", R, X) + case arm_AddrPostIndex: + return fmt.Sprintf("[%s], %s", R, X) + case arm_AddrLDM: + if X == "#0" { + return R + } + case arm_AddrLDM_WB: + if X == "#0" { + return R + "!" + } + } + return fmt.Sprintf("[%s Mode(%d) %s]", R, int(arg.Mode), X) + + case arm_PCRel: + return fmt.Sprintf(".%+#x", int32(arg)+4) + + case arm_Reg: + switch inst.Op &^ 15 { + case arm_LDREX_EQ: + if argIndex == 0 { + return fmt.Sprintf("r%d", int32(arg)) + } + } + switch arg { + case arm_R10: + return "sl" + case arm_R11: + return "fp" + case arm_R12: + return "ip" + } + + case arm_RegList: + var buf bytes.Buffer + fmt.Fprintf(&buf, "{") + sep := "" + for i := 0; i < 16; i++ { + if arg&(1<= arm_Op(len(arm_opstr)) || arm_opstr[op] == "" { + return fmt.Sprintf("Op(%d)", int(op)) + } + return arm_opstr[op] +} + +// An Inst is a single instruction. +type arm_Inst struct { + Op arm_Op // Opcode mnemonic + Enc uint32 // Raw encoding bits. + Len int // Length of encoding in bytes. + Args arm_Args // Instruction arguments, in ARM manual order. +} + +func (i arm_Inst) String() string { + var buf bytes.Buffer + buf.WriteString(i.Op.String()) + for j, arg := range i.Args { + if arg == nil { + break + } + if j == 0 { + buf.WriteString(" ") + } else { + buf.WriteString(", ") + } + buf.WriteString(arg.String()) + } + return buf.String() +} + +// An Args holds the instruction arguments. +// If an instruction has fewer than 4 arguments, +// the final elements in the array are nil. +type arm_Args [4]arm_Arg + +// An Arg is a single instruction argument, one of these types: +// Endian, Imm, Mem, PCRel, Reg, RegList, RegShift, RegShiftReg. +type arm_Arg interface { + IsArg() + String() string +} + +type arm_Float32Imm float32 + +func (arm_Float32Imm) IsArg() {} + +func (f arm_Float32Imm) String() string { + return fmt.Sprintf("#%v", float32(f)) +} + +type arm_Float64Imm float32 + +func (arm_Float64Imm) IsArg() {} + +func (f arm_Float64Imm) String() string { + return fmt.Sprintf("#%v", float64(f)) +} + +// An Imm is an integer constant. +type arm_Imm uint32 + +func (arm_Imm) IsArg() {} + +func (i arm_Imm) String() string { + return fmt.Sprintf("#%#x", uint32(i)) +} + +// A ImmAlt is an alternate encoding of an integer constant. +type arm_ImmAlt struct { + Val uint8 + Rot uint8 +} + +func (arm_ImmAlt) IsArg() {} + +func (i arm_ImmAlt) Imm() arm_Imm { + v := uint32(i.Val) + r := uint(i.Rot) + return arm_Imm(v>>r | v<<(32-r)) +} + +func (i arm_ImmAlt) String() string { + return fmt.Sprintf("#%#x, %d", i.Val, i.Rot) +} + +// A Label is a text (code) address. +type arm_Label uint32 + +func (arm_Label) IsArg() {} + +func (i arm_Label) String() string { + return fmt.Sprintf("%#x", uint32(i)) +} + +// A Reg is a single register. +// The zero value denotes R0, not the absence of a register. +type arm_Reg uint8 + +const ( + arm_R0 arm_Reg = iota + arm_R1 + arm_R2 + arm_R3 + arm_R4 + arm_R5 + arm_R6 + arm_R7 + arm_R8 + arm_R9 + arm_R10 + arm_R11 + arm_R12 + arm_R13 + arm_R14 + arm_R15 + + arm_S0 + arm_S1 + arm_S2 + arm_S3 + arm_S4 + arm_S5 + arm_S6 + arm_S7 + arm_S8 + arm_S9 + arm_S10 + arm_S11 + arm_S12 + arm_S13 + arm_S14 + arm_S15 + arm_S16 + arm_S17 + arm_S18 + arm_S19 + arm_S20 + arm_S21 + arm_S22 + arm_S23 + arm_S24 + arm_S25 + arm_S26 + arm_S27 + arm_S28 + arm_S29 + arm_S30 + arm_S31 + + arm_D0 + arm_D1 + arm_D2 + arm_D3 + arm_D4 + arm_D5 + arm_D6 + arm_D7 + arm_D8 + arm_D9 + arm_D10 + arm_D11 + arm_D12 + arm_D13 + arm_D14 + arm_D15 + arm_D16 + arm_D17 + arm_D18 + arm_D19 + arm_D20 + arm_D21 + arm_D22 + arm_D23 + arm_D24 + arm_D25 + arm_D26 + arm_D27 + arm_D28 + arm_D29 + arm_D30 + arm_D31 + + arm_APSR + arm_APSR_nzcv + arm_FPSCR + + arm_SP = arm_R13 + arm_LR = arm_R14 + arm_PC = arm_R15 +) + +func (arm_Reg) IsArg() {} + +func (r arm_Reg) String() string { + switch r { + case arm_APSR: + return "APSR" + case arm_APSR_nzcv: + return "APSR_nzcv" + case arm_FPSCR: + return "FPSCR" + case arm_SP: + return "SP" + case arm_PC: + return "PC" + case arm_LR: + return "LR" + } + if arm_R0 <= r && r <= arm_R15 { + return fmt.Sprintf("R%d", int(r-arm_R0)) + } + if arm_S0 <= r && r <= arm_S31 { + return fmt.Sprintf("S%d", int(r-arm_S0)) + } + if arm_D0 <= r && r <= arm_D31 { + return fmt.Sprintf("D%d", int(r-arm_D0)) + } + return fmt.Sprintf("Reg(%d)", int(r)) +} + +// A RegX represents a fraction of a multi-value register. +// The Index field specifies the index number, +// but the size of the fraction is not specified. +// It must be inferred from the instruction and the register type. +// For example, in a VMOV instruction, RegX{D5, 1} represents +// the top 32 bits of the 64-bit D5 register. +type arm_RegX struct { + Reg arm_Reg + Index int +} + +func (arm_RegX) IsArg() {} + +func (r arm_RegX) String() string { + return fmt.Sprintf("%s[%d]", r.Reg, r.Index) +} + +// A RegList is a register list. +// Bits at indexes x = 0 through 15 indicate whether the corresponding Rx register is in the list. +type arm_RegList uint16 + +func (arm_RegList) IsArg() {} + +func (r arm_RegList) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "{") + sep := "" + for i := 0; i < 16; i++ { + if r&(1< is a lie; the assembler uses @> 0 +// instead of @x> 1, but i wanted to be clear that it +// was a different operation (rotate right extended, not rotate right). +var arm_plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"} + +func arm_plan9Arg(inst *arm_Inst, pc uint64, symname func(uint64) (string, uint64), arg arm_Arg) string { + switch a := arg.(type) { + case arm_Endian: + + case arm_Imm: + return fmt.Sprintf("$%d", int(a)) + + case arm_Mem: + + case arm_PCRel: + addr := uint32(pc) + 8 + uint32(a) + if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base { + return fmt.Sprintf("%s(SB)", s) + } + return fmt.Sprintf("%#x", addr) + + case arm_Reg: + if a < 16 { + return fmt.Sprintf("R%d", int(a)) + } + + case arm_RegList: + var buf bytes.Buffer + start := -2 + end := -2 + fmt.Fprintf(&buf, "[") + flush := func() { + if start >= 0 { + if buf.Len() > 1 { + fmt.Fprintf(&buf, ",") + } + if start == end { + fmt.Fprintf(&buf, "R%d", start) + } else { + fmt.Fprintf(&buf, "R%d-R%d", start, end) + } + } + } + for i := 0; i < 16; i++ { + if a&(1< ,,# cond:4|0|0|1|0|1|0|1|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x00a00010, 4, arm_ADC_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_R_shift_R}}, // ADC{S} ,,, cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x00a00000, 2, arm_ADC_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_R_shift_imm}}, // ADC{S} ,,{,} cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fe00000, 0x02800000, 2, arm_ADD_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_const}}, // ADD{S} ,,# cond:4|0|0|1|0|1|0|0|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x00800010, 4, arm_ADD_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_R_shift_R}}, // ADD{S} ,,, cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x00800000, 2, arm_ADD_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_R_shift_imm}}, // ADD{S} ,,{,} cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fef0000, 0x028d0000, 2, arm_ADD_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_SP, arm_arg_const}}, // ADD{S} ,SP,# cond:4|0|0|1|0|1|0|0|S|1|1|0|1|Rd:4|imm12:12 + {0x0fef0010, 0x008d0000, 2, arm_ADD_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_SP, arm_arg_R_shift_imm}}, // ADD{S} ,SP,{,} cond:4|0|0|0|0|1|0|0|S|1|1|0|1|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fe00000, 0x02000000, 2, arm_AND_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_const}}, // AND{S} ,,# cond:4|0|0|1|0|0|0|0|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x00000010, 4, arm_AND_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_R_shift_R}}, // AND{S} ,,, cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x00000000, 2, arm_AND_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_R_shift_imm}}, // AND{S} ,,{,} cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fef0070, 0x01a00040, 4, arm_ASR_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_0, arm_arg_imm5_32}}, // ASR{S} ,,# cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|1|0|0|Rm:4 + {0x0fef00f0, 0x01a00050, 4, arm_ASR_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_0, arm_arg_R_8}}, // ASR{S} ,, cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|1|0|1|Rn:4 + {0x0f000000, 0x0a000000, 4, arm_B_EQ, 0x1c04, arm_instArgs{arm_arg_label24}}, // B cond:4|1|0|1|0|imm24:24 + {0x0fe0007f, 0x07c0001f, 4, arm_BFC_EQ, 0x1c04, arm_instArgs{arm_arg_R_12, arm_arg_imm5, arm_arg_lsb_width}}, // BFC ,#,# cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|1|1|1|1 + {0x0fe00070, 0x07c00010, 2, arm_BFI_EQ, 0x1c04, arm_instArgs{arm_arg_R_12, arm_arg_R_0, arm_arg_imm5, arm_arg_lsb_width}}, // BFI ,,#,# cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|Rn:4 + {0x0fe00000, 0x03c00000, 2, arm_BIC_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_const}}, // BIC{S} ,,# cond:4|0|0|1|1|1|1|0|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x01c00010, 4, arm_BIC_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_R_shift_R}}, // BIC{S} ,,, cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x01c00000, 2, arm_BIC_EQ, 0x14011c04, arm_instArgs{arm_arg_R_12, arm_arg_R_16, arm_arg_R_shift_imm}}, // BIC{S} ,,{,} cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0ff000f0, 0x01200070, 4, arm_BKPT_EQ, 0x1c04, arm_instArgs{arm_arg_imm_12at8_4at0}}, // BKPT # cond:4|0|0|0|1|0|0|1|0|imm12:12|0|1|1|1|imm4:4 + {0x0f000000, 0x0b000000, 4, arm_BL_EQ, 0x1c04, arm_instArgs{arm_arg_label24}}, // BL cond:4|1|0|1|1|imm24:24 + {0xfe000000, 0xfa000000, 4, arm_BLX, 0x0, arm_instArgs{arm_arg_label24H}}, // BLX 1|1|1|1|1|0|1|H|imm24:24 + {0x0ffffff0, 0x012fff30, 4, arm_BLX_EQ, 0x1c04, arm_instArgs{arm_arg_R_0}}, // BLX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4 + {0x0ff000f0, 0x012fff30, 3, arm_BLX_EQ, 0x1c04, arm_instArgs{arm_arg_R_0}}, // BLX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4 + {0x0ffffff0, 0x012fff10, 4, arm_BX_EQ, 0x1c04, arm_instArgs{arm_arg_R_0}}, // BX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ff000f0, 0x012fff10, 3, arm_BX_EQ, 0x1c04, arm_instArgs{arm_arg_R_0}}, // BX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ffffff0, 0x012fff20, 4, arm_BXJ_EQ, 0x1c04, arm_instArgs{arm_arg_R_0}}, // BXJ cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|0|Rm:4 + {0x0ff000f0, 0x012fff20, 3, arm_BXJ_EQ, 0x1c04, arm_instArgs{arm_arg_R_0}}, // BXJ cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|0|Rm:4 + {0xffffffff, 0xf57ff01f, 4, arm_CLREX, 0x0, arm_instArgs{}}, // CLREX 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|1|(1)|(1)|(1)|(1) + {0xfff000f0, 0xf57ff01f, 3, arm_CLREX, 0x0, arm_instArgs{}}, // CLREX 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|1|(1)|(1)|(1)|(1) + {0x0fff0ff0, 0x016f0f10, 4, arm_CLZ_EQ, 0x1c04, arm_instArgs{arm_arg_R_12, arm_arg_R_0}}, // CLZ , cond:4|0|0|0|1|0|1|1|0|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ff000f0, 0x016f0f10, 3, arm_CLZ_EQ, 0x1c04, arm_instArgs{arm_arg_R_12, arm_arg_R_0}}, // CLZ , cond:4|0|0|0|1|0|1|1|0|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ff0f000, 0x03700000, 4, arm_CMN_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_const}}, // CMN ,# cond:4|0|0|1|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff00000, 0x03700000, 3, arm_CMN_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_const}}, // CMN ,# cond:4|0|0|1|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff0f090, 0x01700010, 4, arm_CMN_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_R_shift_R}}, // CMN ,, cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff00090, 0x01700010, 3, arm_CMN_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_R_shift_R}}, // CMN ,, cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff0f010, 0x01700000, 4, arm_CMN_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_R_shift_imm}}, // CMN ,{,} cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ff00010, 0x01700000, 3, arm_CMN_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_R_shift_imm}}, // CMN ,{,} cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ff0f000, 0x03500000, 4, arm_CMP_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_const}}, // CMP ,# cond:4|0|0|1|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff00000, 0x03500000, 3, arm_CMP_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_const}}, // CMP ,# cond:4|0|0|1|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff0f090, 0x01500010, 4, arm_CMP_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_R_shift_R}}, // CMP ,, cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff00090, 0x01500010, 3, arm_CMP_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_R_shift_R}}, // CMP ,, cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff0f010, 0x01500000, 4, arm_CMP_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_R_shift_imm}}, // CMP ,{,} cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ff00010, 0x01500000, 3, arm_CMP_EQ, 0x1c04, arm_instArgs{arm_arg_R_16, arm_arg_R_shift_imm}}, // CMP ,{,} cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ffffff0, 0x0320f0f0, 4, arm_DBG_EQ, 0x1c04, arm_instArgs{arm_arg_option}}, // DBG #