This patch changes libgo to handle function descriptors correctly. 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. This fixes GCC PR 89172. Bootstrapped and ran Go tests on x86_64-pc-linux-gnu and ppc64-linux-gnu. Committed to mainline. Ian
Index: gcc/go/gofrontend/MERGE =================================================================== --- gcc/go/gofrontend/MERGE (revision 269258) +++ gcc/go/gofrontend/MERGE (working copy) @@ -1,4 +1,4 @@ -c9581de3804f94c5a74ce14befce5c57368722b9 +74533ed435a1a77e6f9ec8f6cf5db1695c2568e8 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. Index: libgo/Makefile.am =================================================================== --- libgo/Makefile.am (revision 269196) +++ libgo/Makefile.am (working copy) @@ -539,6 +539,7 @@ s-cpu: Makefile 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) $@ Index: libgo/configure.ac =================================================================== --- libgo/configure.ac (revision 269196) +++ libgo/configure.ac (working copy) @@ -353,6 +353,20 @@ AC_SUBST(GOARCH) 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= Index: libgo/go/runtime/pprof/proto.go =================================================================== --- libgo/go/runtime/pprof/proto.go (revision 269196) +++ libgo/go/runtime/pprof/proto.go (working copy) @@ -8,6 +8,7 @@ import ( "bytes" "compress/gzip" "fmt" + internalcpu "internal/cpu" "io" "io/ioutil" "runtime" @@ -28,7 +29,14 @@ func funcPC(f interface{}) uintptr { 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 Index: libgo/go/runtime/proc.go =================================================================== --- libgo/go/runtime/proc.go (revision 269196) +++ libgo/go/runtime/proc.go (working copy) @@ -446,7 +446,14 @@ func releaseSudog(s *sudog) { //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 {