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>
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
// 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
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 {
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
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
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 {
break
}
- if ld.Buildmode == ld.BuildmodeCShared || ctxt.DynlinkingGo() {
- ld.Linkmode = ld.LinkExternal
- }
-
switch ld.Headtype {
default:
ld.Exitf("unknown -H option: %v", ld.Headtype)
/* shared library initializer */
switch Buildmode {
- case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
+ case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePlugin:
hasinitarr = true
}
if hasinitarr {
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)
/* shared library initializer */
switch Buildmode {
- case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
+ case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePlugin:
hasinitarr = true
}
s = Linklookup(ctxt, local, 0)
switch Buildmode {
- case BuildmodeCShared, BuildmodeCArchive:
+ case BuildmodeCShared, BuildmodeCArchive, BuildmodePlugin:
if s == Linklookup(ctxt, "main", 0) {
continue
}
// 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
*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{}) {
func (ctxt *Link) loadlib() {
switch Buildmode {
- case BuildmodeCShared:
+ case BuildmodeCShared, BuildmodePlugin:
s := Linklookup(ctxt, "runtime.islibrary", 0)
s.Attr |= AttrDuplicateOK
Adduint8(ctxt, s, 1)
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
}
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])
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
}
// 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.
}
}
+ 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()
}
// 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")
}
// 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
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
"flag"
"log"
"os"
+ "path/filepath"
"runtime"
"runtime/pprof"
"strings"
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)
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()
}
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
BuildmodeCArchive
BuildmodeCShared
BuildmodeShared
+ BuildmodePlugin
)
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
}
return "c-shared"
case BuildmodeShared:
return "shared"
+ case BuildmodePlugin:
+ return "plugin"
}
return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
}
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 {
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)
if !ld.Iself {
return
}
- case ld.BuildmodePIE, ld.BuildmodeCShared:
+ case ld.BuildmodePIE, ld.BuildmodeCShared, ld.BuildmodePlugin:
// We need get_pc_thunk.
default:
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
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
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 {
--- /dev/null
+// 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
+}
typelinks []int32 // offsets from types
itablinks []*itab
+ ptab []ptabEntry
+
modulename string
modulehashes []modulehash