mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
This commit is contained in:
parent
6e3a6c4d38
commit
ced7ffe95b
5 changed files with 110 additions and 53 deletions
|
|
@ -967,8 +967,11 @@ func dosymtype() {
|
||||||
}
|
}
|
||||||
// Create a new entry in the .init_array section that points to the
|
// Create a new entry in the .init_array section that points to the
|
||||||
// library initializer function.
|
// library initializer function.
|
||||||
if Buildmode == BuildmodeCShared && s.Name == INITENTRY {
|
switch Buildmode {
|
||||||
addinitarrdata(s)
|
case BuildmodeCArchive, BuildmodeCShared:
|
||||||
|
if s.Name == INITENTRY {
|
||||||
|
addinitarrdata(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1329,7 +1332,9 @@ func dodata() {
|
||||||
sect.Length = uint64(datsize) - sect.Vaddr
|
sect.Length = uint64(datsize) - sect.Vaddr
|
||||||
|
|
||||||
/* shared library initializer */
|
/* 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 := addsection(&Segdata, ".init_array", 06)
|
||||||
sect.Align = maxalign(s, SINITARR)
|
sect.Align = maxalign(s, SINITARR)
|
||||||
datsize = Rnd(datsize, int64(sect.Align))
|
datsize = Rnd(datsize, int64(sect.Align))
|
||||||
|
|
|
||||||
|
|
@ -1658,7 +1658,9 @@ func doelf() {
|
||||||
Addstring(shstrtab, ".note.GNU-stack")
|
Addstring(shstrtab, ".note.GNU-stack")
|
||||||
}
|
}
|
||||||
|
|
||||||
if Buildmode == BuildmodeCShared || DynlinkingGo() {
|
switch Buildmode {
|
||||||
|
case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
|
||||||
|
// TODO(mwhudson): switch on Linkshared
|
||||||
Addstring(shstrtab, ".init_array")
|
Addstring(shstrtab, ".init_array")
|
||||||
switch Thearch.Thechar {
|
switch Thearch.Thechar {
|
||||||
case '6', '7', '9':
|
case '6', '7', '9':
|
||||||
|
|
|
||||||
|
|
@ -474,8 +474,11 @@ func loadcgo(file string, pkg string, p string) {
|
||||||
local = expandpkg(local, pkg)
|
local = expandpkg(local, pkg)
|
||||||
s = Linklookup(Ctxt, local, 0)
|
s = Linklookup(Ctxt, local, 0)
|
||||||
|
|
||||||
if Buildmode == BuildmodeCShared && s == Linklookup(Ctxt, "main", 0) {
|
switch Buildmode {
|
||||||
continue
|
case BuildmodeCShared, BuildmodeCArchive:
|
||||||
|
if s == Linklookup(Ctxt, "main", 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// export overrides import, for openbsd/cgo.
|
// export overrides import, for openbsd/cgo.
|
||||||
|
|
@ -619,7 +622,7 @@ func deadcode() {
|
||||||
fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime())
|
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
|
// Mark all symbols as reachable when building a
|
||||||
// shared library.
|
// shared library.
|
||||||
for s := Ctxt.Allsym; s != nil; s = s.Allsym {
|
for s := Ctxt.Allsym; s != nil; s = s.Allsym {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmd/internal/obj"
|
"cmd/internal/obj"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -257,27 +256,35 @@ type BuildMode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BuildmodeExe BuildMode = iota
|
BuildmodeExe BuildMode = iota
|
||||||
|
BuildmodeCArchive
|
||||||
BuildmodeCShared
|
BuildmodeCShared
|
||||||
BuildmodeShared
|
BuildmodeShared
|
||||||
)
|
)
|
||||||
|
|
||||||
func (mode *BuildMode) Set(s string) error {
|
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 {
|
switch s {
|
||||||
default:
|
default:
|
||||||
return errors.New("invalid mode")
|
return fmt.Errorf("invalid buildmode: %q", s)
|
||||||
case "exe":
|
case "exe":
|
||||||
*mode = BuildmodeExe
|
*mode = BuildmodeExe
|
||||||
|
case "c-archive":
|
||||||
|
if goos != "darwin" {
|
||||||
|
return badmode()
|
||||||
|
}
|
||||||
|
*mode = BuildmodeCArchive
|
||||||
case "c-shared":
|
case "c-shared":
|
||||||
goarch := obj.Getgoarch()
|
|
||||||
if goarch != "amd64" && goarch != "arm" {
|
if goarch != "amd64" && goarch != "arm" {
|
||||||
return fmt.Errorf("not supported on %s", goarch)
|
return badmode()
|
||||||
}
|
}
|
||||||
*mode = BuildmodeCShared
|
*mode = BuildmodeCShared
|
||||||
case "shared":
|
case "shared":
|
||||||
goos := obj.Getgoos()
|
|
||||||
goarch := obj.Getgoarch()
|
|
||||||
if goos != "linux" || goarch != "amd64" {
|
if goos != "linux" || goarch != "amd64" {
|
||||||
return fmt.Errorf("not supported on %s/%s", goos, goarch)
|
return badmode()
|
||||||
}
|
}
|
||||||
*mode = BuildmodeShared
|
*mode = BuildmodeShared
|
||||||
}
|
}
|
||||||
|
|
@ -288,6 +295,8 @@ func (mode *BuildMode) String() string {
|
||||||
switch *mode {
|
switch *mode {
|
||||||
case BuildmodeExe:
|
case BuildmodeExe:
|
||||||
return "exe"
|
return "exe"
|
||||||
|
case BuildmodeCArchive:
|
||||||
|
return "c-archive"
|
||||||
case BuildmodeCShared:
|
case BuildmodeCShared:
|
||||||
return "c-shared"
|
return "c-shared"
|
||||||
case BuildmodeShared:
|
case BuildmodeShared:
|
||||||
|
|
@ -339,7 +348,7 @@ func libinit() {
|
||||||
|
|
||||||
if INITENTRY == "" {
|
if INITENTRY == "" {
|
||||||
switch Buildmode {
|
switch Buildmode {
|
||||||
case BuildmodeCShared:
|
case BuildmodeCShared, BuildmodeCArchive:
|
||||||
INITENTRY = fmt.Sprintf("_rt0_%s_%s_lib", goarch, goos)
|
INITENTRY = fmt.Sprintf("_rt0_%s_%s_lib", goarch, goos)
|
||||||
case BuildmodeExe:
|
case BuildmodeExe:
|
||||||
INITENTRY = fmt.Sprintf("_rt0_%s_%s", goarch, goos)
|
INITENTRY = fmt.Sprintf("_rt0_%s_%s", goarch, goos)
|
||||||
|
|
@ -402,10 +411,15 @@ func loadinternal(name string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadlib() {
|
func loadlib() {
|
||||||
if Buildmode == BuildmodeCShared {
|
switch Buildmode {
|
||||||
|
case BuildmodeCShared:
|
||||||
s := Linklookup(Ctxt, "runtime.islibrary", 0)
|
s := Linklookup(Ctxt, "runtime.islibrary", 0)
|
||||||
s.Dupok = 1
|
s.Dupok = 1
|
||||||
Adduint8(Ctxt, s, 1)
|
Adduint8(Ctxt, s, 1)
|
||||||
|
case BuildmodeCArchive:
|
||||||
|
s := Linklookup(Ctxt, "runtime.isarchive", 0)
|
||||||
|
s.Dupok = 1
|
||||||
|
Adduint8(Ctxt, s, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadinternal("runtime")
|
loadinternal("runtime")
|
||||||
|
|
@ -782,14 +796,79 @@ func hostlinksetup() {
|
||||||
coutbuf = *Binitw(cout)
|
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() {
|
func hostlink() {
|
||||||
if Linkmode != LinkExternal || nerrors > 0 {
|
if Linkmode != LinkExternal || nerrors > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if Buildmode == BuildmodeCArchive {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if extld == "" {
|
if extld == "" {
|
||||||
extld = "gcc"
|
extld = "gcc"
|
||||||
}
|
}
|
||||||
|
|
||||||
var argv []string
|
var argv []string
|
||||||
argv = append(argv, extld)
|
argv = append(argv, extld)
|
||||||
switch Thearch.Thechar {
|
switch Thearch.Thechar {
|
||||||
|
|
@ -830,10 +909,11 @@ func hostlink() {
|
||||||
argv = append(argv, "-Wl,--rosegment")
|
argv = append(argv, "-Wl,--rosegment")
|
||||||
}
|
}
|
||||||
|
|
||||||
if Buildmode == BuildmodeCShared {
|
switch Buildmode {
|
||||||
|
case BuildmodeCShared:
|
||||||
argv = append(argv, "-Wl,-Bsymbolic")
|
argv = append(argv, "-Wl,-Bsymbolic")
|
||||||
argv = append(argv, "-shared")
|
argv = append(argv, "-shared")
|
||||||
} else if Buildmode == BuildmodeShared {
|
case BuildmodeShared:
|
||||||
// TODO(mwhudson): unless you do this, dynamic relocations fill
|
// TODO(mwhudson): unless you do this, dynamic relocations fill
|
||||||
// out the findfunctab table and for some reason shared libraries
|
// out the findfunctab table and for some reason shared libraries
|
||||||
// and the executable both define a main function and putting the
|
// and the executable both define a main function and putting the
|
||||||
|
|
@ -868,41 +948,7 @@ func hostlink() {
|
||||||
argv = append(argv, "-Qunused-arguments")
|
argv = append(argv, "-Qunused-arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
// already wrote main object file
|
argv = append(argv, hostobjCopy()...)
|
||||||
// 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, fmt.Sprintf("%s/go.o", tmpdir))
|
argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir))
|
||||||
|
|
||||||
if Linkshared {
|
if Linkshared {
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ func Ldmain() {
|
||||||
Thearch.Asmb()
|
Thearch.Asmb()
|
||||||
undef()
|
undef()
|
||||||
hostlink()
|
hostlink()
|
||||||
|
archive()
|
||||||
if Debug['v'] != 0 {
|
if Debug['v'] != 0 {
|
||||||
fmt.Fprintf(&Bso, "%5.2f cpu time\n", obj.Cputime())
|
fmt.Fprintf(&Bso, "%5.2f cpu time\n", obj.Cputime())
|
||||||
fmt.Fprintf(&Bso, "%d symbols\n", Ctxt.Nsymbol)
|
fmt.Fprintf(&Bso, "%d symbols\n", Ctxt.Nsymbol)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue