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) }
--- /dev/null
+// 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)
+}
--- /dev/null
+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)))
+}
--- /dev/null
+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)))
+}
"bytes"
"flag"
"fmt"
+ "io"
"os"
"os/exec"
"path/filepath"
setup()
checkCC()
+ copyLibgcc()
bootstrapBuildTools()
// For the main bootstrap, building for host os/arch.
}
}
+// 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
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))
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)
}
}
- 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 {
package ld
+import (
+ "cmd/internal/obj"
+ "encoding/binary"
+ "fmt"
+ "os"
+)
+
const (
SARMAG = 8
SAR_HDR = 16 + 44
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
+}
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
}
// 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()
}
"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] {
h.file = file
h.off = obj.Boffset(f)
h.length = length
+ return h
}
func hostobjs() {
}
}
-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)
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 */
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 ") {
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.
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,
theline = line[10:]
} else if theline != line[10:] {
Diag("%s: object is [%s] expected [%s]", pn, line[10:], theline)
- return
+ return nil
}
}
c3 = obj.Bgetc(f)
if c3 == obj.Beof {
Diag("truncated object file: %s", pn)
- return
+ return nil
}
}
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 {