x86: IntelSyntax, GNUSyntax: print Rel arguments as absolute

PC relative addresses are hard to understand and this is already done
in GoSyntax.

Change-Id: I7edcffb54059291c638b633a2e62f6555f7df79c
Reviewed-on: https://go-review.googlesource.com/45097
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/x86/x86asm/decode_test.go b/x86/x86asm/decode_test.go
index b6098b8..fae8490 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)
+				out = GNUSyntax(inst, 0)
 			case "intel":
-				out = IntelSyntax(inst)
+				out = IntelSyntax(inst, 0)
 			case "plan9": // [sic]
 				out = GoSyntax(inst, 0, nil)
 			default:
diff --git a/x86/x86asm/ext_test.go b/x86/x86asm/ext_test.go
index eadfd71..a6097e1 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)
+			text = GNUSyntax(inst, 0)
 		case "intel":
-			text = IntelSyntax(inst)
+			text = IntelSyntax(inst, 0)
 		case "plan9": // [sic]
 			text = GoSyntax(inst, 0, nil)
 		default:
diff --git a/x86/x86asm/format_test.go b/x86/x86asm/format_test.go
new file mode 100644
index 0000000..1f53638
--- /dev/null
+++ b/x86/x86asm/format_test.go
@@ -0,0 +1,66 @@
+// Copyright 2017 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 x86asm
+
+import (
+	"encoding/hex"
+	"testing"
+)
+
+func testFormattingSymname(addr uint64) (string, uint64) {
+	switch addr {
+	case 0x424080:
+		return "runtime.printint", 0x424080
+	}
+	return "", 0
+}
+
+func TestFormatting(t *testing.T) {
+	testCases := []struct {
+		PC    uint64
+		bytes string
+
+		goSyntax, intelSyntax, gnuSyntax string
+	}{
+		{0x4816b2, "0f8677010000",
+			"JBE 0x48182f",
+			"jbe 0x48182f",
+			"jbe 0x48182f"},
+		{0x45065b, "488b442408",
+			"MOVQ 0x8(SP), AX",
+			"mov rax, qword ptr [rsp+0x8]",
+			"mov 0x8(%rsp),%rax"},
+		{0x450678, "488b05e9790700",
+			"MOVQ 0x779e9(IP), AX",
+			"mov rax, qword ptr [rip+0x779e9]",
+			"mov 0x779e9(%rip),%rax"},
+		{0x450664, "e8173afdff",
+			"CALL runtime.printint(SB)",
+			"call 0x424080",
+			"callq 0x424080"},
+		{0x45069b, "488d0575d90100",
+			"LEAQ 0x1d975(IP), AX",
+			"lea rax, ptr [rip+0x1d975]",
+			"lea 0x1d975(%rip),%rax"},
+	}
+
+	for _, testCase := range testCases {
+		t.Logf("%#x %s %s", testCase.PC, testCase.bytes, testCase.goSyntax)
+		bs, _ := hex.DecodeString(testCase.bytes)
+		inst, err := Decode(bs, 64)
+		if err != nil {
+			t.Errorf("decode error %v", err)
+		}
+		if out := GoSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.goSyntax {
+			t.Errorf("GoSyntax: %q", out)
+		}
+		if out := IntelSyntax(inst, testCase.PC); out != testCase.intelSyntax {
+			t.Errorf("IntelSyntax: %q expected: %q", out, testCase.intelSyntax)
+		}
+		if out := GNUSyntax(inst, testCase.PC); 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 728e5d1..073b746 100644
--- a/x86/x86asm/gnu.go
+++ b/x86/x86asm/gnu.go
@@ -11,7 +11,7 @@
 
 // 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) string {
+func GNUSyntax(inst Inst, pc uint64) 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
@@ -403,7 +403,7 @@
 		if a == Imm(1) && (inst.Opcode>>24)&^1 == 0xD0 {
 			continue
 		}
-		args = append(args, gnuArg(&inst, a, &usedPrefixes))
+		args = append(args, gnuArg(&inst, pc, a, &usedPrefixes))
 	}
 
 	// The default is to print the arguments in reverse Intel order.
@@ -513,7 +513,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, x Arg, usedPrefixes *bool) string {
+func gnuArg(inst *Inst, pc uint64, x Arg, usedPrefixes *bool) string {
 	if x == nil {
 		return "<nil>"
 	}
@@ -644,7 +644,12 @@
 		}
 		return fmt.Sprintf("%s%s(%s,%s,%d)", seg, disp, base, index, x.Scale)
 	case Rel:
-		return fmt.Sprintf(".%+#x", int32(x))
+		if pc == 0 {
+			return fmt.Sprintf(".%+#x", int64(x))
+		} else {
+			addr := pc + uint64(inst.Len) + uint64(x)
+			return fmt.Sprintf("%#x", addr)
+		}
 	case Imm:
 		if inst.Mode == 32 {
 			return fmt.Sprintf("$%#x", uint32(x))
diff --git a/x86/x86asm/intel.go b/x86/x86asm/intel.go
index 63fa2cf..6b0e36a 100644
--- a/x86/x86asm/intel.go
+++ b/x86/x86asm/intel.go
@@ -10,7 +10,7 @@
 )
 
 // IntelSyntax returns the Intel assembler syntax for the instruction, as defined by Intel's XED tool.
-func IntelSyntax(inst Inst) string {
+func IntelSyntax(inst Inst, pc uint64) string {
 	var iargs []Arg
 	for _, a := range inst.Args {
 		if a == nil {
@@ -256,7 +256,7 @@
 		if a == nil {
 			break
 		}
-		args = append(args, intelArg(&inst, a))
+		args = append(args, intelArg(&inst, pc, a))
 	}
 
 	var op string
@@ -334,7 +334,7 @@
 	return prefix + op
 }
 
-func intelArg(inst *Inst, arg Arg) string {
+func intelArg(inst *Inst, pc uint64, arg Arg) string {
 	switch a := arg.(type) {
 	case Imm:
 		if inst.Mode == 32 {
@@ -422,13 +422,13 @@
 		}
 		prefix += "["
 		if a.Base != 0 {
-			prefix += intelArg(inst, a.Base)
+			prefix += intelArg(inst, pc, a.Base)
 		}
 		if a.Scale != 0 && a.Index != 0 {
 			if a.Base != 0 {
 				prefix += "+"
 			}
-			prefix += fmt.Sprintf("%s*%d", intelArg(inst, a.Index), a.Scale)
+			prefix += fmt.Sprintf("%s*%d", intelArg(inst, pc, a.Index), a.Scale)
 		}
 		if a.Disp != 0 {
 			if prefix[len(prefix)-1] == '[' && (a.Disp >= 0 || int64(int32(a.Disp)) != a.Disp) {
@@ -440,7 +440,12 @@
 		prefix += "]"
 		return prefix
 	case Rel:
-		return fmt.Sprintf(".%+#x", int64(a))
+		if pc == 0 {
+			return fmt.Sprintf(".%+#x", int64(a))
+		} else {
+			addr := pc + uint64(inst.Len) + uint64(a)
+			return fmt.Sprintf("%#x", addr)
+		}
 	case Reg:
 		if int(a) < len(intelReg) && intelReg[a] != "" {
 			switch inst.Op {