Skip to content

Commit 3c17053

Browse files
ianlancetaylorgopherbot
authored andcommitted
cmd/go, cmd/cgo: support older versions of gccgo that lack cgo.Incomplete
Test whether gccgo/GoLLVM supports cgo.Incomplete. If it doesn't, use a local definition rather than importing it. Roll back 426496, which skipped a gccgo test, as it now works. For #46731 Fixes #54761 Change-Id: I8bb2ad84c317094495405e178bf5c9694f82af56 Reviewed-on: https://go-review.googlesource.com/c/go/+/446260 Reviewed-by: Bryan Mills <bcmills@google.com> Run-TryBot: Ian Lance Taylor <iant@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Run-TryBot: Bryan Mills <bcmills@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
1 parent 91a1f0d commit 3c17053

File tree

7 files changed

+93
-12
lines changed

7 files changed

+93
-12
lines changed

src/cmd/cgo/doc.go

+3
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,9 @@ The following options are available when running cgo directly:
498498
The -fgo-prefix option to be used with gccgo.
499499
-gccgopkgpath path
500500
The -fgo-pkgpath option to be used with gccgo.
501+
-gccgo_define_cgoincomplete
502+
Define cgo.Incomplete locally rather than importing it from
503+
the "runtime/cgo" package. Used for old gccgo versions.
501504
-godefs
502505
Write out input file in Go syntax replacing C package
503506
names with real values. Used to generate files in the

src/cmd/cgo/gcc.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ var nameToC = map[string]string{
4747
"complexdouble": "double _Complex",
4848
}
4949

