cmd/6l: support -linkshared
Change-Id: Id469165b1acd383837b1f4e1e6f961e10dfa5d61
Reviewed-on: https://go-review.googlesource.com/8332
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
diff --git a/src/cmd/internal/ld/go.go b/src/cmd/internal/ld/go.go
index 55c9bb2..1d83081 100644
--- a/src/cmd/internal/ld/go.go
+++ b/src/cmd/internal/ld/go.go
@@ -631,6 +631,10 @@
mark(Linkrlookup(Ctxt, "main.init", 0))
} else {
mark(Linklookup(Ctxt, INITENTRY, 0))
+ if Linkshared && Buildmode == BuildmodeExe {
+ mark(Linkrlookup(Ctxt, "main.main", 0))
+ mark(Linkrlookup(Ctxt, "main.init", 0))
+ }
for i := 0; i < len(markextra); i++ {
mark(Linklookup(Ctxt, markextra[i], 0))
}
diff --git a/src/cmd/internal/ld/ld.go b/src/cmd/internal/ld/ld.go
index a0f1f32..7242301 100644
--- a/src/cmd/internal/ld/ld.go
+++ b/src/cmd/internal/ld/ld.go
@@ -34,6 +34,7 @@
import (
"cmd/internal/obj"
"fmt"
+ "io/ioutil"
"os"
"path"
"strconv"
@@ -57,11 +58,19 @@
}
var pname string
+ isshlib := false
if (ctxt.Windows == 0 && strings.HasPrefix(name, "/")) || (ctxt.Windows != 0 && len(name) >= 2 && name[1] == ':') {
pname = name
} else {
// try dot, -L "libdir", and then goroot.
for _, dir := range ctxt.Libdir {
+ if Linkshared {
+ pname = dir + "/" + pkg + ".shlibname"
+ if _, err := os.Stat(pname); err == nil {
+ isshlib = true
+ break
+ }
+ }
pname = dir + "/" + name
if _, err := os.Stat(pname); err == nil {
break
@@ -72,10 +81,14 @@
pname = path.Clean(pname)
if ctxt.Debugvlog > 1 && ctxt.Bso != nil {
- fmt.Fprintf(ctxt.Bso, "%5.2f addlib: %s %s pulls in %s\n", elapsed(), obj, src, pname)
+ fmt.Fprintf(ctxt.Bso, "%5.2f addlib: %s %s pulls in %s isshlib %v\n", elapsed(), obj, src, pname, isshlib)
}
- addlibpath(ctxt, src, obj, pname, pkg)
+ if isshlib {
+ addlibpath(ctxt, src, obj, "", pkg, pname)
+ } else {
+ addlibpath(ctxt, src, obj, pname, pkg, "")
+ }
}
/*
@@ -85,15 +98,15 @@
* file: object file, e.g., /home/rsc/go/pkg/container/vector.a
* pkg: package import path, e.g. container/vector
*/
-func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string) {
+func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string, shlibnamefile string) {
for i := 0; i < len(ctxt.Library); i++ {
- if file == ctxt.Library[i].File {
+ if pkg == ctxt.Library[i].Pkg {
return
}
}
if ctxt.Debugvlog > 1 && ctxt.Bso != nil {
- fmt.Fprintf(ctxt.Bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s\n", obj.Cputime(), srcref, objref, file, pkg)
+ fmt.Fprintf(ctxt.Bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s shlibnamefile: %s\n", obj.Cputime(), srcref, objref, file, pkg, shlibnamefile)
}
ctxt.Library = append(ctxt.Library, Library{})
@@ -102,6 +115,13 @@
l.Srcref = srcref
l.File = file
l.Pkg = pkg
+ if shlibnamefile != "" {
+ shlibbytes, err := ioutil.ReadFile(shlibnamefile)
+ if err != nil {
+ Diag("cannot read %s: %v", shlibnamefile, err)
+ }
+ l.Shlib = strings.TrimSpace(string(shlibbytes))
+ }
}
func atolwhex(s string) int64 {
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index ed54ce8..02d93af 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -33,6 +33,7 @@
import (
"bytes"
"cmd/internal/obj"
+ "debug/elf"
"errors"
"fmt"
"io"
@@ -40,6 +41,7 @@
"log"
"os"
"os/exec"
+ "path/filepath"
"strings"
)
@@ -153,9 +155,7 @@
// DynlinkingGo returns whether we are producing Go code that can live
// in separate shared libraries linked together at runtime.
func DynlinkingGo() bool {
- // TODO(mwhudson): This is a bit silly for now, but it will need to have
- // "|| Linkshared" appended when a subsequent change adds that flag.
- return Buildmode == BuildmodeShared
+ return Buildmode == BuildmodeShared || Linkshared
}
var (
@@ -171,6 +171,7 @@
flag_installsuffix string
flag_race int
Buildmode BuildMode
+ Linkshared bool
tracksym string
interpreter string
tmpdir string
@@ -371,16 +372,25 @@
}
func loadinternal(name string) {
- var pname string
-
found := 0
for i := 0; i < len(Ctxt.Libdir); i++ {
- pname = fmt.Sprintf("%s/%s.a", Ctxt.Libdir[i], name)
+ if Linkshared {
+ shlibname := fmt.Sprintf("%s/%s.shlibname", Ctxt.Libdir[i], name)
+ if Debug['v'] != 0 {
+ fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, shlibname)
+ }
+ if obj.Access(shlibname, obj.AEXIST) >= 0 {
+ addlibpath(Ctxt, "internal", "internal", "", name, shlibname)
+ found = 1
+ break
+ }
+ }
+ pname := fmt.Sprintf("%s/%s.a", Ctxt.Libdir[i], name)
if Debug['v'] != 0 {
fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, pname)
}
if obj.Access(pname, obj.AEXIST) >= 0 {
- addlibpath(Ctxt, "internal", "internal", pname, name)
+ addlibpath(Ctxt, "internal", "internal", pname, name, "")
found = 1
break
}
@@ -412,7 +422,11 @@
fmt.Fprintf(&Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref)
}
iscgo = iscgo || Ctxt.Library[i].Pkg == "runtime/cgo"
- objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg)
+ if Ctxt.Library[i].Shlib != "" {
+ ldshlibsyms(Ctxt.Library[i].Shlib)
+ } else {
+ objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg)
+ }
}
if Linkmode == LinkAuto {
@@ -451,7 +465,11 @@
loadinternal("runtime/cgo")
if i < len(Ctxt.Library) {
- objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg)
+ if Ctxt.Library[i].Shlib != "" {
+ ldshlibsyms(Ctxt.Library[i].Shlib)
+ } else {
+ objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg)
+ }
}
}
@@ -482,7 +500,7 @@
// TODO(crawshaw): android should require leaving the tlsg->type
// alone (as the runtime-provided SNOPTRBSS) just like darwin/arm.
// But some other part of the linker is expecting STLSBSS.
- if goos != "darwin" || Thearch.Thechar != '5' {
+ if tlsg.Type != SDYNIMPORT && (goos != "darwin" || Thearch.Thechar != '5') {
tlsg.Type = STLSBSS
}
tlsg.Size = int64(Thearch.Ptrsize)
@@ -827,6 +845,13 @@
argv = append(argv, "-shared")
}
+ if Linkshared && Iself {
+ // We force all symbol resolution to be done at program startup
+ // because lazy PLT resolution can use large amounts of stack at
+ // times we cannot allow it to do so.
+ argv = append(argv, "-znow")
+ }
+
argv = append(argv, "-o")
argv = append(argv, outfile)
@@ -879,6 +904,18 @@
}
argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir))
+
+ if Linkshared {
+ for _, shlib := range Ctxt.Shlibs {
+ dir, base := filepath.Split(shlib)
+ argv = append(argv, "-L"+dir)
+ argv = append(argv, "-Wl,-rpath="+dir)
+ base = strings.TrimSuffix(base, ".so")
+ base = strings.TrimPrefix(base, "lib")
+ argv = append(argv, "-l"+base)
+ }
+ }
+
argv = append(argv, ldflag...)
for _, p := range strings.Fields(extldflags) {
@@ -1029,6 +1066,91 @@
Diag("truncated object file: %s", pn)
}
+func ldshlibsyms(shlib string) {
+ found := false
+ libpath := ""
+ for _, libdir := range Ctxt.Libdir {
+ libpath = filepath.Join(libdir, shlib)
+ if _, err := os.Stat(libpath); err == nil {
+ found = true
+ break
+ }
+ }
+ if !found {
+ Diag("cannot find shared library: %s", shlib)
+ return
+ }
+ for _, processedname := range Ctxt.Shlibs {
+ if processedname == libpath {
+ return
+ }
+ }
+ if Ctxt.Debugvlog > 1 && Ctxt.Bso != nil {
+ fmt.Fprintf(Ctxt.Bso, "%5.2f ldshlibsyms: found library with name %s at %s\n", obj.Cputime(), shlib, libpath)
+ Bflush(Ctxt.Bso)
+ }
+
+ f, err := elf.Open(libpath)
+ if err != nil {
+ Diag("cannot open shared library: %s", libpath)
+ return
+ }
+ defer f.Close()
+ syms, err := f.DynamicSymbols()
+ if err != nil {
+ Diag("cannot read symbols from shared library: %s", libpath)
+ return
+ }
+ for _, s := range syms {
+ if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION {
+ continue
+ }
+ if s.Section == elf.SHN_UNDEF {
+ continue
+ }
+ if strings.HasPrefix(s.Name, "_") {
+ continue
+ }
+ lsym := Linklookup(Ctxt, s.Name, 0)
+ if lsym.Type != 0 && lsym.Dupok == 0 {
+ Diag(
+ "Found duplicate symbol %s reading from %s, first found in %s",
+ s.Name, shlib, lsym.File)
+ }
+ lsym.Type = SDYNIMPORT
+ lsym.File = libpath
+ }
+
+ // We might have overwritten some functions above (this tends to happen for the
+ // autogenerated type equality/hashing functions) and we don't want to generated
+ // pcln table entries for these any more so unstitch them from the Textp linked
+ // list.
+ var last *LSym
+
+ for s := Ctxt.Textp; s != nil; s = s.Next {
+ if s.Type == SDYNIMPORT {
+ continue
+ }
+
+ if last == nil {
+ Ctxt.Textp = s
+ } else {
+ last.Next = s
+ }
+ last = s
+ }
+
+ if last == nil {
+ Ctxt.Textp = nil
+ Ctxt.Etextp = nil
+ } else {
+ last.Next = nil
+ Ctxt.Etextp = last
+ }
+
+ Ctxt.Shlibs = append(Ctxt.Shlibs, libpath)
+}
+
func mywhatsys() {
goroot = obj.Getgoroot()
goos = obj.Getgoos()
diff --git a/src/cmd/internal/ld/link.go b/src/cmd/internal/ld/link.go
index 83cfe28..0eca045 100644
--- a/src/cmd/internal/ld/link.go
+++ b/src/cmd/internal/ld/link.go
@@ -114,6 +114,7 @@
Tlsg *LSym
Libdir []string
Library []Library
+ Shlibs []string
Tlsoffset int
Diag func(string, ...interface{})
Cursym *LSym
@@ -138,6 +139,7 @@
Srcref string
File string
Pkg string
+ Shlib string
}
type Pcln struct {
diff --git a/src/cmd/internal/ld/pobj.go b/src/cmd/internal/ld/pobj.go
index 539e3d3..c4e779d 100644
--- a/src/cmd/internal/ld/pobj.go
+++ b/src/cmd/internal/ld/pobj.go
@@ -112,6 +112,7 @@
obj.Flagstr("installsuffix", "suffix: pkg directory suffix", &flag_installsuffix)
obj.Flagstr("k", "sym: set field tracking symbol", &tracksym)
obj.Flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode)
+ flag.BoolVar(&Linkshared, "linkshared", false, "link against installed Go shared libraries")
obj.Flagcount("n", "dump symbol table", &Debug['n'])
obj.Flagstr("o", "outfile: set output file", &outfile)
obj.Flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath)
@@ -176,6 +177,11 @@
Thearch.Archinit()
+ if Linkshared && !Iself {
+ Diag("-linkshared can only be used on elf systems")
+ Errorexit()
+ }
+
if Debug['v'] != 0 {
fmt.Fprintf(&Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND))
}
@@ -191,10 +197,10 @@
} else {
pkgpath, file = parts[0], parts[1]
}
- addlibpath(Ctxt, "command line", "command line", file, pkgpath)
+ addlibpath(Ctxt, "command line", "command line", file, pkgpath, "")
}
} else {
- addlibpath(Ctxt, "command line", "command line", flag.Arg(0), "main")
+ addlibpath(Ctxt, "command line", "command line", flag.Arg(0), "main", "")
}
loadlib()
diff --git a/src/cmd/internal/ld/symtab.go b/src/cmd/internal/ld/symtab.go
index c31f70a..1898a9b 100644
--- a/src/cmd/internal/ld/symtab.go
+++ b/src/cmd/internal/ld/symtab.go
@@ -104,6 +104,9 @@
case 'U':
type_ = STT_NOTYPE
+ if x == Ctxt.Tlsg {
+ type_ = STT_TLS
+ }
case 't':
type_ = STT_TLS