x86: use symname in IntelSyntax and GNUSyntax

Change-Id: I8b75df1c9dedb402a227182588b2dea4f82d348a
Reviewed-on: https://go-review.googlesource.com/45098
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/x86/x86asm/decode_test.go b/x86/x86asm/decode_test.go
index fae8490..127be26 100644
--- a/x86/x86asm/decode_test.go
+++ b/x86/x86asm/decode_test.go
@@ -54,9 +54,9 @@
 		} else {
 			switch syntax {
 			case "gnu":
-				out = GNUSyntax(inst, 0)
+				out = GNUSyntax(inst, 0, nil)
 			case "intel":
-				out = IntelSyntax(inst, 0)
+				out = IntelSyntax(inst, 0, nil)
 			case "plan9": // [sic]
 				out = GoSyntax(inst, 0, nil)
 			default:
diff --git a/x86/x86asm/ext_test.go b/x86/x86asm/ext_test.go
index a6097e1..7a088a0 100644
--- a/x86/x86asm/ext_test.go
+++ b/x86/x86asm/ext_test.go
@@ -248,9 +248,9 @@
 	} else {
 		switch syntax {
 		case "gnu":
-			text = GNUSyntax(inst, 0)
+			text = GNUSyntax(inst, 0, nil)
 		case "intel":
-			text = IntelSyntax(inst, 0)
+			text = IntelSyntax(inst, 0, nil)
 		case "plan9": // [sic]
 			text = GoSyntax(inst, 0, nil)
 		default:
diff --git a/x86/x86asm/format_test.go b/x86/x86asm/format_test.go
index 1f53638..2750a28 100644
--- a/x86/x86asm/format_test.go
+++ b/x86/x86asm/format_test.go
@@ -38,8 +38,8 @@
 			"mov 0x779e9(%rip),%rax"},
 		{0x450664, "e8173afdff",
 			"CALL runtime.printint(SB)",
-			"call 0x424080",
-			"callq 0x424080"},
+			"call runtime.printint",
+			"callq runtime.printint"},
 		{0x45069b, "488d0575d90100",
 			"LEAQ 0x1d975(IP), AX",
 			"lea rax, ptr [rip+0x1d975]",
@@ -56,10 +56,10 @@
 		if out := GoSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.goSyntax {
 			t.Errorf("GoSyntax: %q", out)
 		}
-		if out := IntelSyntax(inst, testCase.PC); out != testCase.intelSyntax {
+		if out := IntelSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.intelSyntax {
 			t.Errorf("IntelSyntax: %q expected: %q", out, testCase.intelSyntax)
 		}
-		if out := GNUSyntax(inst, testCase.PC); out != testCase.gnuSyntax {
+		if out := GNUSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.gnuSyntax {
 			t.Errorf("GNUSyntax: %q expected: %q", out, testCase.gnuSyntax)
 		}
 	}
diff --git a/x86/x86asm/gnu.go b/x86/x86asm/gnu.go
index 073b746..5bb54df 100644
--- a/x86/x86asm/gnu.go
+++ b/x86/x86asm/gnu.go
@@ -11,12 +11,16 @@
 
 // GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
 // This general form is often called ``AT&T syntax'' as a reference to AT&T System V Unix.
-func GNUSyntax(inst Inst, pc uint64) string {
+func GNUSyntax(inst Inst, pc uint64, symname SymLookup) string {
 	// Rewrite instruction to mimic GNU peculiarities.
 	// Note that inst has been passed by value and contains
 	// no pointers, so any changes we make here are local
 	// and will not propagate back out to the caller.
 
+	if symname == nil {
+		symname = func(uint64) (string, uint64) { return "", 0 }
+	}
+
 	// Adjust opcode [sic].
 	switch inst.Op {
 	case FDIV, FDIVR, FSUB, FSUBR, FDIVP, FDIVRP, FSUBP, FSUBRP:
@@ -403,7 +407,7 @@
 		if a == Imm(1) && (inst.Opcode>>24)&^1 == 0xD0 {
 			continue
 		}
-		args = append(args, gnuArg(&inst, pc, a, &usedPrefixes))
+		args = append(args, gnuArg(&inst, pc, symname, a, &usedPrefixes))
 	}
 
 	// The default is to print the arguments in reverse Intel order.
@@ -513,7 +517,7 @@
 // gnuArg returns the GNU syntax for the argument x from the instruction inst.
 // If *usedPrefixes is false and x is a Mem, then the formatting
 // includes any segment prefixes and sets *usedPrefixes to true.
-func gnuArg(inst *Inst, pc uint64, x Arg, usedPrefixes *bool) string {
+func gnuArg(inst *Inst, pc uint64, symname SymLookup, x Arg, usedPrefixes *bool) string {
 	if x == nil {
 		return "<nil>"
 	}
@@ -648,9 +652,21 @@
 			return fmt.Sprintf(".%+#x", int64(x))
 		} else {
 			addr := pc + uint64(inst.Len) + uint64(x)
-			return fmt.Sprintf("%#x", addr)
+			if s, base := symname(addr); s != "" && addr == base {
+				return fmt.Sprintf("%s", s)
+			} else {
+				addr := pc + uint64(inst.Len) + uint64(x)
+				return fmt.Sprintf("%#x", addr)
+			}
 		}
 	case Imm:
+		if s, base := symname(uint64(x)); s != "" {
+			suffix := ""
+			if uint64(x) != base {
+				suffix = fmt.Sprintf("%+d", uint64(x)-base)
+			}
+			return fmt.Sprintf("$%s%s", s, suffix)
+		}
 		if inst.Mode == 32 {
 			return fmt.Sprintf("$%#x", uint32(x))
 		}
diff --git a/x86/x86asm/intel.go b/x86/x86asm/intel.go
index 6b0e36a..4c32d9d 100644
--- a/x86/x86asm/intel.go
+++ b/x86/x86asm/intel.go
@@ -10,7 +10,11 @@
 )
 
 // IntelSyntax returns the Intel assembler syntax for the instruction, as defined by Intel's XED tool.
-func IntelSyntax(inst Inst, pc uint64) string {
+func IntelSyntax(inst Inst, pc uint64, symname SymLookup) string {
+	if symname == nil {
+		symname = func(uint64) (string, uint64) { return "", 0 }
+	}
+
 	var iargs []Arg
 	for _, a := range inst.Args {
 		if a == nil {
@@ -256,7 +260,7 @@
 		if a == nil {
 			break
 		}
-		args = append(args, intelArg(&inst, pc, a))
+		args = append(args, intelArg(&inst, pc, symname, a))
 	}
 
 	var op string
@@ -334,9 +338,16 @@
 	return prefix + op
 }
 
-func intelArg(inst *Inst, pc uint64, arg Arg) string {
+func intelArg(inst *Inst, pc uint64, symname SymLookup, arg Arg) string {
 	switch a := arg.(type) {
 	case Imm:
+		if s, base := symname(uint64(a)); s != "" {
+			suffix := ""
+			if uint64(a) != base {
+				suffix = fmt.Sprintf("%+d", uint64(a)-base)
+			}
+			return fmt.Sprintf("$%s%s", s, suffix)
+		}
 		if inst.Mode == 32 {
 			return fmt.Sprintf("%#x", uint32(a))
 		}
@@ -422,13 +433,13 @@
 		}
 		prefix += "["
 		if a.Base != 0 {
-			prefix += intelArg(inst, pc, a.Base)
+			prefix += intelArg(inst, pc, symname, a.Base)
 		}
 		if a.Scale != 0 && a.Index != 0 {
 			if a.Base != 0 {
 				prefix += "+"
 			}
-			prefix += fmt.Sprintf("%s*%d", intelArg(inst, pc, a.Index), a.Scale)
+			prefix += fmt.Sprintf("%s*%d", intelArg(inst, pc, symname, a.Index), a.Scale)
 		}
 		if a.Disp != 0 {
 			if prefix[len(prefix)-1] == '[' && (a.Disp >= 0 || int64(int32(a.Disp)) != a.Disp) {
@@ -444,7 +455,12 @@
 			return fmt.Sprintf(".%+#x", int64(a))
 		} else {
 			addr := pc + uint64(inst.Len) + uint64(a)
-			return fmt.Sprintf("%#x", addr)
+			if s, base := symname(addr); s != "" && addr == base {
+				return fmt.Sprintf("%s", s)
+			} else {
+				addr := pc + uint64(inst.Len) + uint64(a)
+				return fmt.Sprintf("%#x", addr)
+			}
 		}
 	case Reg:
 		if int(a) < len(intelReg) && intelReg[a] != "" {
diff --git a/x86/x86asm/plan9x.go b/x86/x86asm/plan9x.go
index 41cfc08..d5cf614 100644
--- a/x86/x86asm/plan9x.go
+++ b/x86/x86asm/plan9x.go
@@ -9,6 +9,8 @@
 	"strings"
 )
 
+type SymLookup func(uint64) (string, uint64)
+
 // GoSyntax returns the Go assembler syntax for the instruction.
 // The syntax was originally defined by Plan 9.
 // The pc is the program counter of the instruction, used for expanding
@@ -16,7 +18,7 @@
 // The symname function queries the symbol table for the program
 // being disassembled. Given a target address it returns the name and base
 // address of the symbol containing the target, if any; otherwise it returns "", 0.
-func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string {
+func GoSyntax(inst Inst, pc uint64, symname SymLookup) string {
 	if symname == nil {
 		symname = func(uint64) (string, uint64) { return "", 0 }
 	}