1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package armasm 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "fmt" 11 "io" 12 "math" 13 "strings" 14 ) 15 16 // GoSyntax returns the Go assembler syntax for the instruction. 17 // The syntax was originally defined by Plan 9. 18 // The pc is the program counter of the instruction, used for expanding 19 // PC-relative addresses into absolute ones. 20 // The symname function queries the symbol table for the program 21 // being disassembled. Given a target address it returns the name and base 22 // address of the symbol containing the target, if any; otherwise it returns "", 0. 23 // The reader r should read from the text segment using text addresses 24 // as offsets; it is used to display pc-relative loads as constant loads. 25 func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string { 26 if symname == nil { 27 symname = func(uint64) (string, uint64) { return "", 0 } 28 } 29 30 var args []string 31 for _, a := range inst.Args { 32 if a == nil { 33 break 34 } 35 args = append(args, plan9Arg(&inst, pc, symname, a)) 36 } 37 38 op := inst.Op.String() 39 40 switch inst.Op &^ 15 { 41 case LDR_EQ, LDRB_EQ, LDRH_EQ, LDRSB_EQ, LDRSH_EQ, VLDR_EQ: 42 // Check for RET 43 reg, _ := inst.Args[0].(Reg) 44 mem, _ := inst.Args[1].(Mem) 45 if inst.Op&^15 == LDR_EQ && reg == R15 && mem.Base == SP && mem.Sign == 0 && mem.Mode == AddrPostIndex { 46 return fmt.Sprintf("RET%s #%d", op[3:], mem.Offset) 47 } 48 49 // Check for PC-relative load. 50 if mem.Base == PC && mem.Sign == 0 && mem.Mode == AddrOffset && text != nil { 51 addr := uint32(pc) + 8 + uint32(mem.Offset) 52 buf := make([]byte, 8) 53 switch inst.Op &^ 15 { 54 case LDRB_EQ, LDRSB_EQ: 55 if _, err := text.ReadAt(buf[:1], int64(addr)); err != nil { 56 break 57 } 58 args[1] = fmt.Sprintf("$%#x", buf[0]) 59 60 case LDRH_EQ, LDRSH_EQ: 61 if _, err := text.ReadAt(buf[:2], int64(addr)); err != nil { 62 break 63 } 64 args[1] = fmt.Sprintf("$%#x", binary.LittleEndian.Uint16(buf)) 65 66 case LDR_EQ: 67 if _, err := text.ReadAt(buf[:4], int64(addr)); err != nil { 68 break 69 } 70 x := binary.LittleEndian.Uint32(buf) 71 if s, base := symname(uint64(x)); s != "" && uint64(x) == base { 72 args[1] = fmt.Sprintf("$%s(SB)", s) 73 } else { 74 args[1] = fmt.Sprintf("$%#x", x) 75 } 76 77 case VLDR_EQ: 78 switch { 79 case strings.HasPrefix(args[0], "D"): // VLDR.F64 80 if _, err := text.ReadAt(buf, int64(addr)); err != nil { 81 break 82 } 83 args[1] = fmt.Sprintf("$%f", math.Float64frombits(binary.LittleEndian.Uint64(buf))) 84 case strings.HasPrefix(args[0], "S"): // VLDR.F32 85 if _, err := text.ReadAt(buf[:4], int64(addr)); err != nil { 86 break 87 } 88 args[1] = fmt.Sprintf("$%f", math.Float32frombits(binary.LittleEndian.Uint32(buf))) 89 default: 90 panic(fmt.Sprintf("wrong FP register: %v", inst)) 91 } 92 } 93 } 94 } 95 96 // Move addressing mode into opcode suffix. 97 suffix := "" 98 switch inst.Op &^ 15 { 99 case LDR_EQ, LDRB_EQ, LDRSB_EQ, LDRH_EQ, LDRSH_EQ, STR_EQ, STRB_EQ, STRH_EQ, VLDR_EQ, VSTR_EQ: 100 mem, _ := inst.Args[1].(Mem) 101 switch mem.Mode { 102 case AddrOffset, AddrLDM: 103 // no suffix 104 case AddrPreIndex, AddrLDM_WB: 105 suffix = ".W" 106 case AddrPostIndex: 107 suffix = ".P" 108 } 109 off := "" 110 if mem.Offset != 0 { 111 off = fmt.Sprintf("%#x", mem.Offset) 112 } 113 base := fmt.Sprintf("(R%d)", int(mem.Base)) 114 index := "" 115 if mem.Sign != 0 { 116 sign := "" 117 if mem.Sign < 0 { 118 suffix += ".U" 119 } 120 shift := "" 121 if mem.Count != 0 { 122 shift = fmt.Sprintf("%s%d", plan9Shift[mem.Shift], mem.Count) 123 } 124 index = fmt.Sprintf("(%sR%d%s)", sign, int(mem.Index), shift) 125 } 126 args[1] = off + base + index 127 } 128 129 // Reverse args, placing dest last. 130 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 131 args[i], args[j] = args[j], args[i] 132 } 133 // For MLA-like instructions, the addend is the third operand. 134 switch inst.Op &^ 15 { 135 case SMLAWT_EQ, SMLAWB_EQ, MLA_EQ, MLA_S_EQ, MLS_EQ, SMMLA_EQ, SMMLS_EQ, SMLABB_EQ, SMLATB_EQ, SMLABT_EQ, SMLATT_EQ, SMLAD_EQ, SMLAD_X_EQ, SMLSD_EQ, SMLSD_X_EQ: 136 args = []string{args[1], args[2], args[0], args[3]} 137 } 138 139 switch inst.Op &^ 15 { 140 case MOV_EQ: 141 op = "MOVW" + op[3:] 142 143 case LDR_EQ: 144 op = "MOVW" + op[3:] + suffix 145 case LDRB_EQ: 146 op = "MOVBU" + op[4:] + suffix 147 case LDRSB_EQ: 148 op = "MOVBS" + op[5:] + suffix 149 case LDRH_EQ: 150 op = "MOVHU" + op[4:] + suffix 151 case LDRSH_EQ: 152 op = "MOVHS" + op[5:] + suffix 153 case VLDR_EQ: 154 switch { 155 case strings.HasPrefix(args[1], "D"): // VLDR.F64 156 op = "MOVD" + op[4:] + suffix 157 args[1] = "F" + args[1][1:] // Dx -> Fx 158 case strings.HasPrefix(args[1], "S"): // VLDR.F32 159 op = "MOVF" + op[4:] + suffix 160 if inst.Args[0].(Reg)&1 == 0 { // Sx -> Fy, y = x/2, if x is even 161 args[1] = fmt.Sprintf("F%d", (inst.Args[0].(Reg)-S0)/2) 162 } 163 default: 164 panic(fmt.Sprintf("wrong FP register: %v", inst)) 165 } 166 167 case STR_EQ: 168 op = "MOVW" + op[3:] + suffix 169 args[0], args[1] = args[1], args[0] 170 case STRB_EQ: 171 op = "MOVB" + op[4:] + suffix 172 args[0], args[1] = args[1], args[0] 173 case STRH_EQ: 174 op = "MOVH" + op[4:] + suffix 175 args[0], args[1] = args[1], args[0] 176 case VSTR_EQ: 177 switch { 178 case strings.HasPrefix(args[1], "D"): // VSTR.F64 179 op = "MOVD" + op[4:] + suffix 180 args[1] = "F" + args[1][1:] // Dx -> Fx 181 case strings.HasPrefix(args[1], "S"): // VSTR.F32 182 op = "MOVF" + op[4:] + suffix 183 if inst.Args[0].(Reg)&1 == 0 { // Sx -> Fy, y = x/2, if x is even 184 args[1] = fmt.Sprintf("F%d", (inst.Args[0].(Reg)-S0)/2) 185 } 186 default: 187 panic(fmt.Sprintf("wrong FP register: %v", inst)) 188 } 189 args[0], args[1] = args[1], args[0] 190 } 191 192 if args != nil { 193 op += " " + strings.Join(args, ", ") 194 } 195 196 return op 197 } 198 199 // assembler syntax for the various shifts. 200 // @x> is a lie; the assembler uses @> 0 201 // instead of @x> 1, but i wanted to be clear that it 202 // was a different operation (rotate right extended, not rotate right). 203 var plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"} 204 205 func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string { 206 switch a := arg.(type) { 207 case Endian: 208 209 case Imm: 210 return fmt.Sprintf("$%d", uint32(a)) 211 212 case Mem: 213 214 case PCRel: 215 addr := uint32(pc) + 8 + uint32(a) 216 if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base { 217 return fmt.Sprintf("%s(SB)", s) 218 } 219 return fmt.Sprintf("%#x", addr) 220 221 case Reg: 222 if a < 16 { 223 return fmt.Sprintf("R%d", int(a)) 224 } 225 226 case RegList: 227 var buf bytes.Buffer 228 start := -2 229 end := -2 230 fmt.Fprintf(&buf, "[") 231 flush := func() { 232 if start >= 0 { 233 if buf.Len() > 1 { 234 fmt.Fprintf(&buf, ",") 235 } 236 if start == end { 237 fmt.Fprintf(&buf, "R%d", start) 238 } else { 239 fmt.Fprintf(&buf, "R%d-R%d", start, end) 240 } 241 start = -2 242 end = -2 243 } 244 } 245 for i := 0; i < 16; i++ { 246 if a&(1<<uint(i)) != 0 { 247 if i == end+1 { 248 end++ 249 continue 250 } 251 start = i 252 end = i 253 } else { 254 flush() 255 } 256 } 257 flush() 258 fmt.Fprintf(&buf, "]") 259 return buf.String() 260 261 case RegShift: 262 return fmt.Sprintf("R%d%s$%d", int(a.Reg), plan9Shift[a.Shift], int(a.Count)) 263 264 case RegShiftReg: 265 return fmt.Sprintf("R%d%sR%d", int(a.Reg), plan9Shift[a.Shift], int(a.RegCount)) 266 } 267 return strings.ToUpper(arg.String()) 268 } 269