diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index 948a0eab648a944d6c4f49d8de58b5c9f2a6d640..08800479536dcaa033fed2322f00735cb989d185 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -62,6 +62,7 @@ func Test8811(t *testing.T) { test8811(t) } func TestReturnAfterGrow(t *testing.T) { testReturnAfterGrow(t) } func TestReturnAfterGrowFromGo(t *testing.T) { testReturnAfterGrowFromGo(t) } func Test9026(t *testing.T) { test9026(t) } +func Test9510(t *testing.T) { test9510(t) } func Test9557(t *testing.T) { test9557(t) } func Test10303(t *testing.T) { test10303(t, 10) } func Test11925(t *testing.T) { test11925(t) } diff --git a/misc/cgo/test/issue9510.go b/misc/cgo/test/issue9510.go new file mode 100644 index 0000000000000000000000000000000000000000..a940bfba6f39392627dd90b630991edb1456eefd --- /dev/null +++ b/misc/cgo/test/issue9510.go @@ -0,0 +1,24 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that we can link together two different cgo packages that both +// use the same libgcc function. + +package cgotest + +import ( + "runtime" + "testing" + + "./issue9510a" + "./issue9510b" +) + +func test9510(t *testing.T) { + if runtime.GOARCH == "arm" { + t.Skip("skipping because libgcc may be a Thumb library") + } + issue9510a.F(1, 1) + issue9510b.F(1, 1) +} diff --git a/misc/cgo/test/issue9510a/a.go b/misc/cgo/test/issue9510a/a.go new file mode 100644 index 0000000000000000000000000000000000000000..1a5224b8c6882a7156deaebbaa1cad834fbbd0cf --- /dev/null +++ b/misc/cgo/test/issue9510a/a.go @@ -0,0 +1,15 @@ +package issue9510a + +/* +static double csquare(double a, double b) { + __complex__ double d; + __real__ d = a; + __imag__ d = b; + return __real__ (d * d); +} +*/ +import "C" + +func F(a, b float64) float64 { + return float64(C.csquare(C.double(a), C.double(b))) +} diff --git a/misc/cgo/test/issue9510b/b.go b/misc/cgo/test/issue9510b/b.go new file mode 100644 index 0000000000000000000000000000000000000000..5016b39597e13439c7e800df0727467eee18eccb --- /dev/null +++ b/misc/cgo/test/issue9510b/b.go @@ -0,0 +1,15 @@ +package issue9510b + +/* +static double csquare(double a, double b) { + __complex__ double d; + __real__ d = a; + __imag__ d = b; + return __real__ (d * d); +} +*/ +import "C" + +func F(a, b float64) float64 { + return float64(C.csquare(C.double(a), C.double(b))) +} diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 54e3fdf0409159241d1d6bd61a3014c44a66a0ad..e06fca63d58be217e3603bcdc8c7003a6eddffa9 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -8,6 +8,7 @@ import ( "bytes" "flag" "fmt" + "io" "os" "os/exec" "path/filepath" @@ -1002,6 +1003,7 @@ func cmdbootstrap() { setup() checkCC() + copyLibgcc() bootstrapBuildTools() // For the main bootstrap, building for host os/arch. @@ -1110,6 +1112,53 @@ func checkCC() { } } +// copyLibgcc copies the C compiler's libgcc into the pkg directory. +func copyLibgcc() { + if !needCC() { + return + } + var args []string + switch goarch { + case "386": + args = []string{"-m32"} + case "amd64", "amd64p32": + args = []string{"-m64"} + case "arm": + args = []string{"-marm"} + } + args = append(args, "--print-libgcc-file-name") + output, err := exec.Command(defaultcctarget, args...).Output() + if err != nil { + fatal("cannot find libgcc file name: %v", err) + } + libgcc := strings.TrimSpace(string(output)) + if len(libgcc) == 0 { + return + } + in, err := os.Open(libgcc) + if err != nil { + if os.IsNotExist(err) { + return + } + fatal("cannot open libgcc for copying: %v", err) + } + defer in.Close() + outdir := filepath.Join(goroot, "pkg", "libgcc", goos+"_"+goarch) + if err := os.MkdirAll(outdir, 0777); err != nil { + fatal("cannot create libgcc.a directory: %v", err) + } + out, err := os.Create(filepath.Join(outdir, "libgcc")) + if err != nil { + fatal("cannot create libgcc.a for copying: %v", err) + } + if _, err := io.Copy(out, in); err != nil { + fatal("error copying libgcc: %v", err) + } + if err := out.Close(); err != nil { + fatal("error closing new libgcc: %v", err) + } +} + func defaulttarg() string { // xgetwd might return a path with symlinks fully resolved, and if // there happens to be symlinks in goroot, then the hasprefix test diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 13e98c4a8bed8492b444aaeef07a2014e4f8ada5..c29f6a78a0ee1ac8b3e93325430acba1aa822051 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -2753,43 +2753,6 @@ func gccgoCleanPkgpath(p *Package) string { return strings.Map(clean, gccgoPkgpath(p)) } -// libgcc returns the filename for libgcc, as determined by invoking gcc with -// the -print-libgcc-file-name option. -func (b *builder) libgcc(p *Package) (string, error) { - var buf bytes.Buffer - - gccCmd := b.gccCmd(p.Dir) - - prev := b.print - if buildN { - // In -n mode we temporarily swap out the builder's - // print function to capture the command-line. This - // let's us assign it to $LIBGCC and produce a valid - // buildscript for cgo packages. - b.print = func(a ...interface{}) (int, error) { - return fmt.Fprint(&buf, a...) - } - } - f, err := b.runOut(p.Dir, p.ImportPath, nil, gccCmd, "-print-libgcc-file-name") - if err != nil { - return "", fmt.Errorf("gcc -print-libgcc-file-name: %v (%s)", err, f) - } - if buildN { - s := fmt.Sprintf("LIBGCC=$(%s)\n", buf.Next(buf.Len()-1)) - b.print = prev - b.print(s) - return "$LIBGCC", nil - } - - // The compiler might not be able to find libgcc, and in that case, - // it will simply return "libgcc.a", which is of no use to us. - if !filepath.IsAbs(string(f)) { - return "", nil - } - - return strings.Trim(string(f), "\r\n"), nil -} - // gcc runs the gcc C compiler to create an object from a single C file. func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error { return b.ccompile(p, out, flags, cfile, b.gccCmd(p.Dir)) @@ -2915,12 +2878,6 @@ func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldfl var cgoRe = regexp.MustCompile(`[/\\:]`) -var ( - cgoLibGccFile string - cgoLibGccErr error - cgoLibGccFileOnce sync.Once -) - func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true) _, cgoexeCFLAGS, _, _ := b.cflags(p, false) @@ -3049,22 +3006,12 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi } } - cgoLibGccFileOnce.Do(func() { - cgoLibGccFile, cgoLibGccErr = b.libgcc(p) - }) - if cgoLibGccFile == "" && cgoLibGccErr != nil { - return nil, nil, err - } - var staticLibs []string if goos == "windows" { - // libmingw32 and libmingwex might also use libgcc, so libgcc must come last, - // and they also have some inter-dependencies, so must use linker groups. + // libmingw32 and libmingwex have some inter-dependencies, + // so must use linker groups. staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"} } - if cgoLibGccFile != "" { - staticLibs = append(staticLibs, cgoLibGccFile) - } cflags := stringList(cgoCPPFLAGS, cgoCFLAGS) for _, cfile := range cfiles { diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go index a596e9a9697fc23a68c4d8aaf2cd4fdaedff359b..860a94df02302d617675d6d9cacb26715ea5c6ac 100644 --- a/src/cmd/link/internal/ld/ar.go +++ b/src/cmd/link/internal/ld/ar.go @@ -30,6 +30,13 @@ package ld +import ( + "cmd/internal/obj" + "encoding/binary" + "fmt" + "os" +) + const ( SARMAG = 8 SAR_HDR = 16 + 44 @@ -48,3 +55,128 @@ type ArHdr struct { size string fmag string } + +// hostArchive reads an archive file holding host objects and links in +// required objects. The general format is the same as a Go archive +// file, but it has an armap listing symbols and the objects that +// define them. This is used for the compiler support library +// libgcc.a. +func hostArchive(name string) { + f, err := obj.Bopenr(name) + if err != nil { + if os.IsNotExist(err) { + // It's OK if we don't have a libgcc file at all. + return + } + Exitf("cannot open file %s: %v", name, err) + } + defer obj.Bterm(f) + + magbuf := make([]byte, len(ARMAG)) + if obj.Bread(f, magbuf) != len(magbuf) { + Exitf("file %s too short", name) + } + + var arhdr ArHdr + l := nextar(f, obj.Boffset(f), &arhdr) + if l <= 0 { + Exitf("%s missing armap", name) + } + + var armap archiveMap + if arhdr.name == "/" || arhdr.name == "/SYM64/" { + armap = readArmap(name, f, arhdr) + } else { + Exitf("%s missing armap", name) + } + + loaded := make(map[uint64]bool) + any := true + for any { + var load []uint64 + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + for _, r := range s.R { + if r.Sym != nil && r.Sym.Type&obj.SMASK == obj.SXREF { + if off := armap[r.Sym.Name]; off != 0 && !loaded[off] { + load = append(load, off) + loaded[off] = true + } + } + } + } + + for _, off := range load { + l := nextar(f, int64(off), &arhdr) + if l <= 0 { + Exitf("%s missing archive entry at offset %d", name, off) + } + pname := fmt.Sprintf("%s(%s)", name, arhdr.name) + l = atolwhex(arhdr.size) + + h := ldobj(f, "libgcc", l, pname, name, ArchiveObj) + obj.Bseek(f, h.off, 0) + h.ld(f, h.pkg, h.length, h.pn) + } + + any = len(load) > 0 + } +} + +// archiveMap is an archive symbol map: a mapping from symbol name to +// offset within the archive file. +type archiveMap map[string]uint64 + +// readArmap reads the archive symbol map. +func readArmap(filename string, f *obj.Biobuf, arhdr ArHdr) archiveMap { + is64 := arhdr.name == "/SYM64/" + wordSize := 4 + if is64 { + wordSize = 8 + } + + l := atolwhex(arhdr.size) + contents := make([]byte, l) + if obj.Bread(f, contents) != int(l) { + Exitf("short read from %s", filename) + } + + var c uint64 + if is64 { + c = binary.BigEndian.Uint64(contents) + } else { + c = uint64(binary.BigEndian.Uint32(contents)) + } + contents = contents[wordSize:] + + ret := make(archiveMap) + + names := contents[c*uint64(wordSize):] + for i := uint64(0); i < c; i++ { + n := 0 + for names[n] != 0 { + n++ + } + name := string(names[:n]) + names = names[n+1:] + + // For Mach-O and PE/386 files we strip a leading + // underscore from the symbol name. + if goos == "darwin" || (goos == "windows" && goarch == "386") { + if name[0] == '_' && len(name) > 1 { + name = name[1:] + } + } + + var off uint64 + if is64 { + off = binary.BigEndian.Uint64(contents) + } else { + off = uint64(binary.BigEndian.Uint32(contents)) + } + contents = contents[wordSize:] + + ret[name] = off + } + + return ret +} diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index 280edcdb398a03fcaca8b015a84293c436f5886d..66bf61321d85e287246ca1e87548753446b4ff30 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -1076,7 +1076,7 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) { case ElfSymBindWeak: if needSym != 0 { - s = linknewsym(Ctxt, sym.name, 0) + s = Linklookup(Ctxt, sym.name, 0) if sym.other == 2 { s.Type |= obj.SHIDDEN } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index e2ffa1a123036877fb36fe5110800a30b73a051a..9749355ddbe4ad4f1bcadcdafd1c7316e8252525 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -642,6 +642,22 @@ func loadlib() { // In internal link mode, read the host object files. if Linkmode == LinkInternal { hostobjs() + + // If we have any undefined symbols in external + // objects, try to read them from our copy of the C + // compiler support library, libgcc.a. + any := false + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + for _, r := range s.R { + if r.Sym != nil && r.Sym.Type&obj.SMASK == obj.SXREF { + any = true + break + } + } + } + if any { + hostArchive(fmt.Sprintf("%s/pkg/libgcc/%s_%s/libgcc", goroot, goos, goarch)) + } } else { hostlinksetup() } @@ -819,7 +835,7 @@ var internalpkg = []string{ "runtime/msan", } -func ldhostobj(ld func(*obj.Biobuf, string, int64, string), f *obj.Biobuf, pkg string, length int64, pn string, file string) { +func ldhostobj(ld func(*obj.Biobuf, string, int64, string), f *obj.Biobuf, pkg string, length int64, pn string, file string) *Hostobj { isinternal := false for i := 0; i < len(internalpkg); i++ { if pkg == internalpkg[i] { @@ -852,6 +868,7 @@ func ldhostobj(ld func(*obj.Biobuf, string, int64, string), f *obj.Biobuf, pkg s h.file = file h.off = obj.Boffset(f) h.length = length + return h } func hostobjs() { @@ -1190,7 +1207,10 @@ func hostlink() { } } -func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, whence int) { +// ldobj loads an input object. If it is a host object (an object +// compiled by a non-Go compiler) it returns the Hostobj pointer. If +// it is a Go object, it returns nil. +func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, whence int) *Hostobj { eof := obj.Boffset(f) + length start := obj.Boffset(f) @@ -1202,18 +1222,15 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) if magic == 0x7f454c46 { // \x7F E L F - ldhostobj(ldelf, f, pkg, length, pn, file) - return + return ldhostobj(ldelf, f, pkg, length, pn, file) } if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { - ldhostobj(ldmacho, f, pkg, length, pn, file) - return + return ldhostobj(ldmacho, f, pkg, length, pn, file) } if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 { - ldhostobj(ldpe, f, pkg, length, pn, file) - return + return ldhostobj(ldpe, f, pkg, length, pn, file) } /* check the header */ @@ -1221,10 +1238,10 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when if line == "" { if obj.Blinelen(f) > 0 { Diag("%s: not an object file", pn) - return + return nil } Diag("truncated object file: %s", pn) - return + return nil } if !strings.HasPrefix(line, "go object ") { @@ -1235,11 +1252,11 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when if line == Thestring { // old header format: just $GOOS Diag("%s: stale object file", pn) - return + return nil } Diag("%s: not an object file", pn) - return + return nil } // First, check that the basic goos, goarch, and version match. @@ -1248,7 +1265,7 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when line = strings.TrimRight(line, "\n") if !strings.HasPrefix(line[10:]+" ", t) && Debug['f'] == 0 { Diag("%s: object is [%s] expected [%s]", pn, line[10:], t) - return + return nil } // Second, check that longer lines match each other exactly, @@ -1259,7 +1276,7 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when theline = line[10:] } else if theline != line[10:] { Diag("%s: object is [%s] expected [%s]", pn, line[10:], theline) - return + return nil } } @@ -1275,7 +1292,7 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when c3 = obj.Bgetc(f) if c3 == obj.Beof { Diag("truncated object file: %s", pn) - return + return nil } } @@ -1286,6 +1303,7 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when obj.Bseek(f, import1, 0) ldobjfile(Ctxt, f, pkg, eof-obj.Boffset(f), pn) + return nil } func readelfsymboldata(f *elf.File, sym *elf.Symbol) []byte {