cubefs
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
5package x86asm
6
7import (
8"fmt"
9"strings"
10)
11
12type SymLookup func(uint64) (string, uint64)
13
14// GoSyntax returns the Go assembler syntax for the instruction.
15// The syntax was originally defined by Plan 9.
16// The pc is the program counter of the instruction, used for expanding
17// PC-relative addresses into absolute ones.
18// The symname function queries the symbol table for the program
19// being disassembled. Given a target address it returns the name and base
20// address of the symbol containing the target, if any; otherwise it returns "", 0.
21func GoSyntax(inst Inst, pc uint64, symname SymLookup) string {
22if symname == nil {
23symname = func(uint64) (string, uint64) { return "", 0 }
24}
25var args []string
26for i := len(inst.Args) - 1; i >= 0; i-- {
27a := inst.Args[i]
28if a == nil {
29continue
30}
31args = append(args, plan9Arg(&inst, pc, symname, a))
32}
33
34var rep string
35var last Prefix
36for _, p := range inst.Prefix {
37if p == 0 || p.IsREX() || p.IsVEX() {
38break
39}
40
41switch {
42// Don't show prefixes implied by the instruction text.
43case p&0xFF00 == PrefixImplicit:
44continue
45// Only REP and REPN are recognized repeaters. Plan 9 syntax
46// treats them as separate opcodes.
47case p&0xFF == PrefixREP:
48rep = "REP; "
49case p&0xFF == PrefixREPN:
50rep = "REPNE; "
51default:
52last = p
53}
54}
55
56prefix := ""
57switch last & 0xFF {
58case 0, 0x66, 0x67:
59// ignore
60default:
61prefix += last.String() + " "
62}
63
64op := inst.Op.String()
65if plan9Suffix[inst.Op] {
66s := inst.DataSize
67if inst.MemBytes != 0 {
68s = inst.MemBytes * 8
69}
70switch s {
71case 8:
72op += "B"
73case 16:
74op += "W"
75case 32:
76op += "L"
77case 64:
78op += "Q"
79}
80}
81
82if args != nil {
83op += " " + strings.Join(args, ", ")
84}
85
86return rep + prefix + op
87}
88
89func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
90switch a := arg.(type) {
91case Reg:
92return plan9Reg[a]
93case Rel:
94if pc == 0 {
95break
96}
97// If the absolute address is the start of a symbol, use the name.
98// Otherwise use the raw address, so that things like relative
99// jumps show up as JMP 0x123 instead of JMP f+10(SB).
100// It is usually easier to search for 0x123 than to do the mental
101// arithmetic to find f+10.
102addr := pc + uint64(inst.Len) + uint64(a)
103if s, base := symname(addr); s != "" && addr == base {
104return fmt.Sprintf("%s(SB)", s)
105}
106return fmt.Sprintf("%#x", addr)
107
108case Imm:
109if s, base := symname(uint64(a)); s != "" {
110suffix := ""
111if uint64(a) != base {
112suffix = fmt.Sprintf("%+d", uint64(a)-base)
113}
114return fmt.Sprintf("$%s%s(SB)", s, suffix)
115}
116if inst.Mode == 32 {
117return fmt.Sprintf("$%#x", uint32(a))
118}
119if Imm(int32(a)) == a {
120return fmt.Sprintf("$%#x", int64(a))
121}
122return fmt.Sprintf("$%#x", uint64(a))
123case Mem:
124if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
125suffix := ""
126if disp != 0 {
127suffix = fmt.Sprintf("%+d", disp)
128}
129return fmt.Sprintf("%s%s(SB)", s, suffix)
130}
131s := ""
132if a.Segment != 0 {
133s += fmt.Sprintf("%s:", plan9Reg[a.Segment])
134}
135if a.Disp != 0 {
136s += fmt.Sprintf("%#x", a.Disp)
137} else {
138s += "0"
139}
140if a.Base != 0 {
141s += fmt.Sprintf("(%s)", plan9Reg[a.Base])
142}
143if a.Index != 0 && a.Scale != 0 {
144s += fmt.Sprintf("(%s*%d)", plan9Reg[a.Index], a.Scale)
145}
146return s
147}
148return arg.String()
149}
150
151func memArgToSymbol(a Mem, pc uint64, instrLen int, symname SymLookup) (string, int64) {
152if a.Segment != 0 || a.Disp == 0 || a.Index != 0 || a.Scale != 0 {
153return "", 0
154}
155
156var disp uint64
157switch a.Base {
158case IP, EIP, RIP:
159disp = uint64(a.Disp + int64(pc) + int64(instrLen))
160case 0:
161disp = uint64(a.Disp)
162default:
163return "", 0
164}
165
166s, base := symname(disp)
167return s, int64(disp) - int64(base)
168}
169
170var plan9Suffix = [maxOp + 1]bool{
171ADC: true,
172ADD: true,
173AND: true,
174BSF: true,
175BSR: true,
176BT: true,
177BTC: true,
178BTR: true,
179BTS: true,
180CMP: true,
181CMPXCHG: true,
182CVTSI2SD: true,
183CVTSI2SS: true,
184CVTSD2SI: true,
185CVTSS2SI: true,
186CVTTSD2SI: true,
187CVTTSS2SI: true,
188DEC: true,
189DIV: true,
190FLDENV: true,
191FRSTOR: true,
192IDIV: true,
193IMUL: true,
194IN: true,
195INC: true,
196LEA: true,
197MOV: true,
198MOVNTI: true,
199MUL: true,
200NEG: true,
201NOP: true,
202NOT: true,
203OR: true,
204OUT: true,
205POP: true,
206POPA: true,
207PUSH: true,
208PUSHA: true,
209RCL: true,
210RCR: true,
211ROL: true,
212ROR: true,
213SAR: true,
214SBB: true,
215SHL: true,
216SHLD: true,
217SHR: true,
218SHRD: true,
219SUB: true,
220TEST: true,
221XADD: true,
222XCHG: true,
223XOR: true,
224}
225
226var plan9Reg = [...]string{
227AL: "AL",
228CL: "CL",
229BL: "BL",
230DL: "DL",
231AH: "AH",
232CH: "CH",
233BH: "BH",
234DH: "DH",
235SPB: "SP",
236BPB: "BP",
237SIB: "SI",
238DIB: "DI",
239R8B: "R8",
240R9B: "R9",
241R10B: "R10",
242R11B: "R11",
243R12B: "R12",
244R13B: "R13",
245R14B: "R14",
246R15B: "R15",
247AX: "AX",
248CX: "CX",
249BX: "BX",
250DX: "DX",
251SP: "SP",
252BP: "BP",
253SI: "SI",
254DI: "DI",
255R8W: "R8",
256R9W: "R9",
257R10W: "R10",
258R11W: "R11",
259R12W: "R12",
260R13W: "R13",
261R14W: "R14",
262R15W: "R15",
263EAX: "AX",
264ECX: "CX",
265EDX: "DX",
266EBX: "BX",
267ESP: "SP",
268EBP: "BP",
269ESI: "SI",
270EDI: "DI",
271R8L: "R8",
272R9L: "R9",
273R10L: "R10",
274R11L: "R11",
275R12L: "R12",
276R13L: "R13",
277R14L: "R14",
278R15L: "R15",
279RAX: "AX",
280RCX: "CX",
281RDX: "DX",
282RBX: "BX",
283RSP: "SP",
284RBP: "BP",
285RSI: "SI",
286RDI: "DI",
287R8: "R8",
288R9: "R9",
289R10: "R10",
290R11: "R11",
291R12: "R12",
292R13: "R13",
293R14: "R14",
294R15: "R15",
295IP: "IP",
296EIP: "IP",
297RIP: "IP",
298F0: "F0",
299F1: "F1",
300F2: "F2",
301F3: "F3",
302F4: "F4",
303F5: "F5",
304F6: "F6",
305F7: "F7",
306M0: "M0",
307M1: "M1",
308M2: "M2",
309M3: "M3",
310M4: "M4",
311M5: "M5",
312M6: "M6",
313M7: "M7",
314X0: "X0",
315X1: "X1",
316X2: "X2",
317X3: "X3",
318X4: "X4",
319X5: "X5",
320X6: "X6",
321X7: "X7",
322X8: "X8",
323X9: "X9",
324X10: "X10",
325X11: "X11",
326X12: "X12",
327X13: "X13",
328X14: "X14",
329X15: "X15",
330CS: "CS",
331SS: "SS",
332DS: "DS",
333ES: "ES",
334FS: "FS",
335GS: "GS",
336GDTR: "GDTR",
337IDTR: "IDTR",
338LDTR: "LDTR",
339MSW: "MSW",
340TASK: "TASK",
341CR0: "CR0",
342CR1: "CR1",
343CR2: "CR2",
344CR3: "CR3",
345CR4: "CR4",
346CR5: "CR5",
347CR6: "CR6",
348CR7: "CR7",
349CR8: "CR8",
350CR9: "CR9",
351CR10: "CR10",
352CR11: "CR11",
353CR12: "CR12",
354CR13: "CR13",
355CR14: "CR14",
356CR15: "CR15",
357DR0: "DR0",
358DR1: "DR1",
359DR2: "DR2",
360DR3: "DR3",
361DR4: "DR4",
362DR5: "DR5",
363DR6: "DR6",
364DR7: "DR7",
365DR8: "DR8",
366DR9: "DR9",
367DR10: "DR10",
368DR11: "DR11",
369DR12: "DR12",
370DR13: "DR13",
371DR14: "DR14",
372DR15: "DR15",
373TR0: "TR0",
374TR1: "TR1",
375TR2: "TR2",
376TR3: "TR3",
377TR4: "TR4",
378TR5: "TR5",
379TR6: "TR6",
380TR7: "TR7",
381}
382