diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index fd8ebcc4d91..9601d96448d 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -840,6 +840,8 @@ func (p *Package) writeGccgoExports(fgo2, fm io.Writer) { fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") + fmt.Fprintf(fgcc, "%s\n", gccgoExportFileProlog) + for _, exp := range p.ExpFunc { fn := exp.Func fntype := fn.Type @@ -908,6 +910,8 @@ func (p *Package) writeGccgoExports(fgo2, fm io.Writer) { fmt.Fprint(fgcc, "\n") fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams) + fmt.Fprintf(fgcc, "\tif(_cgo_wait_runtime_init_done)\n") + fmt.Fprintf(fgcc, "\t\t_cgo_wait_runtime_init_done();\n") fmt.Fprint(fgcc, "\t") if resultCount > 0 { fmt.Fprint(fgcc, "return ") @@ -1324,3 +1328,19 @@ typedef void *GoChan; typedef struct { void *t; void *v; } GoInterface; typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; ` + +// gccgoExportFileProlog is written to the _cgo_export.c file when +// using gccgo. +// We use weak declarations, and test the addresses, so that this code +// works with older versions of gccgo. +const gccgoExportFileProlog = ` +extern _Bool runtime_iscgo __attribute__ ((weak)); + +static void GoInit(void) __attribute__ ((constructor)); +static void GoInit(void) { + if(&runtime_iscgo) + runtime_iscgo = 1; +} + +extern void _cgo_wait_runtime_init_done() __attribute__ ((weak)); +` diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index a21d4cbbd2a..1791aa777ef 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -2223,7 +2223,13 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] afiles = append(xfiles, afiles...) for _, a := range allactions { - cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...) + // Gather CgoLDFLAGS, but not from standard packages. + // The go tool can dig up runtime/cgo from GOROOT and + // think that it should use its CgoLDFLAGS, but gccgo + // doesn't use runtime/cgo. + if !a.p.Standard { + cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...) + } if len(a.p.CgoFiles) > 0 { usesCgo = true } @@ -2237,20 +2243,75 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] objc = true } } + + if ldBuildmode == "c-archive" { + ldflags = append(ldflags, "-Wl,--whole-archive") + } + ldflags = append(ldflags, afiles...) + + if ldBuildmode == "c-archive" { + ldflags = append(ldflags, "-Wl,--no-whole-archive") + } + ldflags = append(ldflags, cgoldflags...) ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...) ldflags = append(ldflags, p.CgoLDFLAGS...) - if usesCgo && goos == "linux" { - ldflags = append(ldflags, "-Wl,-E") + + ldflags = stringList("-Wl,-(", ldflags, "-Wl,-)") + + var realOut string + switch ldBuildmode { + case "exe": + if usesCgo && goos == "linux" { + ldflags = append(ldflags, "-Wl,-E") + } + if cxx { + ldflags = append(ldflags, "-lstdc++") + } + if objc { + ldflags = append(ldflags, "-lobjc") + } + + case "c-archive": + // Link the Go files into a single .o, and also link + // in -lgolibbegin. + // + // We need to use --whole-archive with -lgolibbegin + // because it doesn't define any symbols that will + // cause the contents to be pulled in; it's just + // initialization code. + // + // The user remains responsible for linking against + // -lgo -lpthread -lm in the final link. We can't use + // -r to pick them up because we can't combine + // split-stack and non-split-stack code in a single -r + // link, and libgo picks up non-split-stack code from + // libffi. + ldflags = append(ldflags, "-Wl,-r", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive") + + // We are creating an object file, so we don't want a build ID. + ldflags = b.disableBuildID(ldflags) + + realOut = out + out = out + ".o" + + default: + fatalf("-buildmode=%s not supported for gccgo", ldBuildmode) } - if cxx { - ldflags = append(ldflags, "-lstdc++") + + if err := b.run(".", p.ImportPath, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil { + return err } - if objc { - ldflags = append(ldflags, "-lobjc") + + switch ldBuildmode { + case "c-archive": + if err := b.run(".", p.ImportPath, nil, "ar", "rc", realOut, out); err != nil { + return err + } } - return b.run(".", p.ImportPath, nil, tools.linker(), "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags) + + return nil } func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { @@ -2261,6 +2322,10 @@ func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) er if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) } + switch goarch { + case "386", "amd64": + defs = append(defs, "-fsplit-stack") + } return b.run(p.Dir, p.ImportPath, nil, envList("CC", defaultCC), "-Wall", "-g", "-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) } @@ -2504,6 +2569,10 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi } if _, ok := buildToolchain.(gccgoToolchain); ok { + switch goarch { + case "386", "amd64": + cgoCFLAGS = append(cgoCFLAGS, "-fsplit-stack") + } cgoflags = append(cgoflags, "-gccgo") if pkgpath := gccgoPkgpath(p); pkgpath != "" { cgoflags = append(cgoflags, "-gccgopkgpath="+pkgpath) @@ -2657,19 +2726,8 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi } ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs) - // Some systems, such as Ubuntu, always add --build-id to - // every link, but we don't want a build ID since we are - // producing an object file. On some of those system a plain - // -r (not -Wl,-r) will turn off --build-id, but clang 3.0 - // doesn't support a plain -r. I don't know how to turn off - // --build-id when using clang other than passing a trailing - // --build-id=none. So that is what we do, but only on - // systems likely to support it, which is to say, systems that - // normally use gold or the GNU linker. - switch goos { - case "android", "dragonfly", "linux", "netbsd": - ldflags = append(ldflags, "-Wl,--build-id=none") - } + // We are creating an object file, so we don't want a build ID. + ldflags = b.disableBuildID(ldflags) if err := b.gccld(p, ofile, ldflags, gccObjs); err != nil { return nil, nil, err @@ -2883,6 +2941,24 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b return obj + goFile, obj + gccBase + gccExt, nil } +// disableBuildID adjusts a linker command line to avoid creating a +// build ID when creating an object file rather than an executable or +// shared library. Some systems, such as Ubuntu, always add +// --build-id to every link, but we don't want a build ID when we are +// producing an object file. On some of those system a plain -r (not +// -Wl,-r) will turn off --build-id, but clang 3.0 doesn't support a +// plain -r. I don't know how to turn off --build-id when using clang +// other than passing a trailing --build-id=none. So that is what we +// do, but only on systems likely to support it, which is to say, +// systems that normally use gold or the GNU linker. +func (b *builder) disableBuildID(ldflags []string) []string { + switch goos { + case "android", "dragonfly", "linux", "netbsd": + ldflags = append(ldflags, "-Wl,--build-id=none") + } + return ldflags +} + // An actionQueue is a priority queue of actions. type actionQueue []*action