import (
"fmt"
"log"
+ "path/filepath"
"plugin"
"common"
log.Fatalf(`Lookup("Seven") failed: %v`, err)
}
if got, want := *seven.(*int), 7; got != want {
- log.Fatalf("via lookup plugin1.Seven=%d, want %d", got, want)
+ log.Fatalf("plugin1.Seven=%d, want %d", got, want)
}
readFunc, err := p.Lookup("ReadCommonX")
if err != nil {
- log.Fatalf(`Lookup("ReadCommonX") failed: %v`, err)
+ log.Fatalf(`plugin1.Lookup("ReadCommonX") failed: %v`, err)
}
if got := readFunc.(func() int)(); got != wantX {
- log.Fatalf("via lookup plugin1.ReadCommonX()=%d, want %d", got, wantX)
+ log.Fatalf("plugin1.ReadCommonX()=%d, want %d", got, wantX)
+ }
+
+ // sub/plugin1.so is a different plugin with the same name as
+ // the already loaded plugin. It also depends on common. Test
+ // that we can load the different plugin, it is actually
+ // different, and that it sees the same common package.
+ subpPath, err := filepath.Abs("sub/plugin1.so")
+ if err != nil {
+ log.Fatalf("filepath.Abs(%q) failed: %v", subpPath, err)
+ }
+ subp, err := plugin.Open(subpPath)
+ if err != nil {
+ log.Fatalf("plugin.Open(%q) failed: %v", subpPath, err)
+ }
+
+ readFunc, err = subp.Lookup("ReadCommonX")
+ if err != nil {
+ log.Fatalf(`sub/plugin1.Lookup("ReadCommonX") failed: %v`, err)
+ }
+ if got := readFunc.(func() int)(); got != wantX {
+ log.Fatalf("sub/plugin1.ReadCommonX()=%d, want %d", got, wantX)
+ }
+
+ subf, err := subp.Lookup("F")
+ if err != nil {
+ log.Fatalf(`sub/plugin1.Lookup("F") failed: %v`, err)
+ }
+ if gotf := subf.(func() int)(); gotf != 17 {
+ log.Fatalf(`sub/plugin1.F()=%d, want 17`, gotf)
+ }
+ f, err := p.Lookup("F")
+ if err != nil {
+ log.Fatalf(`plugin1.Lookup("F") failed: %v`, err)
+ }
+ if gotf := f.(func() int)(); gotf != 3 {
+ log.Fatalf(`plugin1.F()=%d, want 17`, gotf)
}
fmt.Println("PASS")
import "common"
+func F() int { return 3 }
+
func ReadCommonX() int {
return common.X
}
--- /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 main
+
+// // No C code required.
+import "C"
+
+import "common"
+
+func F() int { return 17 }
+
+func ReadCommonX() int {
+ return common.X
+}
+
+func main() {
+ panic("plugin1.main called")
+}
goarch=$(go env GOARCH)
function cleanup() {
- rm -f plugin1.so host pkg
+ rm -rf plugin1.so host pkg sub
}
trap cleanup EXIT
-rm -rf pkg
+rm -rf pkg sub
+mkdir sub
GOPATH=$(pwd) go build -buildmode=plugin plugin1
+GOPATH=$(pwd) go build -buildmode=plugin -o=sub/plugin1.so sub/plugin1
GOPATH=$(pwd) go build host
LD_LIBRARY_PATH=$(pwd) ./host
if root.p.omitDWARF {
ldflags = append(ldflags, "-w")
}
+ if buildBuildmode == "plugin" {
+ ldflags = append(ldflags, "-pluginpath", root.p.ImportPath)
+ }
// If the user has not specified the -extld option, then specify the
// appropriate linker. In case of C++ code, use the compiler named
Link with C/C++ memory sanitizer support.
-o file
Write output to file (default a.out, or a.out.exe on Windows).
+ -pluginpath path
+ The path name used to prefix exported plugin symbols.
-r dir1:dir2:...
Set the ELF dynamic linker search path.
-race
"flag"
"log"
"os"
- "path/filepath"
"runtime"
"runtime/pprof"
"strings"
flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
flagOutfile = flag.String("o", "", "write output to `file`")
+ flagPluginPath = flag.String("pluginpath", "", "full path name for plugin")
FlagLinkshared = flag.Bool("linkshared", false, "link against installed Go shared libraries")
flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`")
addlibpath(ctxt, "command line", "command line", file, pkgpath, "")
}
case BuildmodePlugin:
- pluginName := strings.TrimSuffix(filepath.Base(flag.Arg(0)), ".a")
- addlibpath(ctxt, "command line", "command line", flag.Arg(0), pluginName, "")
+ addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "")
default:
addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "")
}
adduint(ctxt, moduledata, 0)
adduint(ctxt, moduledata, 0)
}
+ if Buildmode == BuildmodePlugin {
+ addgostring(ctxt, moduledata, "go.link.thispluginpath", *flagPluginPath)
+ } else {
+ adduint(ctxt, moduledata, 0)
+ adduint(ctxt, moduledata, 0)
+ }
if len(ctxt.Shlibs) > 0 {
thismodulename := filepath.Base(*flagOutfile)
switch Buildmode {
// Plugin is a loaded Go plugin.
type Plugin struct {
- name string
- loaded chan struct{} // closed when loaded
- syms map[string]interface{}
+ pluginpath string
+ loaded chan struct{} // closed when loaded
+ syms map[string]interface{}
}
// Open opens a Go plugin.
}
C.free(unsafe.Pointer(cRelName))
- path := C.GoString(cPath)
+ filepath := C.GoString(cPath)
pluginsMu.Lock()
- if p := plugins[path]; p != nil {
+ if p := plugins[filepath]; p != nil {
pluginsMu.Unlock()
<-p.loaded
return p, nil
}
// TODO(crawshaw): look for plugin note, confirm it is a Go plugin
// and it was built with the correct toolchain.
- // TODO(crawshaw): get full plugin name from note.
if len(name) > 3 && name[len(name)-3:] == ".so" {
name = name[:len(name)-3]
}
- syms := lastmoduleinit()
+ pluginpath, syms := lastmoduleinit()
if plugins == nil {
plugins = make(map[string]*Plugin)
}
// This function can be called from the init function of a plugin.
// Drop a placeholder in the map so subsequent opens can wait on it.
p := &Plugin{
- name: name,
- loaded: make(chan struct{}),
- syms: syms,
+ pluginpath: pluginpath,
+ loaded: make(chan struct{}),
+ syms: syms,
}
- plugins[path] = p
+ plugins[filepath] = p
pluginsMu.Unlock()
- initStr := C.CString(name + ".init")
+ initStr := C.CString(pluginpath + ".init")
initFuncPC := C.pluginLookup(h, initStr, &cErr)
C.free(unsafe.Pointer(initStr))
if initFuncPC != nil {
symName = symName[1:]
}
- cname := C.CString(name + "." + symName)
+ cname := C.CString(pluginpath + "." + symName)
p := C.pluginLookup(h, cname, &cErr)
C.free(unsafe.Pointer(cname))
if p == nil {
if s := p.syms[symName]; s != nil {
return s, nil
}
- return nil, errors.New("plugin: symbol " + symName + " not found in plugin " + p.name)
+ return nil, errors.New("plugin: symbol " + symName + " not found in plugin " + p.pluginpath)
}
var (
plugins map[string]*Plugin
)
-func lastmoduleinit() map[string]interface{} // in package runtime
+// lastmoduleinit is defined in package runtime
+func lastmoduleinit() (pluginpath string, syms map[string]interface{})
import "unsafe"
//go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
-func plugin_lastmoduleinit() map[string]interface{} {
+func plugin_lastmoduleinit() (path string, syms map[string]interface{}) {
md := firstmoduledata.next
if md == nil {
throw("runtime: no plugin module data")
throw("runtime: plugin already initialized")
}
- if fmd := &firstmoduledata; inRange(fmd.text, fmd.etext, md.text, md.etext) ||
- inRange(fmd.bss, fmd.ebss, md.bss, md.ebss) ||
- inRange(fmd.data, fmd.edata, md.data, md.edata) ||
- inRange(fmd.types, fmd.etypes, md.types, md.etypes) {
- println("plugin: new module data overlaps with firstmoduledata")
- println("\tfirstmoduledata.text-etext=", hex(fmd.text), "-", hex(fmd.etext))
- println("\tfirstmoduledata.bss-ebss=", hex(fmd.bss), "-", hex(fmd.ebss))
- println("\tfirstmoduledata.data-edata=", hex(fmd.data), "-", hex(fmd.edata))
- println("\tfirstmoduledata.types-etypes=", hex(fmd.types), "-", hex(fmd.etypes))
- println("\tmd.text-etext=", hex(md.text), "-", hex(md.etext))
- println("\tmd.bss-ebss=", hex(md.bss), "-", hex(md.ebss))
- println("\tmd.data-edata=", hex(md.data), "-", hex(md.edata))
- println("\tmd.types-etypes=", hex(md.types), "-", hex(md.etypes))
- throw("plugin: new module data overlaps with firstmoduledata")
+ for pmd := &firstmoduledata; pmd != md; pmd = pmd.next {
+ if pmd.pluginpath == md.pluginpath {
+ println("plugin: plugin", md.pluginpath, "already loaded")
+ throw("plugin: plugin already loaded")
+ }
+
+ if inRange(pmd.text, pmd.etext, md.text, md.etext) ||
+ inRange(pmd.bss, pmd.ebss, md.bss, md.ebss) ||
+ inRange(pmd.data, pmd.edata, md.data, md.edata) ||
+ inRange(pmd.types, pmd.etypes, md.types, md.etypes) {
+ println("plugin: new module data overlaps with previous moduledata")
+ println("\tpmd.text-etext=", hex(pmd.text), "-", hex(pmd.etext))
+ println("\tpmd.bss-ebss=", hex(pmd.bss), "-", hex(pmd.ebss))
+ println("\tpmd.data-edata=", hex(pmd.data), "-", hex(pmd.edata))
+ println("\tpmd.types-etypes=", hex(pmd.types), "-", hex(pmd.etypes))
+ println("\tmd.text-etext=", hex(md.text), "-", hex(md.etext))
+ println("\tmd.bss-ebss=", hex(md.bss), "-", hex(md.ebss))
+ println("\tmd.data-edata=", hex(md.data), "-", hex(md.edata))
+ println("\tmd.types-etypes=", hex(md.types), "-", hex(md.etypes))
+ throw("plugin: new module data overlaps with previous moduledata")
+ }
}
// Initialize the freshly loaded module.
// Because functions are handled specially in the plugin package,
// function symbol names are prefixed here with '.' to avoid
// a dependency on the reflect package.
- syms := make(map[string]interface{}, len(md.ptab))
+ syms = make(map[string]interface{}, len(md.ptab))
for _, ptab := range md.ptab {
symName := resolveNameOff(unsafe.Pointer(md.types), ptab.name)
t := (*_type)(unsafe.Pointer(md.types)).typeOff(ptab.typ)
}
syms[name] = val
}
- return syms
+ return md.pluginpath, syms
}
// inRange reports whether v0 or v1 are in the range [r0, r1].
ptab []ptabEntry
+ pluginpath string
modulename string
modulehashes []modulehash