]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: -buildmode=plugin support for linux
authorDavid Crawshaw <crawshaw@golang.org>
Fri, 26 Aug 2016 01:58:45 +0000 (21:58 -0400)
committerDavid Crawshaw <crawshaw@golang.org>
Fri, 16 Sep 2016 14:49:13 +0000 (14:49 +0000)
This CL contains several linker changes to support creating plugins.

It collects the exported plugin symbols provided by the compiler and
includes them in the moduledata.

It treats a binary as being dynamically linked if it imports the plugin
package. This lets the dynamic linker de-duplicate symbols.

Change-Id: I099b6f38dda26306eba5c41dbe7862f5a5918d95
Reviewed-on: https://go-review.googlesource.com/27820
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
20 files changed:
src/cmd/link/internal/amd64/asm.go
src/cmd/link/internal/amd64/obj.go
src/cmd/link/internal/arm/asm.go
src/cmd/link/internal/arm/obj.go
src/cmd/link/internal/arm64/obj.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/deadcode.go
src/cmd/link/internal/ld/elf.go
src/cmd/link/internal/ld/go.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/link.go
src/cmd/link/internal/ld/main.go
src/cmd/link/internal/ld/objfile.go
src/cmd/link/internal/ld/sym.go
src/cmd/link/internal/ld/symtab.go
src/cmd/link/internal/s390x/obj.go
src/cmd/link/internal/x86/asm.go
src/cmd/link/internal/x86/obj.go
src/runtime/plugin.go [new file with mode: 0644]
src/runtime/symtab.go

index 0d8444eea47c4a1b1f643a255e5f1b6bfc185c42..ea31d6a739602b6ec2e7d0f5efefbea5adede30f 100644 (file)
@@ -59,7 +59,7 @@ func gentext(ctxt *ld.Link) {
                return
        }
        addmoduledata := ld.Linklookup(ctxt, "runtime.addmoduledata", 0)
