From 75c0566b55b7a6ee4866f632fc123b70673ee78f Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Wed, 1 Apr 2015 14:57:34 +1300 Subject: [PATCH] cmd/6l: support -linkshared Change-Id: Id469165b1acd383837b1f4e1e6f961e10dfa5d61 Reviewed-on: https://go-review.googlesource.com/8332 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor --- src/cmd/internal/ld/go.go | 4 + src/cmd/internal/ld/ld.go | 30 +++++-- src/cmd/internal/ld/lib.go | 142 +++++++++++++++++++++++++++++++--- src/cmd/internal/ld/link.go | 2 + src/cmd/internal/ld/pobj.go | 10 ++- src/cmd/internal/ld/symtab.go | 3 + 6 files changed, 174 insertions(+), 17 deletions(-) diff --git a/src/cmd/internal/ld/go.go b/src/cmd/internal/ld/go.go index 55c9bb20a9..1d83081025 100644 --- a/src/cmd/internal/ld/go.go +++ b/src/cmd/internal/ld/go.go @@ -631,6 +631,10 @@ func deadcode() { 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 a0f1f325bd..7242301d0f 100644 --- a/src/cmd/internal/ld/ld.go +++ b/src/cmd/internal/ld/ld.go @@ -34,6 +34,7 @@ package ld import ( "cmd/internal/obj" "fmt" + "io/ioutil" "os" "path" "strconv" @@ -57,11 +58,19 @@ func addlib(ctxt *Link, src string, obj string, pathname string) { } 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 @@ func addlib(ctxt *Link, src string, obj string, pathname string) { 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 @@ func addlib(ctxt *Link, src string, obj string, pathname string) { * 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 @@ func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg strin 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 ed54ce87ae..02d93af6d6 100644 --- a/src/cmd/internal/ld/lib.go +++ b/src/cmd/internal/ld/lib.go @@ -33,6 +33,7 @@ package ld import ( "bytes" "cmd/internal/obj" + "debug/elf" "errors" "fmt" "io" @@ -40,6 +41,7 @@ import ( "log" "os" "os/exec" + "path/filepath" "strings" ) @@ -153,9 +155,7 @@ type Section struct { // 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 @@ var ( flag_installsuffix string flag_race int Buildmode BuildMode + Linkshared bool tracksym string interpreter string tmpdir string @@ -371,16 +372,25 @@ func Errorexit() { } 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 @@ func loadlib() { 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 @@ func loadlib() { 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 @@ func loadlib() { // 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 @@ func hostlink() { 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 @@ func hostlink() { } 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 @@ eof: 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 83cfe283f4..0eca045f7d 100644 --- a/src/cmd/internal/ld/link.go +++ b/src/cmd/internal/ld/link.go @@ -114,6 +114,7 @@ type Link struct { Tlsg *LSym Libdir []string Library []Library + Shlibs []string Tlsoffset int Diag func(string, ...interface{}) Cursym *LSym @@ -138,6 +139,7 @@ type Library struct { 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 539e3d353a..c4e779df7a 100644 --- a/src/cmd/internal/ld/pobj.go +++ b/src/cmd/internal/ld/pobj.go @@ -112,6 +112,7 @@ func Ldmain() { 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 @@ func Ldmain() { 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 @@ func Ldmain() { } 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 c31f70abd1..1898a9b818 100644 --- a/src/cmd/internal/ld/symtab.go +++ b/src/cmd/internal/ld/symtab.go @@ -104,6 +104,9 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L case 'U': type_ = STT_NOTYPE + if x == Ctxt.Tlsg { + type_ = STT_TLS + } case 't': type_ = STT_TLS