]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/interal/ld: darwin c-archive buildmode support
authorDavid Crawshaw <crawshaw@golang.org>
Thu, 9 Apr 2015 14:44:05 +0000 (10:44 -0400)
committerDavid Crawshaw <crawshaw@golang.org>
Sun, 12 Apr 2015 14:00:32 +0000 (14:00 +0000)
Uses ar to create an archive when -buildmode=c-archive.

A small example (that I hope to turn into a test in a later CL):

goarchive.go:
package main

import "fmt"

import "C"

func init() {
fmt.Println("ran go init")
}

//export FuncInGo
func FuncInGo() {
fmt.Println("called a go function")
}

func main() {
fmt.Println("in main")
}

This can be compiled with:

go build -ldflags=-buildmode=c-archive -o=libgo.a goarchive.go

main.c:

#include <stdio.h>

extern void FuncInGo();

int main(void) {
printf("c hello\n");
FuncInGo();
printf("c goodbye\n");
return 0;
}

Can be compiled with:

cc main.c libgo.a

Apple provide a warning about the lack of PIE, but still produce a
binary which runs and outputs (on darwin/amd64):

c hello
ran go init
called a go function
c goodbye

Change-Id: I7611925f210a83afa6bd1e66a5601dd636a428c8
Reviewed-on: https://go-review.googlesource.com/8711
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/cmd/internal/ld/data.go
src/cmd/internal/ld/elf.go
src/cmd/internal/ld/go.go
src/cmd/internal/ld/lib.go
src/cmd/internal/ld/pobj.go

index e9a890d84fd856fca429af7df014aaf45b137a39..75c8dea96dc92d7f685f2cc2243a975260f90c2f 100644 (file)
@@ -967,8 +967,11 @@ func dosymtype() {
                }
                // Create a new entry in the .init_array section that points to the
                // library initializer function.
-               if Buildmode == BuildmodeCShared && s.Name == INITENTRY {
-                       addinitarrdata(s)
+               switch Buildmode {
+               case BuildmodeCArchive, BuildmodeCShared:
+                       if s.Name == INITENTRY {
+                               addinitarrdata(s)
+                       }
                }
        }
 }