-       if addmoduledata.Type == obj.STEXT {
+       if addmoduledata.Type == obj.STEXT && ld.Buildmode != ld.BuildmodePlugin {
                // we're linking a module containing the runtime -> no need for
                // an init function
                return
@@ -86,6 +86,9 @@ func gentext(ctxt *ld.Link) {
        //    c:        c3                      retq
        o(0xc3)
        ctxt.Textp = append(ctxt.Textp, initfunc)
+       if ld.Buildmode == ld.BuildmodePlugin {
+               ctxt.Textp = append(ctxt.Textp, addmoduledata)
+       }
        initarray_entry := ld.Linklookup(ctxt, "go.link.addmoduledatainit", 0)
        initarray_entry.Attr |= ld.AttrReachable
        initarray_entry.Attr |= ld.AttrLocal
index 0494050d8642b926aaf78907b4c3f5a8d239827c..5f85b0b2b35d12317aa6a0e9f8b11d5989271401 100644 (file)
@@ -90,10 +90,6 @@ func archinit(ctxt *ld.Link) {
                ld.Linkmode = ld.LinkInternal
        }
 
-       if ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ctxt.DynlinkingGo() {
-               ld.Linkmode = ld.LinkExternal
-       }
-
        switch ld.Headtype {
        default:
                if ld.Linkmode == ld.LinkAuto {
index 68efc2129a52182266eced6331cd55e3e1400d8b..e246d0f71a4ff4ea4d4599e913abbd66147ae054 100644 (file)
@@ -63,7 +63,7 @@ func gentext(ctxt *ld.Link) {
                return
        }
        addmoduledata := ld.Linklookup(ctxt, "runtime.addmoduledata", 0)
-       if addmoduledata.Type == obj.STEXT {
+       if addmoduledata.Type == obj.STEXT && ld.Buildmode != ld.BuildmodePlugin {
                // we're linking a module containing the runtime -> no need for
                // an init function
                return
@@ -96,6 +96,9 @@ func gentext(ctxt *ld.Link) {
        rel.Add = 4
 
        ctxt.Textp = append(ctxt.Textp, initfunc)
+       if ld.Buildmode == ld.BuildmodePlugin {
+               ctxt.Textp = append(ctxt.Textp, addmoduledata)
+       }
        initarray_entry := ld.Linklookup(ctxt, "go.link.addmoduledatainit", 0)
        initarray_entry.Attr |= ld.AttrReachable
        initarray_entry.Attr |= ld.AttrLocal
index 0b599b4bc1328265e04ed5a3b255fe6244051537..d82c5a2583732abd9308e3217878cf611eb73a57 100644 (file)
@@ -85,10 +85,6 @@ func archinit(ctxt *ld.Link) {
                ld.Linkmode = ld.LinkInternal
        }
 
-       if ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ctxt.DynlinkingGo() {
-               ld.Linkmode = ld.LinkExternal
-       }
-
        switch ld.Headtype {
        default:
                if ld.Linkmode == ld.LinkAuto {
index 5ab3262cb6c273b99f7103511e65750d1285e61d..c1d2ff5cc8bffd73473ef579652c921749377dc2 100644 (file)
@@ -103,10 +103,6 @@ func archinit(ctxt *ld.Link) {
                break
        }
 
-       if ld.Buildmode == ld.BuildmodeCShared || ctxt.DynlinkingGo() {
-               ld.Linkmode = ld.LinkExternal
-       }
-
        switch ld.Headtype {
        default:
                ld.Exitf("unknown -H option: %v", ld.Headtype)
index 3fd2deb157e90593435c4043c8a0257f4df4c302..7ac0eb5b2e1187e19fcbcf51eda4176e52f62b3e 100644 (file)
@@ -1367,7 +1367,7 @@ func (ctxt *Link) dodata() {
 
        /* shared library initializer */
        switch Buildmode {
-       case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
+       case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePlugin:
                hasinitarr = true
        }
        if hasinitarr {
index 9f49cf2dfc3460f0d851c47dbb19fea701c02b35..6d3f74a039c81d8749494851314427333d7868be 100644 (file)
@@ -243,6 +243,16 @@ func (d *deadcodepass) init() {
                names = append(names, *flagEntrySymbol)
                if *FlagLinkshared && (Buildmode == BuildmodeExe || Buildmode == BuildmodePIE) {
                        names = append(names, "main.main", "main.init")
+               } else if Buildmode == BuildmodePlugin {
+                       pluginInit := d.ctxt.Library[0].Pkg + ".init"
+                       names = append(names, pluginInit, "go.plugin.tabs")
+
+                       // We don't keep the go.plugin.exports symbol,
+                       // but we do keep the symbols it refers to.
+                       exports := Linkrlookup(d.ctxt, "go.plugin.exports", 0)
+                       for _, r := range exports.R {
+                               d.mark(r.Sym, nil)
+                       }
                }
                for _, name := range markextra {
                        names = append(names, name)
index 85935b67f92bc759c83de474e0f4f82e66084a82..70cf2540ce66e7d70c7325950d4e78ecae8cc77a 100644 (file)
@@ -1910,7 +1910,7 @@ func (ctxt *Link) doelf() {
 
        /* shared library initializer */
        switch Buildmode {
-       case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
+       case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePlugin:
                hasinitarr = true
        }
 
index 02f70f1a450b284bad1253a3fe73e9e0e421f25c..89fc8ddca65da31bdec8d11afc7f3a273d77e1bc 100644 (file)
@@ -228,7 +228,7 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
                        s = Linklookup(ctxt, local, 0)
 
                        switch Buildmode {
-                       case BuildmodeCShared, BuildmodeCArchive:
+                       case BuildmodeCShared, BuildmodeCArchive, BuildmodePlugin:
                                if s == Linklookup(ctxt, "main", 0) {
                                        continue
                                }
index 9c95d478b56a4f62e28193e0b0e7c3c3fd54023a..dada4cb7a71e271487d55219245ae0ac1386b28d 100644 (file)
@@ -164,14 +164,18 @@ type Section struct {
 // DynlinkingGo returns whether we are producing Go code that can live
 // in separate shared libraries linked together at runtime.
 func (ctxt *Link) DynlinkingGo() bool {
-       return Buildmode == BuildmodeShared || *FlagLinkshared
+       if !ctxt.Loaded {
+               panic("DynlinkingGo called before all symbols loaded")
+       }
+       canUsePlugins := Linkrlookup(ctxt, "plugin.Open", 0) != nil
+       return Buildmode == BuildmodeShared || *FlagLinkshared || Buildmode == BuildmodePlugin || canUsePlugins
 }
 
 // UseRelro returns whether to make use of "read only relocations" aka
 // relro.
 func UseRelro() bool {
        switch Buildmode {
-       case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePIE:
+       case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePIE, BuildmodePlugin:
                return Iself
        default:
                return *FlagLinkshared
@@ -299,16 +303,12 @@ func libinit(ctxt *Link) {
                        *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", obj.GOARCH, obj.GOOS)
                case BuildmodeExe, BuildmodePIE:
                        *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", obj.GOARCH, obj.GOOS)
-               case BuildmodeShared:
-                       // No *flagEntrySymbol for -buildmode=shared
+               case BuildmodeShared, BuildmodePlugin:
+                       // No *flagEntrySymbol for -buildmode=shared and plugin
                default:
                        ctxt.Diag("unknown *flagEntrySymbol for buildmode %v", Buildmode)
                }
        }
-
-       if !ctxt.DynlinkingGo() {
-               Linklookup(ctxt, *flagEntrySymbol, 0).Type = obj.SXREF
-       }
 }
 
 func Exitf(format string, a ...interface{}) {
@@ -400,7 +400,7 @@ func (ctxt *Link) findLibPath(libname string) string {
 
 func (ctxt *Link) loadlib() {
        switch Buildmode {
-       case BuildmodeCShared:
+       case BuildmodeCShared, BuildmodePlugin:
                s := Linklookup(ctxt, "runtime.islibrary", 0)
                s.Attr |= AttrDuplicateOK
                Adduint8(ctxt, s, 1)
@@ -453,9 +453,14 @@ func (ctxt *Link) loadlib() {
                        Linkmode = LinkExternal
                }
 
-               // Force external linking for PIE binaries on systems
-               // that do not support internal PIE linking.
-               if Buildmode == BuildmodePIE {
+               // These build modes depend on the external linker
+               // to handle some relocations (such as TLS IE) not
+               // yet supported by the internal linker.
+               switch Buildmode {
+               case BuildmodeCArchive, BuildmodeCShared, BuildmodePIE, BuildmodePlugin, BuildmodeShared:
+                       Linkmode = LinkExternal
+               }
+               if *FlagLinkshared {
                        Linkmode = LinkExternal
                }
 
@@ -492,7 +497,7 @@ func (ctxt *Link) loadlib() {
                        if ctxt.Library[i].Shlib != "" {
                                ldshlibsyms(ctxt, ctxt.Library[i].Shlib)
                        } else {
-                               if ctxt.DynlinkingGo() {
+                               if Buildmode == BuildmodeShared || *FlagLinkshared {
                                        Exitf("cannot implicitly include runtime/cgo in a shared library")
                                }
                                objfile(ctxt, ctxt.Library[i])
@@ -531,7 +536,13 @@ func (ctxt *Link) loadlib() {
        tlsg.Attr |= AttrReachable
        ctxt.Tlsg = tlsg
 
-       moduledata := Linklookup(ctxt, "runtime.firstmoduledata", 0)
+       var moduledata *Symbol
+       if Buildmode == BuildmodePlugin {
+               moduledata = Linklookup(ctxt, "local.pluginmoduledata", 0)
+               moduledata.Attr |= AttrLocal
+       } else {
+               moduledata = Linklookup(ctxt, "runtime.firstmoduledata", 0)
+       }
        if moduledata.Type != 0 && moduledata.Type != obj.SDYNIMPORT {
                // If the module (toolchain-speak for "executable or shared
                // library") we are linking contains the runtime package, it
@@ -626,6 +637,8 @@ func (ctxt *Link) loadlib() {
        }
 
        // We've loaded all the code now.
+       ctxt.Loaded = true
+
        // If there are no dynamic libraries needed, gcc disables dynamic linking.
        // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
        // assumes that a dynamic binary always refers to at least one dynamic library.
@@ -642,6 +655,14 @@ func (ctxt *Link) loadlib() {
                }
        }
 
+       if SysArch == sys.Arch386 {
+               if (Buildmode == BuildmodeCArchive && Iself) || Buildmode == BuildmodeCShared || Buildmode == BuildmodePIE || ctxt.DynlinkingGo() {
+                       got := Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
+                       got.Type = obj.SDYNIMPORT
+                       got.Attr |= AttrReachable
+               }
+       }
+
        importcycles()
 }
 
@@ -1012,7 +1033,7 @@ func (l *Link) hostlink() {
                        // non-closeable: a dlclose will do nothing.
                        argv = append(argv, "-shared", "-Wl,-z,nodelete")
                }
-       case BuildmodeShared:
+       case BuildmodeShared, BuildmodePlugin:
                if UseRelro() {
                        argv = append(argv, "-Wl,-z,relro")
                }
@@ -1658,7 +1679,7 @@ func stkcheck(ctxt *Link, up *chain, depth int) int {
                // onlyctxt.Diagnose the direct caller.
                // TODO(mwhudson): actually think about this.
                if depth == 1 && s.Type != obj.SXREF && !ctxt.DynlinkingGo() &&
-                       Buildmode != BuildmodeCArchive && Buildmode != BuildmodePIE && Buildmode != BuildmodeCShared {
+                       Buildmode != BuildmodeCArchive && Buildmode != BuildmodePIE && Buildmode != BuildmodeCShared && Buildmode != BuildmodePlugin {
                        ctxt.Diag("call to external function %s", s.Name)
                }
                return -1
index e8a98889f44dfdbae72edb6d703aadade097625b..9b93e0336aa284b1e69b37c099923e7b129165a3 100644 (file)
@@ -165,6 +165,8 @@ type Link struct {
        Bso       *bufio.Writer
        Windows   int32
 
+       Loaded bool // set after all inputs have been loaded as symbols
+
        // Symbol lookup based on name and indexed by version.
        Hash []map[string]*Symbol
 
index d5eeb73bd16d6ed0dd5ef48a45d13888a28282e8..c480cc531a8e590bc4f51828b0fe35e290a4beac 100644 (file)
@@ -37,6 +37,7 @@ import (
        "flag"
        "log"
        "os"
+       "path/filepath"
        "runtime"
        "runtime/pprof"
        "strings"
@@ -158,7 +159,8 @@ func Main() {
                ctxt.Logf("HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", Headtype, uint64(*FlagTextAddr), uint64(*FlagDataAddr), uint32(*FlagRound))
        }
 
-       if Buildmode == BuildmodeShared {
+       switch Buildmode {
+       case BuildmodeShared:
                for i := 0; i < flag.NArg(); i++ {
                        arg := flag.Arg(i)
                        parts := strings.SplitN(arg, "=", 2)
@@ -172,7 +174,10 @@ func Main() {
                        pkglistfornote = append(pkglistfornote, '\n')
                        addlibpath(ctxt, "command line", "command line", file, pkgpath, "")
                }
-       } else {
+       case BuildmodePlugin:
+               pluginName := strings.TrimSuffix(filepath.Base(flag.Arg(0)), ".a")
+               addlibpath(ctxt, "command line", "command line", flag.Arg(0), pluginName, "")
+       default:
                addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "")
        }
        ctxt.loadlib()
index 4b5ae5dee90bd8cb021655fe60827b2076ce7f0d..ee48252867560fec9fabe049857ca063161ab7b9 100644 (file)
@@ -585,7 +585,7 @@ func (r *objReader) readSymName() string {
                        }
                        r.rdBuf = adjName[:0] // in case 2*n wasn't enough
 
-                       if r.ctxt.DynlinkingGo() {
+                       if Buildmode == BuildmodeShared || *FlagLinkshared {
                                // These types are included in the symbol
                                // table when dynamically linking. To keep
                                // binary size down, we replace the names
index a5e2e6fb9e12db46591311d98dfdd43678f872d4..319a69e36480d204773de2f2ab173f257e557876 100644 (file)
@@ -184,6 +184,7 @@ const (
        BuildmodeCArchive
        BuildmodeCShared
        BuildmodeShared
+       BuildmodePlugin
 )
 
 func (mode *BuildMode) Set(s string) error {
@@ -234,6 +235,18 @@ func (mode *BuildMode) Set(s string) error {
                        return badmode()
                }
                *mode = BuildmodeShared
+       case "plugin":
+               switch obj.GOOS {
+               case "linux":
+                       switch obj.GOARCH {
+                       case "386", "amd64", "arm", "arm64":
+                       default:
+                               return badmode()
+                       }
+               default:
+                       return badmode()
+               }
+               *mode = BuildmodePlugin
        }
        return nil
 }
@@ -252,6 +265,8 @@ func (mode *BuildMode) String() string {
                return "c-shared"
        case BuildmodeShared:
                return "shared"
+       case BuildmodePlugin:
+               return "plugin"
        }
        return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
 }
index 7d9e25f8ffef299056db8278118d30d97f243228..dc948d3bf2bef03d6405f4dd23903b944583f89e 100644 (file)
@@ -553,6 +553,22 @@ func (ctxt *Link) symtab() {
        Addaddr(ctxt, moduledata, Linklookup(ctxt, "runtime.itablink", 0))
        adduint(ctxt, moduledata, uint64(nitablinks))
        adduint(ctxt, moduledata, uint64(nitablinks))
+       // The ptab slice
+       if Buildmode == BuildmodePlugin {
+               ptab := Linkrlookup(ctxt, "go.plugin.tabs", 0)
+               ptab.Attr |= AttrReachable
+               ptab.Attr |= AttrLocal
+               ptab.Type = obj.SRODATA
+
+               nentries := uint64(len(ptab.P) / 8) // sizeof(nameOff) + sizeof(typeOff)
+               Addaddr(ctxt, moduledata, ptab)
+               adduint(ctxt, moduledata, nentries)
+               adduint(ctxt, moduledata, nentries)
+       } else {
+               adduint(ctxt, moduledata, 0)
+               adduint(ctxt, moduledata, 0)
+               adduint(ctxt, moduledata, 0)
+       }
        if len(ctxt.Shlibs) > 0 {
                thismodulename := filepath.Base(*flagOutfile)
                switch Buildmode {
index 4554c52e02cf4aa331b4026447e4a32c3feb7f02..67ad3b70ae83c5edd54fdfc01b5ceca945e987b3 100644 (file)
@@ -86,10 +86,6 @@ func archinit(ctxt *ld.Link) {
                ld.Linkmode = ld.LinkInternal
        }
 
-       if ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ctxt.DynlinkingGo() {
-               ld.Linkmode = ld.LinkExternal
-       }
-
        switch ld.Headtype {
        default:
                ld.Exitf("unknown -H option: %v", ld.Headtype)
index 2f6be25bf9bc1de7eaa0e0013bbe0eca5af52947..284d768acd81d246d109f73426cbf3bca38ca305 100644 (file)
@@ -58,7 +58,7 @@ func gentext(ctxt *ld.Link) {
                        if !ld.Iself {
                                return
                        }
-               case ld.BuildmodePIE, ld.BuildmodeCShared:
+               case ld.BuildmodePIE, ld.BuildmodeCShared, ld.BuildmodePlugin:
                        // We need get_pc_thunk.
                default:
                        return
@@ -98,7 +98,7 @@ func gentext(ctxt *ld.Link) {
        }
 
        addmoduledata := ld.Linklookup(ctxt, "runtime.addmoduledata", 0)
-       if addmoduledata.Type == obj.STEXT {
+       if addmoduledata.Type == obj.STEXT && ld.Buildmode != ld.BuildmodePlugin {
                // we're linking a module containing the runtime -> no need for
                // an init function
                return
@@ -152,6 +152,9 @@ func gentext(ctxt *ld.Link) {
        o(0xc3)
 
        ctxt.Textp = append(ctxt.Textp, initfunc)
+       if ld.Buildmode == ld.BuildmodePlugin {
+               ctxt.Textp = append(ctxt.Textp, addmoduledata)
+       }
        initarray_entry := ld.Linklookup(ctxt, "go.link.addmoduledatainit", 0)
        initarray_entry.Attr |= ld.AttrReachable
        initarray_entry.Attr |= ld.AttrLocal
index 773b5c6b8f34314aa073aec0dd9f08c18ab459fe..088a446b334ddb81d89408e1b0055d0568b361bb 100644 (file)
@@ -85,13 +85,6 @@ func archinit(ctxt *ld.Link) {
                ld.Linkmode = ld.LinkInternal
        }
 
-       if (ld.Buildmode == ld.BuildmodeCArchive && ld.Iself) || ld.Buildmode == ld.BuildmodeCShared || ld.Buildmode == ld.BuildmodePIE || ctxt.DynlinkingGo() {
-               ld.Linkmode = ld.LinkExternal
-               got := ld.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
-               got.Type = obj.SDYNIMPORT
-               got.Attr |= ld.AttrReachable
-       }
-
        switch ld.Headtype {
        default:
                if ld.Linkmode == ld.LinkAuto {
diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go
new file mode 100644 (file)
index 0000000..f5f3aa2
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+// A ptabEntry is generated by the compiler for each exported function
+// and global variable in the main package of a plugin. It is used to
+// initialize the plugin module's symbol map.
+type ptabEntry struct {
+       name nameOff
+       typ  typeOff
+}
index 7a37085fab8b9063393c8d78c05d6ac0777f884a..87b478a885fb3232480dc16ca67ce902cd9709fc 100644 (file)
@@ -198,6 +198,8 @@ type moduledata struct {
        typelinks []int32 // offsets from types
        itablinks []*itab
 
+       ptab []ptabEntry
+
        modulename   string
        modulehashes []modulehash