diff options
Diffstat (limited to 'src/runtime')
| -rw-r--r-- | src/runtime/cgo.go | 13 | ||||
| -rw-r--r-- | src/runtime/crash_cgo_test.go | 11 | ||||
| -rw-r--r-- | src/runtime/linkname.go | 1 | ||||
| -rw-r--r-- | src/runtime/testdata/testprogcgo/cgonocallback.go | 3 | ||||
| -rw-r--r-- | src/runtime/testdata/testprogcgo/cgonoescape.go | 3 | ||||
| -rw-r--r-- | src/runtime/testdata/testprogcgo/issue63739.go | 59 |
6 files changed, 84 insertions, 6 deletions
diff --git a/src/runtime/cgo.go b/src/runtime/cgo.go index 8285d87fcf..eca905bad9 100644 --- a/src/runtime/cgo.go +++ b/src/runtime/cgo.go @@ -72,11 +72,20 @@ var cgoHasExtraM bool // cgoUse should not actually be called (see cgoAlwaysFalse). func cgoUse(any) { throw("cgoUse should not be called") } +// cgoKeepAlive is called by cgo-generated code (using go:linkname to get at +// an unexported name). This call keeps its argument alive until the call site; +// cgo emits the call after the last possible use of the argument by C code. +// cgoKeepAlive is marked in the cgo-generated code as //go:noescape, so +// unlike cgoUse it does not force the argument to escape to the heap. +// This is used to implement the #cgo noescape directive. +func cgoKeepAlive(any) { throw("cgoKeepAlive should not be called") } + // cgoAlwaysFalse is a boolean value that is always false. -// The cgo-generated code says if cgoAlwaysFalse { cgoUse(p) }. +// The cgo-generated code says if cgoAlwaysFalse { cgoUse(p) }, +// or if cgoAlwaysFalse { cgoKeepAlive(p) }. // The compiler cannot see that cgoAlwaysFalse is always false, // so it emits the test and keeps the call, giving the desired -// escape analysis result. The test is cheaper than the call. +// escape/alive analysis result. The test is cheaper than the call. var cgoAlwaysFalse bool var cgo_yield = &_cgo_yield diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index 57111c9aef..d164a07047 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -750,7 +750,6 @@ func TestNeedmDeadlock(t *testing.T) { } func TestCgoNoCallback(t *testing.T) { - t.Skip("TODO(#56378): enable in Go 1.23") got := runTestProg(t, "testprogcgo", "CgoNoCallback") want := "function marked with #cgo nocallback called back into Go" if !strings.Contains(got, want) { @@ -759,7 +758,6 @@ func TestCgoNoCallback(t *testing.T) { } func TestCgoNoEscape(t *testing.T) { - t.Skip("TODO(#56378): enable in Go 1.23") got := runTestProg(t, "testprogcgo", "CgoNoEscape") want := "OK\n" if got != want { @@ -767,6 +765,15 @@ func TestCgoNoEscape(t *testing.T) { } } +// Issue #63739. +func TestCgoEscapeWithMultiplePointers(t *testing.T) { + got := runTestProg(t, "testprogcgo", "CgoEscapeWithMultiplePointers") + want := "OK\n" + if got != want { + t.Fatalf("output is %s; want %s", got, want) + } +} + func TestCgoTracebackGoroutineProfile(t *testing.T) { output := runTestProg(t, "testprogcgo", "GoroutineProfile") want := "OK\n" diff --git a/src/runtime/linkname.go b/src/runtime/linkname.go index dd7f674251..eec190c39c 100644 --- a/src/runtime/linkname.go +++ b/src/runtime/linkname.go @@ -13,6 +13,7 @@ import _ "unsafe" //go:linkname _cgo_panic_internal //go:linkname cgoAlwaysFalse //go:linkname cgoUse +//go:linkname cgoKeepAlive //go:linkname cgoCheckPointer //go:linkname cgoCheckResult //go:linkname cgoNoCallback diff --git a/src/runtime/testdata/testprogcgo/cgonocallback.go b/src/runtime/testdata/testprogcgo/cgonocallback.go index c13bf271a4..8cbbfd1957 100644 --- a/src/runtime/testdata/testprogcgo/cgonocallback.go +++ b/src/runtime/testdata/testprogcgo/cgonocallback.go @@ -8,7 +8,8 @@ package main // But it do callback to go in this test, Go should crash here. /* -// TODO(#56378): #cgo nocallback runCShouldNotCallback +#cgo nocallback runCShouldNotCallback + extern void runCShouldNotCallback(); */ import "C" diff --git a/src/runtime/testdata/testprogcgo/cgonoescape.go b/src/runtime/testdata/testprogcgo/cgonoescape.go index f5eebac677..2b7c7a9c55 100644 --- a/src/runtime/testdata/testprogcgo/cgonoescape.go +++ b/src/runtime/testdata/testprogcgo/cgonoescape.go @@ -13,7 +13,8 @@ package main // 2. less than 100 new allocated heap objects after invoking withoutNoEscape 100 times. /* -// TODO(#56378): #cgo noescape runCWithNoEscape +#cgo noescape runCWithNoEscape +#cgo nocallback runCWithNoEscape void runCWithNoEscape(void *p) { } diff --git a/src/runtime/testdata/testprogcgo/issue63739.go b/src/runtime/testdata/testprogcgo/issue63739.go new file mode 100644 index 0000000000..dbe37b6d0e --- /dev/null +++ b/src/runtime/testdata/testprogcgo/issue63739.go @@ -0,0 +1,59 @@ +// Copyright 2020 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 main + +// This is for issue #63739. +// Ensure that parameters are kept alive until the end of the C call. If not, +// then a stack copy at just the right time while calling into C might think +// that any stack pointers are not alive and fail to update them, causing the C +// function to see the old, no longer correct, pointer values. + +/* +int add_from_multiple_pointers(int *a, int *b, int *c) { + *a = *a + 1; + *b = *b + 1; + *c = *c + 1; + return *a + *b + *c; +} +#cgo noescape add_from_multiple_pointers +#cgo nocallback add_from_multiple_pointers +*/ +import "C" + +import ( + "fmt" +) + +const ( + maxStack = 1024 +) + +func init() { + register("CgoEscapeWithMultiplePointers", CgoEscapeWithMultiplePointers) +} + +func CgoEscapeWithMultiplePointers() { + stackGrow(maxStack) + fmt.Println("OK") +} + +//go:noinline +func testCWithMultiplePointers() { + var a C.int = 1 + var b C.int = 2 + var c C.int = 3 + v := C.add_from_multiple_pointers(&a, &b, &c) + if v != 9 || a != 2 || b != 3 || c != 4 { + fmt.Printf("%d + %d + %d != %d\n", a, b, c, v) + } +} + +func stackGrow(n int) { + if n == 0 { + return + } + testCWithMultiplePointers() + stackGrow(n - 1) +} |
