internal/cpu, runtime, runtime/pprof: handle function descriptors

When using PPC64 ELF ABI v1 a function address is not a PC, but is the
address of a function descriptor.  The first field in the function
descriptor is the actual PC (see
http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUNC-DES).
The libbacktrace library knows about this, and libgo uses actual PC
values consistently except for the helper function funcPC that appears
in both runtime and runtime/pprof.

This patch fixes funcPC by recording, in the internal/cpu package,
whether function descriptors are being used.  We have to check for
function descriptors using a C compiler check, because GCC can be
configured using --with-abi to select the ELF ABI to use.

Fixes https://gcc.gnu.org/PR89172

Change-Id: I97d7bfbfaf35b3909c5f68f183ab6b1fbcac7f13
Reviewed-on: https://go-review.googlesource.com/c/162978
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/libgo/Makefile.am b/libgo/Makefile.am
index 1f78111..aeaa203 100644
--- a/libgo/Makefile.am
+++ b/libgo/Makefile.am
@@ -539,6 +539,7 @@
 	rm -f cpugen.go.tmp
 	echo "package cpu" > cpugen.go.tmp
 	echo "const CacheLinePadSize = `$(SHELL) $(srcdir)/goarch.sh $(GOARCH) cachelinesize`" >> cpugen.go.tmp
+	echo "const FunctionDescriptors = $(FUNCTION_DESCRIPTORS)" >> cpugen.go.tmp
 	$(SHELL) $(srcdir)/mvifdiff.sh cpugen.go.tmp cpugen.go
 	$(STAMP) $@
 
diff --git a/libgo/Makefile.in b/libgo/Makefile.in
index 0b26158..7f39891 100644
--- a/libgo/Makefile.in
+++ b/libgo/Makefile.in
@@ -397,6 +397,7 @@
 EGREP = @EGREP@
 EXEEXT = @EXEEXT@
 FGREP = @FGREP@
+FUNCTION_DESCRIPTORS = @FUNCTION_DESCRIPTORS@
 GOARCH = @GOARCH@
 GOC = @GOC@
 GOFLAGS = @GOFLAGS@
@@ -2635,6 +2636,7 @@
 	rm -f cpugen.go.tmp
 	echo "package cpu" > cpugen.go.tmp
 	echo "const CacheLinePadSize = `$(SHELL) $(srcdir)/goarch.sh $(GOARCH) cachelinesize`" >> cpugen.go.tmp
+	echo "const FunctionDescriptors = $(FUNCTION_DESCRIPTORS)" >> cpugen.go.tmp
 	$(SHELL) $(srcdir)/mvifdiff.sh cpugen.go.tmp cpugen.go
 	$(STAMP) $@
 
diff --git a/libgo/configure b/libgo/configure
index 0b8ebce..06b68b0 100755
--- a/libgo/configure
+++ b/libgo/configure
@@ -661,6 +661,7 @@
 GO_SYSCALL_OS_FILE
 GO_LIBCALL_OS_ARCH_FILE
 GO_LIBCALL_OS_FILE
+FUNCTION_DESCRIPTORS
 ALLGOARCHFAMILY
 ALLGOARCH
 GOARCH
@@ -11343,7 +11344,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11346 "configure"
+#line 11347 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11449,7 +11450,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11452 "configure"
+#line 11453 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -14088,6 +14089,27 @@
 
 
 
+FUNCTION_DESCRIPTORS=false
+case ${host} in
+  rs6000*-*-* | powerpc*-*-*)
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#if _CALL_ELF == 1
+#error descriptors
+#endif
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  FUNCTION_DESCRIPTORS=false
+else
+  FUNCTION_DESCRIPTORS=true
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    ;;
+esac
+
+
 GO_LIBCALL_OS_FILE=
 GO_LIBCALL_OS_ARCH_FILE=
 GO_SYSCALL_OS_FILE=
diff --git a/libgo/configure.ac b/libgo/configure.ac
index 44b5948..03c07fe 100644
--- a/libgo/configure.ac
+++ b/libgo/configure.ac
@@ -353,6 +353,20 @@
 AC_SUBST(ALLGOARCH)
 AC_SUBST(ALLGOARCHFAMILY)
 
+FUNCTION_DESCRIPTORS=false
+case ${host} in
+  rs6000*-*-* | powerpc*-*-*)
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+#if _CALL_ELF == 1
+#error descriptors
+#endif
+])],
+	[FUNCTION_DESCRIPTORS=false],
+	[FUNCTION_DESCRIPTORS=true])
+    ;;
+esac
+AC_SUBST(FUNCTION_DESCRIPTORS)
+
 dnl Some files are only present when needed for specific architectures.
 GO_LIBCALL_OS_FILE=
 GO_LIBCALL_OS_ARCH_FILE=
diff --git a/libgo/go/runtime/pprof/proto.go b/libgo/go/runtime/pprof/proto.go
index b82e738..27cd09e 100644
--- a/libgo/go/runtime/pprof/proto.go
+++ b/libgo/go/runtime/pprof/proto.go
@@ -8,6 +8,7 @@
 	"bytes"
 	"compress/gzip"
 	"fmt"
+	internalcpu "internal/cpu"
 	"io"
 	"io/ioutil"
 	"runtime"
@@ -28,7 +29,14 @@
 		data unsafe.Pointer
 	}
 	i := (*iface)(unsafe.Pointer(&f))
-	return **(**uintptr)(i.data)
+	r := **(**uintptr)(i.data)
+	if internalcpu.FunctionDescriptors {
+		// With PPC64 ELF ABI v1 function descriptors the
+		// function address is a pointer to a struct whose
+		// first field is the actual PC.
+		r = *(*uintptr)(unsafe.Pointer(r))
+	}
+	return r
 }
 
 // A profileBuilder writes a profile incrementally from a
diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go
index 1c944d6..0e6c9e1 100644
--- a/libgo/go/runtime/proc.go
+++ b/libgo/go/runtime/proc.go
@@ -446,7 +446,14 @@
 //go:nosplit
 func funcPC(f interface{}) uintptr {
 	i := (*iface)(unsafe.Pointer(&f))
-	return **(**uintptr)(i.data)
+	r := **(**uintptr)(i.data)
+	if cpu.FunctionDescriptors {
+		// With PPC64 ELF ABI v1 function descriptors the
+		// function address is a pointer to a struct whose
+		// first field is the actual PC.
+		r = *(*uintptr)(unsafe.Pointer(r))
+	}
+	return r
 }
 
 func lockedOSThread() bool {
diff --git a/libgo/testsuite/Makefile.in b/libgo/testsuite/Makefile.in
index 035a9fa..1307589 100644
--- a/libgo/testsuite/Makefile.in
+++ b/libgo/testsuite/Makefile.in
@@ -157,6 +157,7 @@
 EGREP = @EGREP@
 EXEEXT = @EXEEXT@
 FGREP = @FGREP@
+FUNCTION_DESCRIPTORS = @FUNCTION_DESCRIPTORS@
 GOARCH = @GOARCH@
 GOC = @GOC@
 GOFLAGS = @GOFLAGS@