From 12b05bf8fd63634589f71d6ba9d8519d0ae8184b Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 11 May 2015 13:39:28 +1200 Subject: [PATCH] cmd/go: support -buildmode=shared with gccgo Change-Id: Id93b8ab42fa311ce32209734ec9a0813f8736e25 Reviewed-on: https://go-review.googlesource.com/9914 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor --- misc/cgo/testshared/shared_test.go | 87 +++++++- src/cmd/go/build.go | 322 +++++++++++++++++++---------- src/cmd/go/pkg.go | 7 +- 3 files changed, 297 insertions(+), 119 deletions(-) diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index 09b159d773..ae977c0063 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -20,6 +20,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strings" "testing" "time" @@ -274,13 +275,17 @@ func dynStrings(path string, flag elf.DynTag) []string { return dynstrings } -func AssertIsLinkedTo(t *testing.T, path, lib string) { +func AssertIsLinkedToRegexp(t *testing.T, path string, re *regexp.Regexp) { for _, dynstring := range dynStrings(path, elf.DT_NEEDED) { - if dynstring == lib { + if re.MatchString(dynstring) { return } } - t.Errorf("%s is not linked to %s", path, lib) + t.Errorf("%s is not linked to anything matching %v", path, re) +} + +func AssertIsLinkedTo(t *testing.T, path, lib string) { + AssertIsLinkedToRegexp(t, path, regexp.MustCompile(regexp.QuoteMeta(lib))) } func AssertHasRPath(t *testing.T, path, dir string) { @@ -306,7 +311,7 @@ func TestTrivialExecutable(t *testing.T) { // Build a GOPATH package into a shared library that links against the goroot runtime // and an executable that links against both. -func TestGOPathShlib(t *testing.T) { +func TestGopathShlib(t *testing.T) { goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep") AssertIsLinkedTo(t, filepath.Join(gopathInstallDir, "libdep.so"), soname) goCmd(t, "install", "-linkshared", "exe") @@ -437,13 +442,85 @@ func TestNotes(t *testing.T) { // Build a GOPATH package (dep) into a shared library that links against the goroot // runtime, another package (dep2) that links against the first, and and an // executable that links against dep2. -func TestTwoGOPathShlibs(t *testing.T) { +func TestTwoGopathShlibs(t *testing.T) { goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep") goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep2") goCmd(t, "install", "-linkshared", "exe2") run(t, "executable linked to GOPATH library", "./bin/exe2") } +// Build a GOPATH package into a shared library with gccgo and an executable that +// links against it. +func TestGoPathShlibGccgo(t *testing.T) { + gccgoName := os.Getenv("GCCGO") + if gccgoName == "" { + gccgoName = "gccgo" + } + _, err := exec.LookPath(gccgoName) + if err != nil { + t.Skip("gccgo not found") + } + + libgoRE := regexp.MustCompile("libgo.so.[0-9]+") + + gccgoContext := build.Default + gccgoContext.InstallSuffix = suffix + "_fPIC" + gccgoContext.Compiler = "gccgo" + gccgoContext.GOPATH = os.Getenv("GOPATH") + depP, err := gccgoContext.Import("dep", ".", build.ImportComment) + if err != nil { + t.Fatalf("import failed: %v", err) + } + gccgoInstallDir := filepath.Join(depP.PkgTargetRoot, "shlibs") + goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "dep") + AssertIsLinkedToRegexp(t, filepath.Join(gccgoInstallDir, "libdep.so"), libgoRE) + goCmd(t, "install", "-compiler=gccgo", "-linkshared", "exe") + AssertIsLinkedToRegexp(t, "./bin/exe", libgoRE) + AssertIsLinkedTo(t, "./bin/exe", "libdep.so") + AssertHasRPath(t, "./bin/exe", gccgoInstallDir) + // And check it runs. + run(t, "gccgo-built", "./bin/exe") +} + +// The gccgo version of TestTwoGopathShlibs: build a GOPATH package into a shared +// library with gccgo, another GOPATH package that depends on the first and an +// executable that links the second library. +func TestTwoGopathShlibsGccgo(t *testing.T) { + gccgoName := os.Getenv("GCCGO") + if gccgoName == "" { + gccgoName = "gccgo" + } + _, err := exec.LookPath(gccgoName) + if err != nil { + t.Skip("gccgo not found") + } + + libgoRE := regexp.MustCompile("libgo.so.[0-9]+") + + gccgoContext := build.Default + gccgoContext.InstallSuffix = suffix + "_fPIC" + gccgoContext.Compiler = "gccgo" + gccgoContext.GOPATH = os.Getenv("GOPATH") + depP, err := gccgoContext.Import("dep", ".", build.ImportComment) + if err != nil { + t.Fatalf("import failed: %v", err) + } + gccgoInstallDir := filepath.Join(depP.PkgTargetRoot, "shlibs") + goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "dep") + goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "dep2") + goCmd(t, "install", "-compiler=gccgo", "-linkshared", "exe2") + + AssertIsLinkedToRegexp(t, filepath.Join(gccgoInstallDir, "libdep.so"), libgoRE) + AssertIsLinkedToRegexp(t, filepath.Join(gccgoInstallDir, "libdep2.so"), libgoRE) + AssertIsLinkedTo(t, filepath.Join(gccgoInstallDir, "libdep2.so"), "libdep.so") + AssertIsLinkedToRegexp(t, "./bin/exe2", libgoRE) + AssertIsLinkedTo(t, "./bin/exe2", "libdep2") + AssertIsLinkedTo(t, "./bin/exe2", "libdep.so") + + // And check it runs. + run(t, "gccgo-built", "./bin/exe2") +} + // Testing rebuilding of shared libraries when they are stale is a bit more // complicated that it seems like it should be. First, we make everything "old": but // only a few seconds old, or it might be older than 6g (or the runtime source) and diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 4c8f319217..07b4c30794 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -8,6 +8,7 @@ import ( "bufio" "bytes" "container/heap" + "debug/elf" "errors" "flag" "fmt" @@ -752,19 +753,37 @@ func goFilesPackage(gofiles []string) *Package { return pkg } -func readpkglist(shlibpath string) []*Package { - pkglistbytes, err := readELFNote(shlibpath, "Go\x00\x00", 1) - if err != nil { - fatalf("readELFNote failed: %v", err) - } - scanner := bufio.NewScanner(bytes.NewBuffer(pkglistbytes)) - var pkgs []*Package +// readpkglist returns the list of packages that were built into the shared library +// at shlibpath. For the native toolchain this list is stored, newline separated, in +// an ELF note with name "Go\x00\x00" and type 1. For GCCGO it is extracted from the +// .go_export section. +func readpkglist(shlibpath string) (pkgs []*Package) { var stk importStack - for scanner.Scan() { - t := scanner.Text() - pkgs = append(pkgs, loadPackage(t, &stk)) + if _, gccgo := buildToolchain.(gccgoToolchain); gccgo { + f, _ := elf.Open(shlibpath) + sect := f.Section(".go_export") + data, _ := sect.Data() + scanner := bufio.NewScanner(bytes.NewBuffer(data)) + for scanner.Scan() { + t := scanner.Text() + if strings.HasPrefix(t, "pkgpath ") { + t = strings.TrimPrefix(t, "pkgpath ") + t = strings.TrimSuffix(t, ";") + pkgs = append(pkgs, loadPackage(t, &stk)) + } + } + } else { + pkglistbytes, err := readELFNote(shlibpath, "Go\x00\x00", 1) + if err != nil { + fatalf("readELFNote failed: %v", err) + } + scanner := bufio.NewScanner(bytes.NewBuffer(pkglistbytes)) + for scanner.Scan() { + t := scanner.Text() + pkgs = append(pkgs, loadPackage(t, &stk)) + } } - return pkgs + return } // action returns the action for applying the given operation (mode) to the package. @@ -914,23 +933,26 @@ func (b *builder) libaction(libname string, pkgs []*Package, mode, depMode build // was not passed on the command line and it is not present in // another shared library, add it here. seencgo := false - for _, p := range pkgs { - seencgo = seencgo || (p.Standard && p.ImportPath == "runtime/cgo") - } - if !seencgo { - var stk importStack - p := loadPackage("runtime/cgo", &stk) - if p.Error != nil { - fatalf("load runtime/cgo: %v", p.Error) + _, gccgo := buildToolchain.(gccgoToolchain) + if !gccgo { + for _, p := range pkgs { + seencgo = seencgo || (p.Standard && p.ImportPath == "runtime/cgo") } - computeStale(p) - // If runtime/cgo is in another shared library, then that's - // also the shared library that contains runtime, so - // something will depend on it and so runtime/cgo's staleness - // will be checked when processing that library. - if p.Shlib == "" || p.Shlib == libname { - pkgs = append([]*Package{}, pkgs...) - pkgs = append(pkgs, p) + if !seencgo { + var stk importStack + p := loadPackage("runtime/cgo", &stk) + if p.Error != nil { + fatalf("load runtime/cgo: %v", p.Error) + } + computeStale(p) + // If runtime/cgo is in another shared library, then that's + // also the shared library that contains runtime, so + // something will depend on it and so runtime/cgo's staleness + // will be checked when processing that library. + if p.Shlib == "" || p.Shlib == libname { + pkgs = append([]*Package{}, pkgs...) + pkgs = append(pkgs, p) + } } } @@ -938,6 +960,9 @@ func (b *builder) libaction(libname string, pkgs []*Package, mode, depMode build var libdir string for _, p := range pkgs { plibdir := p.build.PkgTargetRoot + if gccgo { + plibdir = filepath.Join(plibdir, "shlibs") + } if libdir == "" { libdir = plibdir } else if libdir != plibdir { @@ -1429,7 +1454,7 @@ func (b *builder) build(a *action) (err error) { // linker needs the whole dependency tree. all := actionList(a) all = all[:len(all)-1] // drop a - if err := buildToolchain.ld(b, a.p, a.target, all, a.objpkg, objects); err != nil { + if err := buildToolchain.ld(b, a, a.target, all, a.objpkg, objects); err != nil { return err } } @@ -1477,68 +1502,10 @@ func (b *builder) installShlibname(a *action) error { return nil } -// setextld sets the appropriate linker flags for the specified compiler. -func setextld(ldflags []string, compiler []string) []string { - for _, f := range ldflags { - if f == "-extld" || strings.HasPrefix(f, "-extld=") { - // don't override -extld if supplied - return ldflags - } - } - ldflags = append(ldflags, "-extld="+compiler[0]) - if len(compiler) > 1 { - extldflags := false - add := strings.Join(compiler[1:], " ") - for i, f := range ldflags { - if f == "-extldflags" && i+1 < len(ldflags) { - ldflags[i+1] = add + " " + ldflags[i+1] - extldflags = true - break - } else if strings.HasPrefix(f, "-extldflags=") { - ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):] - extldflags = true - break - } - } - if !extldflags { - ldflags = append(ldflags, "-extldflags="+add) - } - } - return ldflags -} - func (b *builder) linkShared(a *action) (err error) { - // TODO(mwhudson): obvious copy pasting from gcToolchain.ld, should make a few - // changes to that function and then call it. And support gccgo. allactions := actionList(a) - importArgs := b.includeArgs("-L", allactions[:len(allactions)-1]) - ldflags := []string{"-installsuffix", buildContext.InstallSuffix} - ldflags = append(ldflags, "-buildmode=shared") - ldflags = append(ldflags, buildLdflags...) - cxx := a.p != nil && (len(a.p.CXXFiles) > 0 || len(a.p.SwigCXXFiles) > 0) - for _, a := range allactions { - if a.p != nil && (len(a.p.CXXFiles) > 0 || len(a.p.SwigCXXFiles) > 0) { - cxx = true - } - } - // If the user has not specified the -extld option, then specify the - // appropriate linker. In case of C++ code, use the compiler named - // by the CXX environment variable or defaultCXX if CXX is not set. - // Else, use the CC environment variable and defaultCC as fallback. - var compiler []string - if cxx { - compiler = envList("CXX", defaultCXX) - } else { - compiler = envList("CC", defaultCC) - } - ldflags = setextld(ldflags, compiler) - for _, d := range a.deps { - if !strings.HasSuffix(d.target, ".a") { // omit unsafe etc and actions for other shared libraries - continue - } - ldflags = append(ldflags, d.p.ImportPath+"="+d.target) - } - return b.run(".", a.target, nil, buildToolExec, tool("link"), "-o", a.target, importArgs, ldflags) + allactions = allactions[:len(allactions)-1] + return buildToolchain.ldShared(b, a.deps, a.target, allactions) } // install is the action for installing a single package or executable. @@ -2062,8 +2029,10 @@ type toolchain interface { // an archive from a set of object files. // typically it is run in the object directory. pack(b *builder, p *Package, objDir, afile string, ofiles []string) error - // ld runs the linker to create a package starting at mainpkg. - ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error + // ld runs the linker to create an executable starting at mainpkg. + ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error + // ldShared runs the linker to create a shared library containing the pkgs built by toplevelactions + ldShared(b *builder, toplevelactions []*action, out string, allactions []*action) error compiler() string linker() string @@ -2103,7 +2072,11 @@ func (noToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []s return noCompiler() } -func (noToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { +func (noToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error { + return noCompiler() +} + +func (noToolchain) ldShared(b *builder, toplevelactions []*action, out string, allactions []*action) error { return noCompiler() } @@ -2326,9 +2299,39 @@ func packInternal(b *builder, afile string, ofiles []string) error { return dst.Close() } -func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { +// setextld sets the appropriate linker flags for the specified compiler. +func setextld(ldflags []string, compiler []string) []string { + for _, f := range ldflags { + if f == "-extld" || strings.HasPrefix(f, "-extld=") { + // don't override -extld if supplied + return ldflags + } + } + ldflags = append(ldflags, "-extld="+compiler[0]) + if len(compiler) > 1 { + extldflags := false + add := strings.Join(compiler[1:], " ") + for i, f := range ldflags { + if f == "-extldflags" && i+1 < len(ldflags) { + ldflags[i+1] = add + " " + ldflags[i+1] + extldflags = true + break + } else if strings.HasPrefix(f, "-extldflags=") { + ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):] + extldflags = true + break + } + } + if !extldflags { + ldflags = append(ldflags, "-extldflags="+add) + } + } + return ldflags +} + +func (gcToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error { importArgs := b.includeArgs("-L", allactions) - cxx := len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0 + cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0 for _, a := range allactions { if a.p != nil && (len(a.p.CXXFiles) > 0 || len(a.p.SwigCXXFiles) > 0) { cxx = true @@ -2338,7 +2341,7 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, if buildContext.InstallSuffix != "" { ldflags = append(ldflags, "-installsuffix", buildContext.InstallSuffix) } - if p.omitDWARF { + if root.p.omitDWARF { ldflags = append(ldflags, "-w") } @@ -2354,11 +2357,42 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, } ldflags = setextld(ldflags, compiler) ldflags = append(ldflags, "-buildmode="+ldBuildmode) - if p.buildID != "" { - ldflags = append(ldflags, "-buildid="+p.buildID) + if root.p.buildID != "" { + ldflags = append(ldflags, "-buildid="+root.p.buildID) } ldflags = append(ldflags, buildLdflags...) - return b.run(".", p.ImportPath, nil, buildToolExec, tool("link"), "-o", out, importArgs, ldflags, mainpkg) + return b.run(".", root.p.ImportPath, nil, buildToolExec, tool("link"), "-o", out, importArgs, ldflags, mainpkg) +} + +func (gcToolchain) ldShared(b *builder, toplevelactions []*action, out string, allactions []*action) error { + importArgs := b.includeArgs("-L", allactions) + ldflags := []string{"-installsuffix", buildContext.InstallSuffix} + ldflags = append(ldflags, "-buildmode=shared") + ldflags = append(ldflags, buildLdflags...) + cxx := false + for _, a := range allactions { + if a.p != nil && (len(a.p.CXXFiles) > 0 || len(a.p.SwigCXXFiles) > 0) { + cxx = true + } + } + // If the user has not specified the -extld option, then specify the + // appropriate linker. In case of C++ code, use the compiler named + // by the CXX environment variable or defaultCXX if CXX is not set. + // Else, use the CC environment variable and defaultCC as fallback. + var compiler []string + if cxx { + compiler = envList("CXX", defaultCXX) + } else { + compiler = envList("CC", defaultCC) + } + ldflags = setextld(ldflags, compiler) + for _, d := range toplevelactions { + if !strings.HasSuffix(d.target, ".a") { // omit unsafe etc and actions for other shared libraries + continue + } + ldflags = append(ldflags, d.p.ImportPath+"="+d.target) + } + return b.run(".", out, nil, buildToolExec, tool("link"), "-o", out, importArgs, ldflags) } func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { @@ -2432,26 +2466,42 @@ func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles return b.run(p.Dir, p.ImportPath, nil, "ar", "cru", mkAbs(objDir, afile), absOfiles) } -func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { +func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error { // gccgo needs explicit linking with all package dependencies, // and all LDFLAGS from cgo dependencies. apackagesSeen := make(map[*Package]bool) afiles := []string{} + shlibs := []string{} xfiles := []string{} ldflags := b.gccArchArgs() cgoldflags := []string{} usesCgo := false - cxx := len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0 - objc := len(p.MFiles) > 0 - - // Prefer the output of an install action to the output of a build action, - // because the install action will delete the output of the build action. - // Iterate over the list backward (reverse dependency order) so that we - // always see the install before the build. - for i := len(allactions) - 1; i >= 0; i-- { - a := allactions[i] - if !a.p.Standard { - if a.p != nil && !apackagesSeen[a.p] { + cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0 + objc := len(root.p.MFiles) > 0 + + actionsSeen := make(map[*action]bool) + // Make a pre-order depth-first traversal of the action graph, taking note of + // whether a shared library action has been seen on the way to an action (the + // construction of the graph means that if any path to a node passes through + // a shared library action, they all do). + var walk func(a *action, seenShlib bool) + walk = func(a *action, seenShlib bool) { + if actionsSeen[a] { + return + } + actionsSeen[a] = true + if a.p != nil && !seenShlib { + if a.p.Standard { + return + } + // We record the target of the first time we see a .a file + // for a package to make sure that we prefer the 'install' + // rather than the 'build' location (which may not exist any + // more). We still need to traverse the dependencies of the + // build action though so saying + // if apackagesSeen[a.p] { return } + // doesn't work. + if !apackagesSeen[a.p] { apackagesSeen[a.p] = true if a.p.fake && a.p.external { // external _tests, if present must come before @@ -2466,6 +2516,16 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] } } } + if strings.HasSuffix(a.target, ".so") { + shlibs = append(shlibs, a.target) + seenShlib = true + } + for _, a1 := range a.deps { + walk(a1, seenShlib) + } + } + for _, a1 := range root.deps { + walk(a1, false) } afiles = append(xfiles, afiles...) @@ -2474,6 +2534,9 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] // 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 == nil { + continue + } if !a.p.Standard { cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...) } @@ -2505,10 +2568,20 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] ldflags = append(ldflags, cgoldflags...) ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...) - ldflags = append(ldflags, p.CgoLDFLAGS...) + ldflags = append(ldflags, root.p.CgoLDFLAGS...) ldflags = stringList("-Wl,-(", ldflags, "-Wl,-)") + for _, shlib := range shlibs { + ldflags = append( + ldflags, + "-L"+filepath.Dir(shlib), + "-Wl,-rpath="+filepath.Dir(shlib), + "-l"+strings.TrimSuffix( + strings.TrimPrefix(filepath.Base(shlib), "lib"), + ".so")) + } + var realOut string switch ldBuildmode { case "exe": @@ -2556,20 +2629,43 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] } } - if err := b.run(".", p.ImportPath, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil { + if err := b.run(".", root.p.ImportPath, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil { return err } switch ldBuildmode { case "c-archive": - if err := b.run(".", p.ImportPath, nil, "ar", "rc", realOut, out); err != nil { + if err := b.run(".", root.p.ImportPath, nil, "ar", "rc", realOut, out); err != nil { return err } } - return nil } +func (tools gccgoToolchain) ldShared(b *builder, toplevelactions []*action, out string, allactions []*action) error { + args := []string{"-o", out, "-shared", "-nostdlib", "-zdefs", "-Wl,--whole-archive"} + for _, a := range toplevelactions { + args = append(args, a.target) + } + args = append(args, "-Wl,--no-whole-archive", "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc") + shlibs := []string{} + for _, a := range allactions { + if strings.HasSuffix(a.target, ".so") { + shlibs = append(shlibs, a.target) + } + } + for _, shlib := range shlibs { + args = append( + args, + "-L"+filepath.Dir(shlib), + "-Wl,-rpath="+filepath.Dir(shlib), + "-l"+strings.TrimSuffix( + strings.TrimPrefix(filepath.Base(shlib), "lib"), + ".so")) + } + return b.run(".", out, nil, tools.linker(), args, buildGccgoflags) +} + func (tools gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { inc := filepath.Join(goroot, "pkg", "include") cfile = mkAbs(p.Dir, cfile) diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index d04f5bc97b..cf3e8b38aa 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -530,7 +530,12 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package shlib, err := ioutil.ReadFile(shlibnamefile) if err == nil { libname := strings.TrimSpace(string(shlib)) - p.Shlib = filepath.Join(p.build.PkgTargetRoot, libname) + if buildContext.Compiler == "gccgo" { + p.Shlib = filepath.Join(p.build.PkgTargetRoot, "shlibs", libname) + } else { + p.Shlib = filepath.Join(p.build.PkgTargetRoot, libname) + + } } else if !os.IsNotExist(err) { fatalf("unexpected error reading %s: %v", shlibnamefile, err) } -- 2.50.0