50+
var incomplete = "_cgopackage.Incomplete"
51+
5052
// cname returns the C name to use for C.s.
5153
// The expansions are listed in nameToC and also
5254
// struct_foo becomes "struct foo", and similarly for
@@ -2565,7 +2567,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
25652567
// get writebarrier-ed or adjusted during a stack copy. This should handle
25662568
// all the cases badPointerTypedef used to handle, but hopefully will
25672569
// continue to work going forward without any more need for cgo changes.
2568-
tt.Go = c.Ident("_cgopackage.Incomplete")
2570+
tt.Go = c.Ident(incomplete)
25692571
typedef[name.Name] = &tt
25702572
break
25712573
}
@@ -2592,7 +2594,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
25922594
}
25932595
tt.Go = g
25942596
if c.incompleteStructs[tag] {
2595-
tt.Go = c.Ident("_cgopackage.Incomplete")
2597+
tt.Go = c.Ident(incomplete)
25962598
}
25972599
typedef[name.Name] = &tt
25982600
}
@@ -2640,7 +2642,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
26402642
if c.badVoidPointerTypedef(dt) {
26412643
// Treat this typedef as a pointer to a _cgopackage.Incomplete.
26422644
s := *sub
2643-
s.Go = c.Ident("*_cgopackage.Incomplete")
2645+
s.Go = c.Ident("*" + incomplete)
26442646
sub = &s
26452647
// Make sure we update any previously computed type.
26462648
if oldType := typedef[name.Name]; oldType != nil {
@@ -2656,7 +2658,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
26562658
// Make sure we update any previously computed type.
26572659
name := "_Ctype_struct_" + strct.StructName
26582660
if oldType := typedef[name]; oldType != nil {
2659-
oldType.Go = c.Ident("_cgopackage.Incomplete")
2661+
oldType.Go = c.Ident(incomplete)
26602662
}
26612663
}
26622664
}
@@ -3196,7 +3198,7 @@ func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool {
31963198
// non-pointers in this type.
31973199
// TODO: Currently our best solution is to find these manually and list them as
31983200
// they come up. A better solution is desired.
3199-
// Note: DEPRECATED. There is now a better solution. Search for _cgopackage.Incomplete in this file.
3201+
// Note: DEPRECATED. There is now a better solution. Search for incomplete in this file.
32003202
func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
32013203
if c.badCFType(dt) {
32023204
return true

src/cmd/cgo/main.go

+9
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
242242
var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
243243
var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
244244
var gccgoMangler func(string) string
245+
var gccgoDefineCgoIncomplete = flag.Bool("gccgo_define_cgoincomplete", false, "define cgo.Incomplete for older gccgo/GoLLVM")
245246
var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
246247
var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
247248
var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths")
@@ -253,6 +254,14 @@ func main() {
253254
objabi.AddVersionFlag() // -V
254255
objabi.Flagparse(usage)
255256

257+
if *gccgoDefineCgoIncomplete {
258+
if !*gccgo {
259+
fmt.Fprintf(os.Stderr, "cgo: -gccgo_define_cgoincomplete without -gccgo\n")
260+
os.Exit(2)
261+
}
262+
incomplete = "_cgopackage_Incomplete"
263+
}
264+
256265
if *dynobj != "" {
257266
// cgo -dynimport is essentially a separate helper command
258267
// built into the cgo binary. It scans a gcc-produced executable

src/cmd/cgo/out.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,13 @@ func (p *Package) writeDefs() {
8585
fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
8686
}
8787
if *importRuntimeCgo {
88-
fmt.Fprintf(fgo2, "import _cgopackage \"runtime/cgo\"\n\n")
89-
fmt.Fprintf(fgo2, "type _ _cgopackage.Incomplete\n") // prevent import-not-used error
88+
if !*gccgoDefineCgoIncomplete {
89+
fmt.Fprintf(fgo2, "import _cgopackage \"runtime/cgo\"\n\n")
90+
fmt.Fprintf(fgo2, "type _ _cgopackage.Incomplete\n") // prevent import-not-used error
91+
} else {
92+
fmt.Fprintf(fgo2, "//go:notinheap\n")
93+
fmt.Fprintf(fgo2, "type _cgopackage_Incomplete struct{ _ struct{ _ struct{} } }\n")
94+
}
9095
}
9196
if *importSyscall {
9297
fmt.Fprintf(fgo2, "var _ syscall.Errno\n")

src/cmd/go/internal/work/exec.go

+3
Original file line numberDiff line numberDiff line change
@@ -2953,6 +2953,9 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
29532953
if pkgpath := gccgoPkgpath(p); pkgpath != "" {
29542954
cgoflags = append(cgoflags, "-gccgopkgpath="+pkgpath)
29552955
}
2956+
if !BuildToolchain.(gccgoToolchain).supportsCgoIncomplete(b) {
2957+
cgoflags = append(cgoflags, "-gccgo_define_cgoincomplete")
2958+
}
29562959
}
29572960

29582961
switch cfg.BuildBuildmode {

src/cmd/go/internal/work/gccgo.go

+60
Original file line numberDiff line numberDiff line change
@@ -617,3 +617,63 @@ func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) strin
617617

618618
return gccgoToSymbolFunc(gccgoPkgpath(p))
619619
}
620+
621+
var (
622+
gccgoSupportsCgoIncompleteOnce sync.Once
623+
gccgoSupportsCgoIncomplete bool
624+
)
625+
626+
const gccgoSupportsCgoIncompleteCode = `
627+
package p
628+
629+
import "runtime/cgo"
630+
631+
type I cgo.Incomplete
632+
`
633+
634+
// supportsCgoIncomplete reports whether the gccgo/GoLLVM compiler
635+
// being used supports cgo.Incomplete, which was added in GCC 13.
636+
func (tools gccgoToolchain) supportsCgoIncomplete(b *Builder) bool {
637+
gccgoSupportsCgoIncompleteOnce.Do(func() {
638+
fail := func(err error) {
639+
fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
640+
base.SetExitStatus(2)
641+
base.Exit()
642+
}
643+
644+
tmpdir := b.WorkDir
645+
if cfg.BuildN {
646+
tmpdir = os.TempDir()
647+
}
648+
f, err := os.CreateTemp(tmpdir, "*_gccgo_cgoincomplete.go")
649+
if err != nil {
650+
fail(err)
651+
}
652+
fn := f.Name()
653+
f.Close()
654+
defer os.Remove(fn)
655+
656+
if err := os.WriteFile(fn, []byte(gccgoSupportsCgoIncompleteCode), 0644); err != nil {
657+
fail(err)
658+
}
659+
660+
on := strings.TrimSuffix(fn, ".go") + ".o"
661+
if cfg.BuildN || cfg.BuildX {
662+
b.Showcmd(tmpdir, "%s -c -o %s %s || true", tools.compiler(), on, fn)
663+
// Since this function affects later builds,
664+
// and only generates temporary files,
665+
// we run the command even with -n.
666+
}
667+
cmd := exec.Command(tools.compiler(), "-c", "-o", on, fn)
668+
cmd.Dir = tmpdir
669+
var buf strings.Builder
670+
cmd.Stdout = &buf
671+
cmd.Stderr = &buf
672+
err = cmd.Run()
673+
if out := buf.String(); len(out) > 0 {
674+
b.showOutput(nil, tmpdir, b.fmtcmd(tmpdir, "%s -c -o %s %s", tools.compiler(), on, fn), buf.String())
675+
}
676+
gccgoSupportsCgoIncomplete = err == nil
677+
})
678+
return gccgoSupportsCgoIncomplete
679+
}

src/cmd/go/testdata/script/build_overlay.txt

+4-5
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,6 @@ go build -compiler=gccgo -overlay overlay.json -o print_trimpath_gccgo$GOEXE -tr
8383
exec ./print_trimpath_gccgo$GOEXE
8484
stdout ^\.[/\\]printpath[/\\]main.go
8585

86-
go build -compiler=gccgo -overlay overlay.json -o main_call_asm_gccgo$GOEXE ./call_asm
87-
exec ./main_call_asm_gccgo$GOEXE
88-
! stdout .
89-
90-
skip 'broken as of CL 421879: see https://go.dev/issue/54761'
9186

9287
go build -compiler=gccgo -overlay overlay.json -o main_cgo_replace_gccgo$GOEXE ./cgo_hello_replace
9388
exec ./main_cgo_replace_gccgo$GOEXE
@@ -101,6 +96,10 @@ go build -compiler=gccgo -overlay overlay.json -o main_cgo_angle_gccgo$GOEXE ./
10196
exec ./main_cgo_angle_gccgo$GOEXE
10297
stdout '^hello cgo\r?\n'
10398

99+
go build -compiler=gccgo -overlay overlay.json -o main_call_asm_gccgo$GOEXE ./call_asm
100+
exec ./main_call_asm_gccgo$GOEXE
101+
! stdout .
102+
104103

105104
-- m/go.mod --
106105
// TODO(matloob): how do overlays work with go.mod (especially if mod=readonly)

0 commit comments

Comments
 (0)