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