cmd/cgo: add #cgo noescape/nocallback annotations

When passing pointers of Go objects from Go to C, the cgo command generate _Cgo_use(pN) for the unsafe.Pointer type arguments, so that the Go compiler will escape these object to heap.

Since the C function may callback to Go, then the Go stack might grow/shrink, that means the pointers that the C function have will be invalid.

After adding the #cgo noescape annotation for a C function, the cgo command won't generate _Cgo_use(pN), and the Go compiler won't force the object escape to heap.

After adding the #cgo nocallback annotation for a C function, which means the C function won't callback to Go, if it do callback to Go, the Go process will crash.

Fixes #56378

Change-Id: Ifdca070584e0d349c7b12276270e50089e481f7a
GitHub-Last-Rev: f1a17b08b0
GitHub-Pull-Request: golang/go#60399
Reviewed-on: https://go-review.googlesource.com/c/go/+/497837
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
doujiang24 2023-08-25 17:06:31 +00:00 committed by Gopher Robot
parent 1a01cb22f9
commit 24b9ef1a73
15 changed files with 312 additions and 33 deletions

View file

@ -73,18 +73,32 @@ func cname(s string) string {
return s
}
// DiscardCgoDirectives processes the import C preamble, and discards
// all #cgo CFLAGS and LDFLAGS directives, so they don't make their
// way into _cgo_export.h.
func (f *File) DiscardCgoDirectives() {
// ProcessCgoDirectives processes the import C preamble:
// 1. discards all #cgo CFLAGS, LDFLAGS, nocallback and noescape directives,
// so they don't make their way into _cgo_export.h.
// 2. parse the nocallback and noescape directives.
func (f *File) ProcessCgoDirectives() {
linesIn := strings.Split(f.Preamble, "\n")
linesOut := make([]string, 0, len(linesIn))
f.NoCallbacks = make(map[string]bool)
f.NoEscapes = make(map[string]bool)
for _, line := range linesIn {
l := strings.TrimSpace(line)
if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) {
linesOut = append(linesOut, line)
} else {
linesOut = append(linesOut, "")
// #cgo (nocallback|noescape) <function name>
if fields := strings.Fields(l); len(fields) == 3 {
directive := fields[1]
funcName := fields[2]
if directive == "nocallback" {
f.NoCallbacks[funcName] = true
} else if directive == "noescape" {
f.NoEscapes[funcName] = true
}
}
}
}
f.Preamble = strings.Join(linesOut, "\n")