Skip to content

Commit a70a2a8

Browse files
cmd/cgo: don't update each call in place
Updating each call in place broke when there were multiple cgo calls used as arguments to another cgo call where some required rewriting. Instead, rewrite calls to strings via the existing mangling mechanism, and only substitute the top level call in place. Fixes #28540 Change-Id: Ifd66f04c205adc4ad6dd5ee8e79e57dce17e86bb Reviewed-on: https://go-review.googlesource.com/c/146860 Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 2182bb0 commit a70a2a8

File tree

3 files changed

+102
-37
lines changed

3 files changed

+102
-37
lines changed

misc/cgo/test/twoargs.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
// Crash from call with two arguments that need pointer checking.
6+
// No runtime test; just make sure it compiles.
7+
8+
package cgotest
9+
10+
/*
11+
static void twoargs1(void *p, int n) {}
12+
static void *twoargs2() { return 0; }
13+
static int twoargs3(void * p) { return 0; }
14+
*/
15+
import "C"
16+
17+
import "unsafe"
18+
19+
func twoargsF() {
20+
v := []string{}
21+
C.twoargs1(C.twoargs2(), C.twoargs3(unsafe.Pointer(&v)))
22+
}

src/cmd/cgo/gcc.go

Lines changed: 79 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -722,20 +722,18 @@ func (p *Package) mangleName(n *Name) {
722722
func (p *Package) rewriteCalls(f *File) bool {
723723
needsUnsafe := false
724724
// Walk backward so that in C.f1(C.f2()) we rewrite C.f2 first.
725-
for i := len(f.Calls) - 1; i >= 0; i-- {
726-
call := f.Calls[i]
727-
// This is a call to C.xxx; set goname to "xxx".
728-
goname := call.Call.Fun.(*ast.SelectorExpr).Sel.Name
729-
if goname == "malloc" {
725+
for _, call := range f.Calls {
726+
if call.Done {
730727
continue
731728
}
732-
name := f.Name[goname]
733-
if name.Kind != "func" {
734-
// Probably a type conversion.
735-
continue
736-
}
737-
if p.rewriteCall(f, call, name) {
738-
needsUnsafe = true
729+
start := f.offset(call.Call.Pos())
730+
end := f.offset(call.Call.End())
731+
str, nu := p.rewriteCall(f, call)
732+
if str != "" {
733+
f.Edit.Replace(start, end, str)
734+
if nu {
735+
needsUnsafe = true
736+
}
739737
}
740738
}
741739
return needsUnsafe
@@ -745,16 +743,37 @@ func (p *Package) rewriteCalls(f *File) bool {
745743
// If any pointer checks are required, we rewrite the call into a
746744
// function literal that calls _cgoCheckPointer for each pointer
747745
// argument and then calls the original function.
748-
// This returns whether the package needs to import unsafe as _cgo_unsafe.
749-
func (p *Package) rewriteCall(f *File, call *Call, name *Name) bool {
746+
// This returns the rewritten call and whether the package needs to
747+
// import unsafe as _cgo_unsafe.
748+
// If it returns the empty string, the call did not need to be rewritten.
749+
func (p *Package) rewriteCall(f *File, call *Call) (string, bool) {
750+
// This is a call to C.xxx; set goname to "xxx".
751+
// It may have already been mangled by rewriteName.
752+
var goname string
753+
switch fun := call.Call.Fun.(type) {
754+
case *ast.SelectorExpr:
755+
goname = fun.Sel.Name
756+
case *ast.Ident:
757+
goname = strings.TrimPrefix(fun.Name, "_C2func_")
758+
goname = strings.TrimPrefix(goname, "_Cfunc_")
759+
}
760+
if goname == "" || goname == "malloc" {
761+
return "", false
762+
}
763+
name := f.Name[goname]
764+
if name == nil || name.Kind != "func" {
765+
// Probably a type conversion.
766+
return "", false
767+
}
768+
750769
params := name.FuncType.Params
751770
args := call.Call.Args
752771

753772
// Avoid a crash if the number of arguments is
754773
// less than the number of parameters.
755774
// This will be caught when the generated file is compiled.
756775
if len(args) < len(params) {
757-
return false
776+
return "", false
758777
}
759778

760779
any := false
@@ -765,7 +784,7 @@ func (p *Package) rewriteCall(f *File, call *Call, name *Name) bool {
765784
}
766785
}
767786
if !any {
768-
return false
787+
return "", false
769788
}
770789

771790
// We need to rewrite this call.
@@ -848,7 +867,10 @@ func (p *Package) rewriteCall(f *File, call *Call, name *Name) bool {
848867
// Write _cgoCheckPointer calls to sbCheck.
849868
var sbCheck bytes.Buffer
850869
for i, param := range params {
851-
arg := p.mangle(f, &args[i])
870+
arg, nu := p.mangle(f, &args[i])
871+
if nu {
872+
needsUnsafe = true
873+
}
852874

853875
// Explicitly convert untyped constants to the
854876
// parameter type, to avoid a type mismatch.
@@ -893,12 +915,12 @@ func (p *Package) rewriteCall(f *File, call *Call, name *Name) bool {
893915
sb.WriteString("return ")
894916
}
895917

896-
// Now we are ready to call the C function.
897-
// To work smoothly with rewriteRef we leave the call in place
898-
// and just replace the old arguments with our new ones.
899-
f.Edit.Insert(f.offset(call.Call.Fun.Pos()), sb.String())
918+
m, nu := p.mangle(f, &call.Call.Fun)
919+
if nu {
920+
needsUnsafe = true
921+
}
922+
sb.WriteString(gofmtLine(m))
900923

901-
sb.Reset()
902924
sb.WriteString("(")
903925
for i := range params {
904926
if i > 0 {
@@ -916,9 +938,7 @@ func (p *Package) rewriteCall(f *File, call *Call, name *Name) bool {
916938
}
917939
sb.WriteString("()")
918940

919-
f.Edit.Replace(f.offset(call.Call.Lparen), f.offset(call.Call.Rparen)+1, sb.String())
920-
921-
return needsUnsafe
941+
return sb.String(), needsUnsafe
922942
}
923943

924944
// needsPointerCheck returns whether the type t needs a pointer check.
@@ -1025,32 +1045,54 @@ func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {
10251045
}
10261046
}
10271047

1028-
// mangle replaces references to C names in arg with the mangled names.
1029-
// It removes the corresponding references in f.Ref, so that we don't
1030-
// try to do the replacement again in rewriteRef.
1031-
func (p *Package) mangle(f *File, arg *ast.Expr) ast.Expr {
1048+
// mangle replaces references to C names in arg with the mangled names,
1049+
// rewriting calls when it finds them.
1050+
// It removes the corresponding references in f.Ref and f.Calls, so that we
1051+
// don't try to do the replacement again in rewriteRef or rewriteCall.
1052+
func (p *Package) mangle(f *File, arg *ast.Expr) (ast.Expr, bool) {
1053+
needsUnsafe := false
10321054
f.walk(arg, ctxExpr, func(f *File, arg interface{}, context astContext) {
10331055
px, ok := arg.(*ast.Expr)
10341056
if !ok {
10351057
return
10361058
}
10371059
sel, ok := (*px).(*ast.SelectorExpr)
1038-
if !ok {
1060+
if ok {
1061+
if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
1062+
return
1063+
}
1064+
1065+
for _, r := range f.Ref {
1066+
if r.Expr == px {
1067+
*px = p.rewriteName(f, r)
1068+
r.Done = true
1069+
break
1070+
}
1071+
}
1072+
10391073
return
10401074
}
1041-
if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
1075+
1076+
call, ok := (*px).(*ast.CallExpr)
1077+
if !ok {
10421078
return
10431079
}
10441080

1045-
for _, r := range f.Ref {
1046-
if r.Expr == px {
1047-
*px = p.rewriteName(f, r)
1048-
r.Done = true
1049-
break
1081+
for _, c := range f.Calls {
1082+
if !c.Done && c.Call.Lparen == call.Lparen {
1083+
cstr, nu := p.rewriteCall(f, c)
1084+
if cstr != "" {
1085+
// Smuggle the rewritten call through an ident.
1086+
*px = ast.NewIdent(cstr)
1087+
if nu {
1088+
needsUnsafe = true
1089+
}
1090+
c.Done = true
1091+
}
10501092
}
10511093
}
10521094
})
1053-
return *arg
1095+
return *arg, needsUnsafe
10541096
}
10551097

10561098
// checkIndex checks whether arg the form &a[i], possibly inside type

src/cmd/cgo/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ func nameKeys(m map[string]*Name) []string {
8181
type Call struct {
8282
Call *ast.CallExpr
8383
Deferred bool
84+
Done bool
8485
}
8586

8687
// A Ref refers to an expression of the form C.xxx in the AST.

0 commit comments

Comments
 (0)