Commit 9da70584 authored by David Crawshaw's avatar David Crawshaw

cmd/link, plugin: use full plugin path for symbols

Plumb the import path of a plugin package through to the linker, and
use it as the prefix on the exported symbol names.

Before this we used the basename of the plugin file as the prefix,
which could conflict and result in multiple loaded plugins sharing
symbols that are distinct.

Fixes #17155
Fixes #17579

Change-Id: I7ce966ca82d04e8507c0bcb8ea4ad946809b1ef5
Reviewed-on: https://go-review.googlesource.com/32355Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent 590fce48
...@@ -7,6 +7,7 @@ package main ...@@ -7,6 +7,7 @@ package main
import ( import (
"fmt" "fmt"
"log" "log"
"path/filepath"
"plugin" "plugin"
"common" "common"
...@@ -36,15 +37,51 @@ func main() { ...@@ -36,15 +37,51 @@ func main() {
log.Fatalf(`Lookup("Seven") failed: %v`, err) log.Fatalf(`Lookup("Seven") failed: %v`, err)
} }
if got, want := *seven.(*int), 7; got != want { 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") readFunc, err := p.Lookup("ReadCommonX")
if err != nil { 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 { 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") fmt.Println("PASS")
......
...@@ -9,6 +9,8 @@ import "C" ...@@ -9,6 +9,8 @@ import "C"
import "common" import "common"
func F() int { return 3 }
func ReadCommonX() int { func ReadCommonX() int {
return common.X return common.X
} }
......
// 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")
}
...@@ -15,13 +15,15 @@ goos=$(go env GOOS) ...@@ -15,13 +15,15 @@ goos=$(go env GOOS)
goarch=$(go env GOARCH) goarch=$(go env GOARCH)
function cleanup() { function cleanup() {
rm -f plugin1.so host pkg rm -rf plugin1.so host pkg sub
} }
trap cleanup EXIT 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 plugin1
GOPATH=$(pwd) go build -buildmode=plugin -o=sub/plugin1.so sub/plugin1
GOPATH=$(pwd) go build host GOPATH=$(pwd) go build host
LD_LIBRARY_PATH=$(pwd) ./host LD_LIBRARY_PATH=$(pwd) ./host
...@@ -2546,6 +2546,9 @@ func (gcToolchain) ld(b *builder, root *action, out string, allactions []*action ...@@ -2546,6 +2546,9 @@ func (gcToolchain) ld(b *builder, root *action, out string, allactions []*action
if root.p.omitDWARF { if root.p.omitDWARF {
ldflags = append(ldflags, "-w") 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 // If the user has not specified the -extld option, then specify the
// appropriate linker. In case of C++ code, use the compiler named // appropriate linker. In case of C++ code, use the compiler named
......
...@@ -85,6 +85,8 @@ Flags: ...@@ -85,6 +85,8 @@ Flags:
Link with C/C++ memory sanitizer support. Link with C/C++ memory sanitizer support.
-o file -o file
Write output to file (default a.out, or a.out.exe on Windows). 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:... -r dir1:dir2:...
Set the ELF dynamic linker search path. Set the ELF dynamic linker search path.
-race -race
......
...@@ -37,7 +37,6 @@ import ( ...@@ -37,7 +37,6 @@ import (
"flag" "flag"
"log" "log"
"os" "os"
"path/filepath"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"strings" "strings"
...@@ -59,6 +58,7 @@ var ( ...@@ -59,6 +58,7 @@ var (
flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id") flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
flagOutfile = flag.String("o", "", "write output to `file`") 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") FlagLinkshared = flag.Bool("linkshared", false, "link against installed Go shared libraries")
flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`") flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`")
...@@ -175,8 +175,7 @@ func Main() { ...@@ -175,8 +175,7 @@ func Main() {
addlibpath(ctxt, "command line", "command line", file, pkgpath, "") addlibpath(ctxt, "command line", "command line", file, pkgpath, "")
} }
case BuildmodePlugin: case BuildmodePlugin:
pluginName := strings.TrimSuffix(filepath.Base(flag.Arg(0)), ".a") addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "")
addlibpath(ctxt, "command line", "command line", flag.Arg(0), pluginName, "")
default: default:
addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "") addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "")
} }
......
...@@ -603,6 +603,12 @@ func (ctxt *Link) symtab() { ...@@ -603,6 +603,12 @@ func (ctxt *Link) symtab() {
adduint(ctxt, moduledata, 0) adduint(ctxt, moduledata, 0)
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 { if len(ctxt.Shlibs) > 0 {
thismodulename := filepath.Base(*flagOutfile) thismodulename := filepath.Base(*flagOutfile)
switch Buildmode { switch Buildmode {
......
...@@ -18,7 +18,7 @@ package plugin ...@@ -18,7 +18,7 @@ package plugin
// Plugin is a loaded Go plugin. // Plugin is a loaded Go plugin.
type Plugin struct { type Plugin struct {
name string pluginpath string
loaded chan struct{} // closed when loaded loaded chan struct{} // closed when loaded
syms map[string]interface{} syms map[string]interface{}
} }
......
...@@ -49,10 +49,10 @@ func open(name string) (*Plugin, error) { ...@@ -49,10 +49,10 @@ func open(name string) (*Plugin, error) {
} }
C.free(unsafe.Pointer(cRelName)) C.free(unsafe.Pointer(cRelName))
path := C.GoString(cPath) filepath := C.GoString(cPath)
pluginsMu.Lock() pluginsMu.Lock()
if p := plugins[path]; p != nil { if p := plugins[filepath]; p != nil {
pluginsMu.Unlock() pluginsMu.Unlock()
<-p.loaded <-p.loaded
return p, nil return p, nil
...@@ -65,26 +65,25 @@ func open(name string) (*Plugin, error) { ...@@ -65,26 +65,25 @@ func open(name string) (*Plugin, error) {
} }
// TODO(crawshaw): look for plugin note, confirm it is a Go plugin // TODO(crawshaw): look for plugin note, confirm it is a Go plugin
// and it was built with the correct toolchain. // 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" { if len(name) > 3 && name[len(name)-3:] == ".so" {
name = name[:len(name)-3] name = name[:len(name)-3]
} }
syms := lastmoduleinit() pluginpath, syms := lastmoduleinit()
if plugins == nil { if plugins == nil {
plugins = make(map[string]*Plugin) plugins = make(map[string]*Plugin)
} }
// This function can be called from the init function of a 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. // Drop a placeholder in the map so subsequent opens can wait on it.
p := &Plugin{ p := &Plugin{
name: name, pluginpath: pluginpath,
loaded: make(chan struct{}), loaded: make(chan struct{}),
syms: syms, syms: syms,
} }
plugins[path] = p plugins[filepath] = p
pluginsMu.Unlock() pluginsMu.Unlock()
initStr := C.CString(name + ".init") initStr := C.CString(pluginpath + ".init")
initFuncPC := C.pluginLookup(h, initStr, &cErr) initFuncPC := C.pluginLookup(h, initStr, &cErr)
C.free(unsafe.Pointer(initStr)) C.free(unsafe.Pointer(initStr))
if initFuncPC != nil { if initFuncPC != nil {
...@@ -101,7 +100,7 @@ func open(name string) (*Plugin, error) { ...@@ -101,7 +100,7 @@ func open(name string) (*Plugin, error) {
symName = symName[1:] symName = symName[1:]
} }
cname := C.CString(name + "." + symName) cname := C.CString(pluginpath + "." + symName)
p := C.pluginLookup(h, cname, &cErr) p := C.pluginLookup(h, cname, &cErr)
C.free(unsafe.Pointer(cname)) C.free(unsafe.Pointer(cname))
if p == nil { if p == nil {
...@@ -123,7 +122,7 @@ func lookup(p *Plugin, symName string) (Symbol, error) { ...@@ -123,7 +122,7 @@ func lookup(p *Plugin, symName string) (Symbol, error) {
if s := p.syms[symName]; s != nil { if s := p.syms[symName]; s != nil {
return 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 ( var (
...@@ -131,4 +130,5 @@ var ( ...@@ -131,4 +130,5 @@ var (
plugins map[string]*Plugin 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{})
...@@ -7,7 +7,7 @@ package runtime ...@@ -7,7 +7,7 @@ package runtime
import "unsafe" import "unsafe"
//go:linkname plugin_lastmoduleinit plugin.lastmoduleinit //go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
func plugin_lastmoduleinit() map[string]interface{} { func plugin_lastmoduleinit() (path string, syms map[string]interface{}) {
md := firstmoduledata.next md := firstmoduledata.next
if md == nil { if md == nil {
throw("runtime: no plugin module data") throw("runtime: no plugin module data")
...@@ -19,20 +19,27 @@ func plugin_lastmoduleinit() map[string]interface{} { ...@@ -19,20 +19,27 @@ func plugin_lastmoduleinit() map[string]interface{} {
throw("runtime: plugin already initialized") throw("runtime: plugin already initialized")
} }
if fmd := &firstmoduledata; inRange(fmd.text, fmd.etext, md.text, md.etext) || for pmd := &firstmoduledata; pmd != md; pmd = pmd.next {
inRange(fmd.bss, fmd.ebss, md.bss, md.ebss) || if pmd.pluginpath == md.pluginpath {
inRange(fmd.data, fmd.edata, md.data, md.edata) || println("plugin: plugin", md.pluginpath, "already loaded")
inRange(fmd.types, fmd.etypes, md.types, md.etypes) { throw("plugin: plugin already loaded")
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)) if inRange(pmd.text, pmd.etext, md.text, md.etext) ||
println("\tfirstmoduledata.data-edata=", hex(fmd.data), "-", hex(fmd.edata)) inRange(pmd.bss, pmd.ebss, md.bss, md.ebss) ||
println("\tfirstmoduledata.types-etypes=", hex(fmd.types), "-", hex(fmd.etypes)) 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.text-etext=", hex(md.text), "-", hex(md.etext))
println("\tmd.bss-ebss=", hex(md.bss), "-", hex(md.ebss)) println("\tmd.bss-ebss=", hex(md.bss), "-", hex(md.ebss))
println("\tmd.data-edata=", hex(md.data), "-", hex(md.edata)) println("\tmd.data-edata=", hex(md.data), "-", hex(md.edata))
println("\tmd.types-etypes=", hex(md.types), "-", hex(md.etypes)) println("\tmd.types-etypes=", hex(md.types), "-", hex(md.etypes))
throw("plugin: new module data overlaps with firstmoduledata") throw("plugin: new module data overlaps with previous moduledata")
}
} }
// Initialize the freshly loaded module. // Initialize the freshly loaded module.
...@@ -54,7 +61,7 @@ func plugin_lastmoduleinit() map[string]interface{} { ...@@ -54,7 +61,7 @@ func plugin_lastmoduleinit() map[string]interface{} {
// Because functions are handled specially in the plugin package, // Because functions are handled specially in the plugin package,
// function symbol names are prefixed here with '.' to avoid // function symbol names are prefixed here with '.' to avoid
// a dependency on the reflect package. // 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 { for _, ptab := range md.ptab {
symName := resolveNameOff(unsafe.Pointer(md.types), ptab.name) symName := resolveNameOff(unsafe.Pointer(md.types), ptab.name)
t := (*_type)(unsafe.Pointer(md.types)).typeOff(ptab.typ) t := (*_type)(unsafe.Pointer(md.types)).typeOff(ptab.typ)
...@@ -68,7 +75,7 @@ func plugin_lastmoduleinit() map[string]interface{} { ...@@ -68,7 +75,7 @@ func plugin_lastmoduleinit() map[string]interface{} {
} }
syms[name] = val syms[name] = val
} }
return syms return md.pluginpath, syms
} }
// inRange reports whether v0 or v1 are in the range [r0, r1]. // inRange reports whether v0 or v1 are in the range [r0, r1].
......
...@@ -201,6 +201,7 @@ type moduledata struct { ...@@ -201,6 +201,7 @@ type moduledata struct {
ptab []ptabEntry ptab []ptabEntry
pluginpath string
modulename string modulename string
modulehashes []modulehash modulehashes []modulehash
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment