aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Crawshaw <crawshaw@golang.org>2015-04-09 10:44:05 -0400
committerDavid Crawshaw <crawshaw@golang.org>2015-04-12 14:00:32 +0000
commitced7ffe95ba176b26ef835b2f225255d1cd7808f (patch)
tree03e5cbdffd2b109b0fd304d98b2cf5aa1274ffba /src
parent6e3a6c4d384bb1ca532727bbd7a1cd221786c42a (diff)
downloadgo-ced7ffe95ba176b26ef835b2f225255d1cd7808f.tar.xz
cmd/interal/ld: darwin c-archive buildmode support
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>
Diffstat (limited to 'src')
-rw-r--r--src/cmd/internal/ld/data.go11
-rw-r--r--src/cmd/internal/ld/elf.go4
-rw-r--r--src/cmd/internal/ld/go.go9
-rw-r--r--src/cmd/internal/ld/lib.go138
-rw-r--r--src/cmd/internal/ld/pobj.go1
5 files changed, 110 insertions, 53 deletions
diff --git a/src/cmd/internal/ld/data.go b/src/cmd/internal/ld/data.go
index e9a890d84f..75c8dea96d 100644
--- a/src/cmd/internal/ld/data.go
+++ b/src/cmd/internal/ld/data.go
@@ -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))
diff --git a/src/cmd/internal/ld/elf.go b/src/cmd/internal/ld/elf.go
index a7674da311..0de24fa6fd 100644
--- a/src/cmd/internal/ld/elf.go
+++ b/src/cmd/internal/ld/elf.go
@@ -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':
diff --git a/src/cmd/internal/ld/go.go b/src/cmd/internal/ld/go.go
index 1d83081025..47e9933d83 100644
--- a/src/cmd/internal/ld/go.go
+++ b/src/cmd/internal/ld/go.go
@@ -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 {
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index 02d93af6d6..b87f83c177 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -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 {
diff --git a/src/cmd/internal/ld/pobj.go b/src/cmd/internal/ld/pobj.go
index c4e779df7a..f5dd2d7944 100644
--- a/src/cmd/internal/ld/pobj.go
+++ b/src/cmd/internal/ld/pobj.go
@@ -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)