Skip to content

Commit 30a63ec

Browse files
committed
runtime: restore r2 when restoring state from gobuf in gogo on ppc64x
When using plugins with goroutines calling cgo, we hit a case where an intermittent SIGSEGV occurs when referencing an address that is based on r2 (TOC address). When the failure can be generated in gdb, the contents of r2 is wrong even though the value in the current stack's slot for r2 is correct. So that means it somehow switched to start running the code in this function without passing through the beginning of the function which had the correct value of r2 and stored it there. It was noted that in runtime.gogo when the state is restored from gobuf, r2 is not restored from its slot on the stack. Adding the instruction to restore r2 prevents the SIGSEGV. This adds a testcase under testplugin which reproduces the problem if the program is run multiple times. The team who reported this problem has verified it fixes the issue on their larger, more complex application. Fixes golang#25756 Change-Id: I6028b6f1f8775d5c23f4ebb57ae273330a28eb8f Reviewed-on: https://go-review.googlesource.com/117515 Run-TryBot: Lynn Boger <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 7ba0c62 commit 30a63ec

File tree

6 files changed

+165
-1
lines changed

6 files changed

+165
-1
lines changed
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Run the game of life in C using Go for parallelization.
6+
7+
package main
8+
9+
import (
10+
"flag"
11+
"fmt"
12+
"plugin"
13+
)
14+
15+
const MAXDIM = 100
16+
17+
var dim = flag.Int("dim", 16, "board dimensions")
18+
var gen = flag.Int("gen", 10, "generations")
19+
20+
func main() {
21+
flag.Parse()
22+
23+
var a [MAXDIM * MAXDIM]int32
24+
for i := 2; i < *dim; i += 8 {
25+
for j := 2; j < *dim-3; j += 8 {
26+
for y := 0; y < 3; y++ {
27+
a[i**dim+j+y] = 1
28+
}
29+
}
30+
}
31+
32+
p, err := plugin.Open("life.so")
33+
if err != nil {
34+
panic(err)
35+
}
36+
f, err := p.Lookup("Run")
37+
if err != nil {
38+
panic(err)
39+
}
40+
f.(func(int, int, int, []int32))(*gen, *dim, *dim, a[:])
41+
42+
for i := 0; i < *dim; i++ {
43+
for j := 0; j < *dim; j++ {
44+
if a[i**dim+j] == 0 {
45+
fmt.Print(" ")
46+
} else {
47+
fmt.Print("X")
48+
}
49+
}
50+
fmt.Print("\n")
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2010 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
#include <assert.h>
6+
#include "life.h"
7+
#include "_cgo_export.h"
8+
9+
const int MYCONST = 0;
10+
11+
// Do the actual manipulation of the life board in C. This could be
12+
// done easily in Go, we are just using C for demonstration
13+
// purposes.
14+
void
15+
Step(int x, int y, int *a, int *n)
16+
{
17+
struct GoStart_return r;
18+
19+
// Use Go to start 4 goroutines each of which handles 1/4 of the
20+
// board.
21+
r = GoStart(0, x, y, 0, x / 2, 0, y / 2, a, n);
22+
assert(r.r0 == 0 && r.r1 == 100); // test multiple returns
23+
r = GoStart(1, x, y, x / 2, x, 0, y / 2, a, n);
24+
assert(r.r0 == 1 && r.r1 == 101); // test multiple returns
25+
GoStart(2, x, y, 0, x / 2, y / 2, y, a, n);
26+
GoStart(3, x, y, x / 2, x, y / 2, y, a, n);
27+
GoWait(0);
28+
GoWait(1);
29+
GoWait(2);
30+
GoWait(3);
31+
}
32+
33+
// The actual computation. This is called in parallel.
34+
void
35+
DoStep(int xdim, int ydim, int xstart, int xend, int ystart, int yend, int *a, int *n)
36+
{
37+
int x, y, c, i, j;
38+
39+
for(x = xstart; x < xend; x++) {
40+
for(y = ystart; y < yend; y++) {
41+
c = 0;
42+
for(i = -1; i <= 1; i++) {
43+
for(j = -1; j <= 1; j++) {
44+
if(x+i >= 0 && x+i < xdim &&
45+
y+j >= 0 && y+j < ydim &&
46+
(i != 0 || j != 0))
47+
c += a[(x+i)*xdim + (y+j)] != 0;
48+
}
49+
}
50+
if(c == 3 || (c == 2 && a[x*xdim + y] != 0))
51+
n[x*xdim + y] = 1;
52+
else
53+
n[x*xdim + y] = 0;
54+
}
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2010 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
// #include "life.h"
8+
import "C"
9+
10+
import "unsafe"
11+
12+
func Run(gen, x, y int, a []int32) {
13+
n := make([]int32, x*y)
14+
for i := 0; i < gen; i++ {
15+
C.Step(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&a[0])), (*C.int)(unsafe.Pointer(&n[0])))
16+
copy(a, n)
17+
}
18+
}
19+
20+
// Keep the channels visible from Go.
21+
var chans [4]chan bool
22+
23+
//export GoStart
24+
// Double return value is just for testing.
25+
func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) {
26+
c := make(chan bool, int(C.MYCONST))
27+
go func() {
28+
C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n)
29+
c <- true
30+
}()
31+
chans[i] = c
32+
return int(i), int(i + 100)
33+
}
34+
35+
//export GoWait
36+
func GoWait(i C.int) {
37+
<-chans[i]
38+
chans[i] = nil
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2010 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
extern void Step(int, int, int *, int *);
6+
extern void DoStep(int, int, int, int, int, int, int *, int *);
7+
extern const int MYCONST;

misc/cgo/testplugin/test.bash

+10-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ goos=$(go env GOOS)
1515
goarch=$(go env GOARCH)
1616

1717
function cleanup() {
18-
rm -f plugin*.so unnamed*.so iface*.so issue*
18+
rm -f plugin*.so unnamed*.so iface*.so life.so issue*
1919
rm -rf host pkg sub iface
2020
}
2121
trap cleanup EXIT
@@ -90,3 +90,12 @@ GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue22295 src/issue22295.pkg/m
9090
GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o issue24351.so src/issue24351/plugin.go
9191
GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue24351 src/issue24351/main.go
9292
./issue24351
93+
94+
# Test for issue 25756
95+
GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o life.so issue25756/plugin
96+
GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue25756 src/issue25756/main.go
97+
# Fails intermittently, but 20 runs should cause the failure
98+
for i in `seq 1 20`;
99+
do
100+
./issue25756 > /dev/null
101+
done

src/runtime/asm_ppc64x.s

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ TEXT runtime·gogo(SB), NOSPLIT, $16-8
139139
MOVD 0(g), R4
140140
MOVD gobuf_sp(R5), R1
141141
MOVD gobuf_lr(R5), R31
142+
MOVD 24(R1), R2 // restore R2
142143
MOVD R31, LR
143144
MOVD gobuf_ret(R5), R3
144145
MOVD gobuf_ctxt(R5), R11

0 commit comments

Comments
 (0)