aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmisc/cgo/testsanitizers/test.bash10
-rw-r--r--misc/cgo/testsanitizers/tsan4.go34
-rw-r--r--src/cmd/cgo/out.go69
-rw-r--r--src/runtime/cgo.go4
-rw-r--r--src/runtime/cgo/callbacks.go12
-rw-r--r--src/runtime/cgo/gcc_util.c25
-rw-r--r--src/runtime/cgocall.go19
-rw-r--r--src/runtime/proc.go6
8 files changed, 105 insertions, 74 deletions
diff --git a/misc/cgo/testsanitizers/test.bash b/misc/cgo/testsanitizers/test.bash
index 8718815d3e..c30df3b6c2 100755
--- a/misc/cgo/testsanitizers/test.bash
+++ b/misc/cgo/testsanitizers/test.bash
@@ -144,6 +144,16 @@ if test "$tsan" = "yes"; then
status=1
fi
+ if ! go run tsan4.go 2>$err; then
+ cat $err
+ echo "FAIL: tsan4"
+ status=1
+ elif grep -i warning $err >/dev/null 2>&1; then
+ cat $err
+ echo "FAIL: tsan4"
+ status=1
+ fi
+
rm -f $err
fi
diff --git a/misc/cgo/testsanitizers/tsan4.go b/misc/cgo/testsanitizers/tsan4.go
new file mode 100644
index 0000000000..f0c76d8411
--- /dev/null
+++ b/misc/cgo/testsanitizers/tsan4.go
@@ -0,0 +1,34 @@
+// Copyright 2016 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
+
+// Check that calls to C.malloc/C.free do not trigger TSAN false
+// positive reports.
+
+// #cgo CFLAGS: -fsanitize=thread
+// #cgo LDFLAGS: -fsanitize=thread
+// #include <stdlib.h>
+import "C"
+
+import (
+ "runtime"
+ "sync"
+)
+
+func main() {
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 100; i++ {
+ p := C.malloc(C.size_t(i * 10))
+ runtime.Gosched()
+ C.free(p)
+ }
+ }()
+ }
+ wg.Wait()
+}
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 256b059e57..5d6930d3ea 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -175,10 +175,11 @@ func (p *Package) writeDefs() {
}
fmt.Fprintf(fgo2, "\n")
+ callsMalloc := false
for _, key := range nameKeys(p.Name) {
n := p.Name[key]
if n.FuncType != nil {
- p.writeDefsFunc(fgo2, n)
+ p.writeDefsFunc(fgo2, n, &callsMalloc)
}
}
@@ -189,6 +190,12 @@ func (p *Package) writeDefs() {
} else {
p.writeExports(fgo2, fm, fgcc, fgcch)
}
+
+ if callsMalloc && !*gccgo {
+ fmt.Fprint(fgo2, strings.Replace(cMallocDefGo, "PREFIX", cPrefix, -1))
+ fmt.Fprint(fgcc, strings.Replace(strings.Replace(cMallocDefC, "PREFIX", cPrefix, -1), "PACKED", p.packedAttribute(), -1))
+ }
+
if err := fgcc.Close(); err != nil {
fatalf("%s", err)
}
@@ -352,7 +359,7 @@ func (p *Package) structType(n *Name) (string, int64) {
return buf.String(), off
}
-func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name) {
+func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name, callsMalloc *bool) {
name := n.Go
gtype := n.FuncType.Go
void := gtype.Results == nil || len(gtype.Results.List) == 0
@@ -441,6 +448,9 @@ func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name) {
if inProlog {
fmt.Fprint(fgo2, builtinDefs[name])
+ if strings.Contains(builtinDefs[name], "_cgo_cmalloc") {
+ *callsMalloc = true
+ }
return
}
@@ -712,11 +722,13 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
p.writeExportHeader(fgcch)
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
+ fmt.Fprintf(fgcc, "#include <stdlib.h>\n")
fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n\n")
fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *, int, __SIZE_TYPE__), void *, int, __SIZE_TYPE__);\n")
fmt.Fprintf(fgcc, "extern __SIZE_TYPE__ _cgo_wait_runtime_init_done();\n")
fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n")
+ fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);")
fmt.Fprintf(fgcc, "%s\n", tsanProlog)
for _, exp := range p.ExpFunc {
@@ -1352,9 +1364,6 @@ const goProlog = `
//go:linkname _cgo_runtime_cgocall runtime.cgocall
func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32
-//go:linkname _cgo_runtime_cmalloc runtime.cmalloc
-func _cgo_runtime_cmalloc(uintptr) unsafe.Pointer
-
//go:linkname _cgo_runtime_cgocallback runtime.cgocallback
func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr, uintptr)
@@ -1400,7 +1409,7 @@ func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte {
const cStringDef = `
func _Cfunc_CString(s string) *_Ctype_char {
- p := _cgo_runtime_cmalloc(uintptr(len(s)+1))
+ p := _cgo_cmalloc(uint64(len(s)+1))
pp := (*[1<<30]byte)(p)
copy(pp[:], s)
pp[len(s)] = 0
@@ -1410,7 +1419,7 @@ func _Cfunc_CString(s string) *_Ctype_char {
const cBytesDef = `
func _Cfunc_CBytes(b []byte) unsafe.Pointer {
- p := _cgo_runtime_cmalloc(uintptr(len(b)))
+ p := _cgo_cmalloc(uint64(len(b)))
pp := (*[1<<30]byte)(p)
copy(pp[:], b)
return p
@@ -1419,7 +1428,7 @@ func _Cfunc_CBytes(b []byte) unsafe.Pointer {
const cMallocDef = `
func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer {
- return _cgo_runtime_cmalloc(uintptr(n))
+ return _cgo_cmalloc(uint64(n))
}
`
@@ -1432,6 +1441,50 @@ var builtinDefs = map[string]string{
"_CMalloc": cMallocDef,
}
+// Definitions for C.malloc in Go and in C. We define it ourselves
+// since we call it from functions we define, such as C.CString.
+// Also, we have historically ensured that C.malloc does not return
+// nil even for an allocation of 0.
+
+const cMallocDefGo = `
+//go:cgo_import_static _cgoPREFIX_Cfunc__Cmalloc
+//go:linkname __cgofn__cgoPREFIX_Cfunc__Cmalloc _cgoPREFIX_Cfunc__Cmalloc
+var __cgofn__cgoPREFIX_Cfunc__Cmalloc byte
+var _cgoPREFIX_Cfunc__Cmalloc = unsafe.Pointer(&__cgofn__cgoPREFIX_Cfunc__Cmalloc)
+
+//go:cgo_unsafe_args
+func _cgo_cmalloc(p0 uint64) (r1 unsafe.Pointer) {
+ _cgo_runtime_cgocall(_cgoPREFIX_Cfunc__Cmalloc, uintptr(unsafe.Pointer(&p0)))
+ return
+}
+`
+
+// cMallocDefC defines the C version of C.malloc for the gc compiler.
+// It is defined here because C.CString and friends need a definition.
+// We define it by hand, rather than simply inventing a reference to
+// C.malloc, because <stdlib.h> may not have been included.
+// This is approximately what writeOutputFunc would generate, but
+// skips the cgo_topofstack code (which is only needed if the C code
+// calls back into Go). This also avoids returning nil for an
+// allocation of 0 bytes.
+const cMallocDefC = `
+CGO_NO_SANITIZE_THREAD
+void _cgoPREFIX_Cfunc__Cmalloc(void *v) {
+ struct {
+ unsigned long long p0;
+ void *r1;
+ } PACKED *a = v;
+ void *ret;
+ _cgo_tsan_acquire();
+ ret = malloc(a->p0);
+ if (ret == 0 && a->p0 == 0) {
+ ret = malloc(1);
+ }
+ a->r1 = ret;
+ _cgo_tsan_release();
+}
+`
+
func (p *Package) cPrologGccgo() string {
return strings.Replace(strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1),
"GCCGOSYMBOLPREF", p.gccgoSymbolPrefix(), -1)
diff --git a/src/runtime/cgo.go b/src/runtime/cgo.go
index 4fb4a613e0..9cf7b58a2f 100644
--- a/src/runtime/cgo.go
+++ b/src/runtime/cgo.go
@@ -11,8 +11,6 @@ import "unsafe"
// Filled in by runtime/cgo when linked into binary.
//go:linkname _cgo_init _cgo_init
-//go:linkname _cgo_malloc _cgo_malloc
-//go:linkname _cgo_free _cgo_free
//go:linkname _cgo_thread_start _cgo_thread_start
//go:linkname _cgo_sys_thread_create _cgo_sys_thread_create
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
@@ -21,8 +19,6 @@ import "unsafe"
var (
_cgo_init unsafe.Pointer
- _cgo_malloc unsafe.Pointer
- _cgo_free unsafe.Pointer
_cgo_thread_start unsafe.Pointer
_cgo_sys_thread_create unsafe.Pointer
_cgo_notify_runtime_init_done unsafe.Pointer
diff --git a/src/runtime/cgo/callbacks.go b/src/runtime/cgo/callbacks.go
index d0f63fb4ff..9bde5a933f 100644
--- a/src/runtime/cgo/callbacks.go
+++ b/src/runtime/cgo/callbacks.go
@@ -52,18 +52,6 @@ func _cgo_panic(a unsafe.Pointer, n int32) {
var x_cgo_init byte
var _cgo_init = &x_cgo_init
-//go:cgo_import_static x_cgo_malloc
-//go:linkname x_cgo_malloc x_cgo_malloc
-//go:linkname _cgo_malloc _cgo_malloc
-var x_cgo_malloc byte
-var _cgo_malloc = &x_cgo_malloc
-
-//go:cgo_import_static x_cgo_free
-//go:linkname x_cgo_free x_cgo_free
-//go:linkname _cgo_free _cgo_free
-var x_cgo_free byte
-var _cgo_free = &x_cgo_free
-
//go:cgo_import_static x_cgo_thread_start
//go:linkname x_cgo_thread_start x_cgo_thread_start
//go:linkname _cgo_thread_start _cgo_thread_start
diff --git a/src/runtime/cgo/gcc_util.c b/src/runtime/cgo/gcc_util.c
index e20d206be6..4111fe1195 100644
--- a/src/runtime/cgo/gcc_util.c
+++ b/src/runtime/cgo/gcc_util.c
@@ -4,31 +4,6 @@
#include "libcgo.h"
-/* Stub for calling malloc from Go */
-void
-x_cgo_malloc(void *p)
-{
- struct a {
- long long n;
- void *ret;
- } *a = p;
-
- a->ret = malloc(a->n);
- if(a->ret == NULL && a->n == 0)
- a->ret = malloc(1);
-}
-
-/* Stub for calling free from Go */
-void
-x_cgo_free(void *p)
-{
- struct a {
- void *arg;
- } *a = p;
-
- free(a->arg);
-}
-
/* Stub for creating a new thread */
void
x_cgo_thread_start(ThreadStart *arg)
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index 1e0d4c7f19..0f8386b10f 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -145,25 +145,6 @@ func endcgo(mp *m) {
unlockOSThread() // invalidates mp
}
-// Helper functions for cgo code.
-
-func cmalloc(n uintptr) unsafe.Pointer {
- var args struct {
- n uint64
- ret unsafe.Pointer
- }
- args.n = uint64(n)
- cgocall(_cgo_malloc, unsafe.Pointer(&args))
- if args.ret == nil {
- throw("C malloc failed")
- }
- return args.ret
-}
-
-func cfree(p unsafe.Pointer) {
- cgocall(_cgo_free, p)
-}
-
// Call from C back to Go.
//go:nosplit
func cgocallbackg(ctxt uintptr) {
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 8f98cfa8a4..ee89547104 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -155,12 +155,6 @@ func main() {
if _cgo_thread_start == nil {
throw("_cgo_thread_start missing")
}
- if _cgo_malloc == nil {
- throw("_cgo_malloc missing")
- }
- if _cgo_free == nil {
- throw("_cgo_free missing")
- }
if GOOS != "windows" {
if _cgo_setenv == nil {
throw("_cgo_setenv missing")