@@ -1329,7 +1332,9 @@ func dodata() {
        sect.Length = uint64(datsize) - sect.Vaddr
 
        /* shared library initializer */
-       if Buildmode == BuildmodeCShared || DynlinkingGo() {
+       switch Buildmode {
+       case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
+               // TODO(mwhudson): switch on Linkshared
                sect := addsection(&Segdata, ".init_array", 06)
                sect.Align = maxalign(s, SINITARR)
                datsize = Rnd(datsize, int64(sect.Align))
index a7674da3117d140e4cc04e3bac4398c7b2134155..0de24fa6fd8a052a83760bf5d1859f49dd08d574 100644 (file)
@@ -1658,7 +1658,9 @@ func doelf() {
                Addstring(shstrtab, ".note.GNU-stack")
        }
 
-       if Buildmode == BuildmodeCShared || DynlinkingGo() {
+       switch Buildmode {
+       case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
+               // TODO(mwhudson): switch on Linkshared
                Addstring(shstrtab, ".init_array")
                switch Thearch.Thechar {
                case '6', '7', '9':
index 1d83081025fbba8eaef4bc95aa0f1a843cc04a88..47e9933d83a666217dc6bc7db6c2ea95c81df2f6 100644 (file)
@@ -474,8 +474,11 @@ func loadcgo(file string, pkg string, p string) {
                        local = expandpkg(local, pkg)
                        s = Linklookup(Ctxt, local, 0)
 
-                       if Buildmode == BuildmodeCShared && s == Linklookup(Ctxt, "main", 0) {
-                               continue
+                       switch Buildmode {
+                       case BuildmodeCShared, BuildmodeCArchive:
+                               if s == Linklookup(Ctxt, "main", 0) {
+                                       continue
+                               }
                        }
 
                        // export overrides import, for openbsd/cgo.
@@ -619,7 +622,7 @@ func deadcode() {
                fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime())
        }
 
-       if Buildmode == BuildmodeShared {
+       if Buildmode == BuildmodeShared || Buildmode == BuildmodeCArchive {
                // Mark all symbols as reachable when building a
                // shared library.
                for s := Ctxt.Allsym; s != nil; s = s.Allsym {
index 02d93af6d6d42f03d86e407572791b08b67359ec..b87f83c17726bd77e58729e46ede98b274a2311f 100644 (file)
@@ -34,7 +34,6 @@ import (
        "bytes"
        "cmd/internal/obj"
        "debug/elf"
-       "errors"
        "fmt"
        "io"
        "io/ioutil"
@@ -257,27 +256,35 @@ type BuildMode uint8
 
 const (
        BuildmodeExe BuildMode = iota
+       BuildmodeCArchive
        BuildmodeCShared
        BuildmodeShared
 )
 
 func (mode *BuildMode) Set(s string) error {
+       goos := obj.Getgoos()
+       goarch := obj.Getgoarch()
+       badmode := func() error {
+               return fmt.Errorf("buildmode %s not supported on %s/%s", s, goos, goarch)
+       }
        switch s {
        default:
-               return errors.New("invalid mode")
+               return fmt.Errorf("invalid buildmode: %q", s)
        case "exe":
                *mode = BuildmodeExe
+       case "c-archive":
+               if goos != "darwin" {
+                       return badmode()
+               }
+               *mode = BuildmodeCArchive
        case "c-shared":
-               goarch := obj.Getgoarch()
                if goarch != "amd64" && goarch != "arm" {
-                       return fmt.Errorf("not supported on %s", goarch)
+                       return badmode()
                }
                *mode = BuildmodeCShared
        case "shared":
-               goos := obj.Getgoos()
-               goarch := obj.Getgoarch()
                if goos != "linux" || goarch != "amd64" {
-                       return fmt.Errorf("not supported on %s/%s", goos, goarch)
+                       return badmode()
                }
                *mode = BuildmodeShared
        }
@@ -288,6 +295,8 @@ func (mode *BuildMode) String() string {
        switch *mode {
        case BuildmodeExe:
                return "exe"
+       case BuildmodeCArchive:
+               return "c-archive"
        case BuildmodeCShared:
                return "c-shared"
        case BuildmodeShared:
@@ -339,7 +348,7 @@ func libinit() {
 
        if INITENTRY == "" {
                switch Buildmode {
-               case BuildmodeCShared:
+               case BuildmodeCShared, BuildmodeCArchive:
                        INITENTRY = fmt.Sprintf("_rt0_%s_%s_lib", goarch, goos)
                case BuildmodeExe:
                        INITENTRY = fmt.Sprintf("_rt0_%s_%s", goarch, goos)
@@ -402,10 +411,15 @@ func loadinternal(name string) {
 }
 
 func loadlib() {
-       if Buildmode == BuildmodeCShared {
+       switch Buildmode {
+       case BuildmodeCShared:
                s := Linklookup(Ctxt, "runtime.islibrary", 0)
                s.Dupok = 1
                Adduint8(Ctxt, s, 1)
+       case BuildmodeCArchive:
+               s := Linklookup(Ctxt, "runtime.isarchive", 0)
+               s.Dupok = 1
+               Adduint8(Ctxt, s, 1)
        }
 
        loadinternal("runtime")
@@ -782,14 +796,79 @@ func hostlinksetup() {
        coutbuf = *Binitw(cout)
 }
 
+// hostobjCopy creates a copy of the object files in hostobj in a
+// temporary directory.
+func hostobjCopy() (paths []string) {
+       for i, h := range hostobj {
+               f, err := os.Open(h.file)
+               if err != nil {
+                       Ctxt.Cursym = nil
+                       Diag("cannot reopen %s: %v", h.pn, err)
+                       Errorexit()
+               }
+               if _, err := f.Seek(h.off, 0); err != nil {
+                       Ctxt.Cursym = nil
+                       Diag("cannot seek %s: %v", h.pn, err)
+                       Errorexit()
+               }
+
+               p := fmt.Sprintf("%s/%06d.o", tmpdir, i)
+               paths = append(paths, p)
+               w, err := os.Create(p)
+               if err != nil {
+                       Ctxt.Cursym = nil
+                       Diag("cannot create %s: %v", p, err)
+                       Errorexit()
+               }
+               if _, err := io.CopyN(w, f, h.length); err != nil {
+                       Ctxt.Cursym = nil
+                       Diag("cannot write %s: %v", p, err)
+                       Errorexit()
+               }
+               if err := w.Close(); err != nil {
+                       Ctxt.Cursym = nil
+                       Diag("cannot close %s: %v", p, err)
+                       Errorexit()
+               }
+       }
+       return paths
+}
+
+// archive builds a .a archive from the hostobj object files.
+func archive() {
+       if Buildmode != BuildmodeCArchive {
+               return
+       }
+
+       os.Remove(outfile)
+       argv := []string{"ar", "-q", "-c", outfile}
+       argv = append(argv, hostobjCopy()...)
+       argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir))
+
+       if Debug['v'] != 0 {
+               fmt.Fprintf(&Bso, "archive: %s\n", strings.Join(argv, " "))
+               Bflush(&Bso)
+       }
+
+       if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
+               Ctxt.Cursym = nil
+               Diag("%s: running %s failed: %v\n%s", os.Args[0], argv[0], err, out)
+               Errorexit()
+       }
+}
+
 func hostlink() {
        if Linkmode != LinkExternal || nerrors > 0 {
                return
        }
+       if Buildmode == BuildmodeCArchive {
+               return
+       }
 
        if extld == "" {
                extld = "gcc"
        }
+
        var argv []string
        argv = append(argv, extld)
        switch Thearch.Thechar {
@@ -830,10 +909,11 @@ func hostlink() {
                argv = append(argv, "-Wl,--rosegment")
        }
 
-       if Buildmode == BuildmodeCShared {
+       switch Buildmode {
+       case BuildmodeCShared:
                argv = append(argv, "-Wl,-Bsymbolic")
                argv = append(argv, "-shared")
-       } else if Buildmode == BuildmodeShared {
+       case BuildmodeShared:
                // TODO(mwhudson): unless you do this, dynamic relocations fill
                // out the findfunctab table and for some reason shared libraries
                // and the executable both define a main function and putting the
@@ -868,41 +948,7 @@ func hostlink() {
                argv = append(argv, "-Qunused-arguments")
        }
 
-       // already wrote main object file
-       // copy host objects to temporary directory
-       for i, h := range hostobj {
-               f, err := os.Open(h.file)
-               if err != nil {
-                       Ctxt.Cursym = nil
-                       Diag("cannot reopen %s: %v", h.pn, err)
-                       Errorexit()
-               }
-               if _, err := f.Seek(h.off, 0); err != nil {
-                       Ctxt.Cursym = nil
-                       Diag("cannot seek %s: %v", h.pn, err)
-                       Errorexit()
-               }
-
-               p := fmt.Sprintf("%s/%06d.o", tmpdir, i)
-               argv = append(argv, p)
-               w, err := os.Create(p)
-               if err != nil {
-                       Ctxt.Cursym = nil
-                       Diag("cannot create %s: %v", p, err)
-                       Errorexit()
-               }
-               if _, err := io.CopyN(w, f, h.length); err != nil {
-                       Ctxt.Cursym = nil
-                       Diag("cannot write %s: %v", p, err)
-                       Errorexit()
-               }
-               if err := w.Close(); err != nil {
-                       Ctxt.Cursym = nil
-                       Diag("cannot close %s: %v", p, err)
-                       Errorexit()
-               }
-       }
-
+       argv = append(argv, hostobjCopy()...)
        argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir))
 
        if Linkshared {
index c4e779df7a9c10dc96e5fa2baabfa8cdcfa09785..f5dd2d79447d5ad3bb8d3e7bcda44855e187c507 100644 (file)
@@ -237,6 +237,7 @@ func Ldmain() {
        Thearch.Asmb()
        undef()
        hostlink()
+       archive()
        if Debug['v'] != 0 {
                fmt.Fprintf(&Bso, "%5.2f cpu time\n", obj.Cputime())
                fmt.Fprintf(&Bso, "%d symbols\n", Ctxt.Nsymbol)