]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link, cmd/go, cmd/dist: use copy of libgcc.a for internal linking
authorIan Lance Taylor <iant@golang.org>
Wed, 4 Nov 2015 19:14:19 +0000 (11:14 -0800)
committerIan Lance Taylor <iant@golang.org>
Sat, 14 Nov 2015 18:56:17 +0000 (18:56 +0000)
Change the linker to use a copy of the C compiler support library,
libgcc.a, when doing internal linking.  This will be used to satisfy any
undefined symbols referenced by host objects.

Change the dist tool to copy the support library into a new directory
tree under GOROOT/pkg/libgcc.  This ensures that libgcc is available
even when building Go programs on a system that has no C compiler.  The
C compiler is required when building the Go installation in the first
place, but is not required thereafter.

Change the go tool to not link libgcc into cgo objects.

Correct the linker handling of a weak symbol in an ELF input object to
not always create a new symbol, but to use an existing symbol if there
is one; this is necessary on freebsd-amd64, where libgcc contains a weak
definition of compilerrt_abort_impl.

Fixes #9510.

Change-Id: I1ab28182263238d9bcaf6a42804e5da2a87d8778
Reviewed-on: https://go-review.googlesource.com/16741
Reviewed-by: Russ Cox <rsc@golang.org>
misc/cgo/test/cgo_test.go
misc/cgo/test/issue9510.go [new file with mode: 0644]
misc/cgo/test/issue9510a/a.go [new file with mode: 0644]
misc/cgo/test/issue9510b/b.go [new file with mode: 0644]
src/cmd/dist/build.go
src/cmd/go/build.go
src/cmd/link/internal/ld/ar.go
src/cmd/link/internal/ld/ldelf.go
src/cmd/link/internal/ld/lib.go

index 948a0eab648a944d6c4f49d8de58b5c9f2a6d640..08800479536dcaa033fed2322f00735cb989d185 100644 (file)
@@ -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 (file)
index 0000000..a940bfb
--- /dev/null
@@ -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 (file)
index 0000000..1a5224b
--- /dev/null
@@ -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 (file)
index 0000000..5016b39
--- /dev/null
@@ -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)))
+}
index 54e3fdf0409159241d1d6bd61a3014c44a66a0ad..e06fca63d58be217e3603bcdc8c7003a6eddffa9 100644 (file)
@@ -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
index 13e98c4a8bed8492b444aaeef07a2014e4f8ada5..c29f6a78a0ee1ac8b3e93325430acba1aa822051 100644 (file)
@@ -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 {
index a596e9a9697fc23a68c4d8aaf2cd4fdaedff359b..860a94df02302d617675d6d9cacb26715ea5c6ac 100644 (file)
 
 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
+}
index 280edcdb398a03fcaca8b015a84293c436f5886d..66bf61321d85e287246ca1e87548753446b4ff30 100644 (file)
@@ -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
                                }
index e2ffa1a123036877fb36fe5110800a30b73a051a..9749355ddbe4ad4f1bcadcdafd1c7316e8252525 100644 (file)
@@ -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 {