ppc64/ppc64asm: improve PCrel argument decoding

If an object is built for PIE, CALL opcodes will target the symbol's
local entry point. When disassembling, we should print the symbol
name if the target is the symbol+8.

The local entry offset on PPC64 is almost always 0 or 8. For pure go,
it is always 0 or 8 today.

If a call looks like it targets a local entry, print it as
"CALL symbol+8(SB)".

Change-Id: I72a2f1eaafd226ed5466384c63040d2f375a541f
Reviewed-on: https://go-review.googlesource.com/c/arch/+/432166
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Paul Murphy <murp@ibm.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/ppc64/ppc64asm/decode_test.go b/ppc64/ppc64asm/decode_test.go
index 3337312..83a3acd 100644
--- a/ppc64/ppc64asm/decode_test.go
+++ b/ppc64/ppc64asm/decode_test.go
@@ -31,6 +31,15 @@
 	}
 }
 
+// Provide a fake symbol to verify PCrel argument decoding.
+func symlookup(pc uint64) (string, uint64) {
+	foopc := uint64(0x100000)
+	if pc >= foopc && pc < foopc+0x10 {
+		return "foo", foopc
+	}
+	return "", 0
+}
+
 func decode(data []byte, t *testing.T, filename string) {
 	all := string(data)
 	// Simulate PC based on number of instructions found in the test file.
@@ -68,7 +77,14 @@
 			case "gnu":
 				out = GNUSyntax(inst, pc)
 			case "plan9":
-				out = GoSyntax(inst, pc, nil)
+				pc := pc
+				// Hack: Setting PC to 0 effectively transforms the PC relative address
+				// of CALL (bl) into an absolute address when decoding in GoSyntax. This
+				// simplifies the testing of symbol lookups via symlookup above.
+				if inst.Op == BL {
+					pc = 0
+				}
+				out = GoSyntax(inst, pc, symlookup)
 			default:
 				t.Errorf("unknown syntax %q", syntax)
 				continue
diff --git a/ppc64/ppc64asm/objdump_test.go b/ppc64/ppc64asm/objdump_test.go
index e89146e..414fada 100644
--- a/ppc64/ppc64asm/objdump_test.go
+++ b/ppc64/ppc64asm/objdump_test.go
@@ -47,6 +47,10 @@
 		return true
 	case SYNC, WAIT, RFEBB: // ISA 3.1 adds more bits and extended mnemonics for these book ii instructions.
 		return true
+	case BL:
+		// TODO: Ignore these for now. The output format from gnu objdump is dependent on more than the
+		// instruction itself e.g: decode(48100009) = "bl 0x100008", 4, want "bl .+0x100008", 4
+		return true
 	}
 
 	if len(dec.enc) >= 4 {
diff --git a/ppc64/ppc64asm/plan9.go b/ppc64/ppc64asm/plan9.go
index 5fe4077..4bd1c7f 100644
--- a/ppc64/ppc64asm/plan9.go
+++ b/ppc64/ppc64asm/plan9.go
@@ -168,8 +168,9 @@
 }
 
 // plan9Arg formats arg (which is the argIndex's arg in inst) according to Plan 9 rules.
+//
 // NOTE: because Plan9Syntax is the only caller of this func, and it receives a copy
-//       of inst, it's ok to modify inst.Args here.
+// of inst, it's ok to modify inst.Args here.
 func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64) (string, uint64)) string {
 	// special cases for load/store instructions
 	if _, ok := arg.(Offset); ok {
@@ -211,9 +212,16 @@
 		return fmt.Sprintf("SPR(%d)", int(arg))
 	case PCRel:
 		addr := pc + uint64(int64(arg))
-		if s, base := symname(addr); s != "" && base == addr {
+		s, base := symname(addr)
+		if s != "" && addr == base {
 			return fmt.Sprintf("%s(SB)", s)
 		}
+		if inst.Op == BL && s != "" && (addr-base) == 8 {
+			// When decoding an object built for PIE, a CALL targeting
+			// a global entry point will be adjusted to the local entry
+			// if any. For now, assume any symname+8 PC is a local call.
+			return fmt.Sprintf("%s+%d(SB)", s, addr-base)
+		}
 		return fmt.Sprintf("%#x", addr)
 	case Label:
 		return fmt.Sprintf("%#x", int(arg))
diff --git a/ppc64/ppc64asm/testdata/decode.txt b/ppc64/ppc64asm/testdata/decode.txt
index 54fcafd..7bf4355 100644
--- a/ppc64/ppc64asm/testdata/decode.txt
+++ b/ppc64/ppc64asm/testdata/decode.txt
@@ -469,6 +469,9 @@
 7c6802a6|	plan9	MOVD LR,R3
 7c6902a6|	plan9	MOVD CTR,R3
 4c8c0000|	plan9	MOVFL CR3,CR1
+48100001|	plan9	CALL foo(SB)
+48100009|	plan9	CALL foo+8(SB)
+4810000d|	plan9	CALL 0x10000c
 7c6803a6|	gnu	mtlr r3
 7c6802a6|	gnu	mflr r3
 7c6903a6|	gnu	mtctr r3