Skip to content

Commit 9da7058

Browse files
committed
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/32355 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 590fce4 commit 9da7058

File tree

12 files changed

+119
-40
lines changed

12 files changed

+119
-40
lines changed

misc/cgo/testplugin/src/host/host.go

+40-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package main
77
import (
88
"fmt"
99
"log"
10+
"path/filepath"
1011
"plugin"
1112

1213
"common"
@@ -36,15 +37,51 @@ func main() {
3637
log.Fatalf(`Lookup("Seven") failed: %v`, err)
3738
}
3839
if got, want := *seven.(*int), 7; got != want {
39-
log.Fatalf("via lookup plugin1.Seven=%d, want %d", got, want)
40+
log.Fatalf("plugin1.Seven=%d, want %d", got, want)
4041
}
4142

4243
readFunc, err := p.Lookup("ReadCommonX")
4344
if err != nil {
44-
log.Fatalf(`Lookup("ReadCommonX") failed: %v`, err)
45+
log.Fatalf(`plugin1.Lookup("ReadCommonX") failed: %v`, err)
4546
}
4647
if got := readFunc.(func() int)(); got != wantX {
47-
log.Fatalf("via lookup plugin1.ReadCommonX()=%d, want %d", got, wantX)
48+
log.Fatalf("plugin1.ReadCommonX()=%d, want %d", got, wantX)
49+
}
50+
51+
// sub/plugin1.so is a different plugin with the same name as
52+
// the already loaded plugin. It also depends on common. Test
53+
// that we can load the different plugin, it is actually
54+
// different, and that it sees the same common package.
55+
subpPath, err := filepath.Abs("sub/plugin1.so")
56+
if err != nil {
57+
log.Fatalf("filepath.Abs(%q) failed: %v", subpPath, err)
58+
}
59+
subp, err := plugin.Open(subpPath)
60+
if err != nil {
61+
log.Fatalf("plugin.Open(%q) failed: %v", subpPath, err)
62+
}
63+
64+
readFunc, err = subp.Lookup("ReadCommonX")
65+
if err != nil {
66+
log.Fatalf(`sub/plugin1.Lookup("ReadCommonX") failed: %v`, err)
67+
}
68+
if got := readFunc.(func() int)(); got != wantX {
69+
log.Fatalf("sub/plugin1.ReadCommonX()=%d, want %d", got, wantX)
70+
}
71+
72+
subf, err := subp.Lookup("F")
73+
if err != nil {
74+
log.Fatalf(`sub/plugin1.Lookup("F") failed: %v`, err)
75+
}
76+
if gotf := subf.(func() int)(); gotf != 17 {
77+
log.Fatalf(`sub/plugin1.F()=%d, want 17`, gotf)
78+
}
79+
f, err := p.Lookup("F")
80+
if err != nil {
81+
log.Fatalf(`plugin1.Lookup("F") failed: %v`, err)
82+
}
83+
if gotf := f.(func() int)(); gotf != 3 {
84+
log.Fatalf(`plugin1.F()=%d, want 17`, gotf)
4885
}
4986

5087
fmt.Println("PASS")

misc/cgo/testplugin/src/plugin1/plugin1.go

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import "C"
99

1010
import "common"
1111

12+
func F() int { return 3 }
13+
1214
func ReadCommonX() int {
1315
return common.X
1416
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2016 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
// // No C code required.
8+
import "C"
9+
10+
import "common"
11+
12+
func F() int { return 17 }
13+
14+
func ReadCommonX() int {
15+
return common.X
16+
}
17+
18+
func main() {
19+
panic("plugin1.main called")
20+
}

misc/cgo/testplugin/test.bash

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ goos=$(go env GOOS)
1515
goarch=$(go env GOARCH)
1616

1717
function cleanup() {
18-
rm -f plugin1.so host pkg
18+
rm -rf plugin1.so host pkg sub
1919
}
2020
trap cleanup EXIT
2121

22-
rm -rf pkg
22+
rm -rf pkg sub
23+
mkdir sub
2324

2425
GOPATH=$(pwd) go build -buildmode=plugin plugin1
26+
GOPATH=$(pwd) go build -buildmode=plugin -o=sub/plugin1.so sub/plugin1
2527
GOPATH=$(pwd) go build host
2628

2729
LD_LIBRARY_PATH=$(pwd) ./host

src/cmd/go/build.go

+3
Original file line numberDiff line numberDiff line change
@@ -2546,6 +2546,9 @@ func (gcToolchain) ld(b *builder, root *action, out string, allactions []*action
25462546
if root.p.omitDWARF {
25472547
ldflags = append(ldflags, "-w")
25482548
}
2549+
if buildBuildmode == "plugin" {
2550+
ldflags = append(ldflags, "-pluginpath", root.p.ImportPath)
2551+
}
25492552

25502553
// If the user has not specified the -extld option, then specify the
25512554
// appropriate linker. In case of C++ code, use the compiler named

src/cmd/link/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ Flags:
8585
Link with C/C++ memory sanitizer support.
8686
-o file
8787
Write output to file (default a.out, or a.out.exe on Windows).
88+
-pluginpath path
89+
The path name used to prefix exported plugin symbols.
8890
-r dir1:dir2:...
8991
Set the ELF dynamic linker search path.
9092
-race

src/cmd/link/internal/ld/main.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import (
3737
"flag"
3838
"log"
3939
"os"
40-
"path/filepath"
4140
"runtime"
4241
"runtime/pprof"
4342
"strings"
@@ -59,6 +58,7 @@ var (
5958
flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
6059

6160
flagOutfile = flag.String("o", "", "write output to `file`")
61+
flagPluginPath = flag.String("pluginpath", "", "full path name for plugin")
6262
FlagLinkshared = flag.Bool("linkshared", false, "link against installed Go shared libraries")
6363

6464
flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`")
@@ -175,8 +175,7 @@ func Main() {
175175
addlibpath(ctxt, "command line", "command line", file, pkgpath, "")
176176
}
177177
case BuildmodePlugin:
178-
pluginName := strings.TrimSuffix(filepath.Base(flag.Arg(0)), ".a")
179-
addlibpath(ctxt, "command line", "command line", flag.Arg(0), pluginName, "")
178+
addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "")
180179
default:
181180
addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "")
182181
}

src/cmd/link/internal/ld/symtab.go

+6
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,12 @@ func (ctxt *Link) symtab() {
603603
adduint(ctxt, moduledata, 0)
604604
adduint(ctxt, moduledata, 0)
605605
}
606+
if Buildmode == BuildmodePlugin {
607+
addgostring(ctxt, moduledata, "go.link.thispluginpath", *flagPluginPath)
608+
} else {
609+
adduint(ctxt, moduledata, 0)
610+
adduint(ctxt, moduledata, 0)
611+
}
606612
if len(ctxt.Shlibs) > 0 {
607613
thismodulename := filepath.Base(*flagOutfile)
608614
switch Buildmode {

src/plugin/plugin.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ package plugin
1818

1919
// Plugin is a loaded Go plugin.
2020
type Plugin struct {
21-
name string
22-
loaded chan struct{} // closed when loaded
23-
syms map[string]interface{}
21+
pluginpath string
22+
loaded chan struct{} // closed when loaded
23+
syms map[string]interface{}
2424
}
2525

2626
// Open opens a Go plugin.

src/plugin/plugin_dlopen.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ func open(name string) (*Plugin, error) {
4949
}
5050
C.free(unsafe.Pointer(cRelName))
5151

52-
path := C.GoString(cPath)
52+
filepath := C.GoString(cPath)
5353

5454
pluginsMu.Lock()
55-
if p := plugins[path]; p != nil {
55+
if p := plugins[filepath]; p != nil {
5656
pluginsMu.Unlock()
5757
<-p.loaded
5858
return p, nil
@@ -65,26 +65,25 @@ func open(name string) (*Plugin, error) {
6565
}
6666
// TODO(crawshaw): look for plugin note, confirm it is a Go plugin
6767
// and it was built with the correct toolchain.
68-
// TODO(crawshaw): get full plugin name from note.
6968
if len(name) > 3 && name[len(name)-3:] == ".so" {
7069
name = name[:len(name)-3]
7170
}
7271

73-
syms := lastmoduleinit()
72+
pluginpath, syms := lastmoduleinit()
7473
if plugins == nil {
7574
plugins = make(map[string]*Plugin)
7675
}
7776
// This function can be called from the init function of a plugin.
7877
// Drop a placeholder in the map so subsequent opens can wait on it.
7978
p := &Plugin{
80-
name: name,
81-
loaded: make(chan struct{}),
82-
syms: syms,
79+
pluginpath: pluginpath,
80+
loaded: make(chan struct{}),
81+
syms: syms,
8382
}
84-
plugins[path] = p
83+
plugins[filepath] = p
8584
pluginsMu.Unlock()
8685

87-
initStr := C.CString(name + ".init")
86+
initStr := C.CString(pluginpath + ".init")
8887
initFuncPC := C.pluginLookup(h, initStr, &cErr)
8988
C.free(unsafe.Pointer(initStr))
9089
if initFuncPC != nil {
@@ -101,7 +100,7 @@ func open(name string) (*Plugin, error) {
101100
symName = symName[1:]
102101
}
103102

104-
cname := C.CString(name + "." + symName)
103+
cname := C.CString(pluginpath + "." + symName)
105104
p := C.pluginLookup(h, cname, &cErr)
106105
C.free(unsafe.Pointer(cname))
107106
if p == nil {
@@ -123,12 +122,13 @@ func lookup(p *Plugin, symName string) (Symbol, error) {
123122
if s := p.syms[symName]; s != nil {
124123
return s, nil
125124
}
126-
return nil, errors.New("plugin: symbol " + symName + " not found in plugin " + p.name)
125+
return nil, errors.New("plugin: symbol " + symName + " not found in plugin " + p.pluginpath)
127126
}
128127

129128
var (
130129
pluginsMu sync.Mutex
131130
plugins map[string]*Plugin
132131
)
133132

134-
func lastmoduleinit() map[string]interface{} // in package runtime
133+
// lastmoduleinit is defined in package runtime
134+
func lastmoduleinit() (pluginpath string, syms map[string]interface{})

src/runtime/plugin.go

+24-17
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ package runtime
77
import "unsafe"
88

99
//go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
10-
func plugin_lastmoduleinit() map[string]interface{} {
10+
func plugin_lastmoduleinit() (path string, syms map[string]interface{}) {
1111
md := firstmoduledata.next
1212
if md == nil {
1313
throw("runtime: no plugin module data")
@@ -19,20 +19,27 @@ func plugin_lastmoduleinit() map[string]interface{} {
1919
throw("runtime: plugin already initialized")
2020
}
2121

22-
if fmd := &firstmoduledata; inRange(fmd.text, fmd.etext, md.text, md.etext) ||
23-
inRange(fmd.bss, fmd.ebss, md.bss, md.ebss) ||
24-
inRange(fmd.data, fmd.edata, md.data, md.edata) ||
25-
inRange(fmd.types, fmd.etypes, md.types, md.etypes) {
26-
println("plugin: new module data overlaps with firstmoduledata")
27-
println("\tfirstmoduledata.text-etext=", hex(fmd.text), "-", hex(fmd.etext))
28-
println("\tfirstmoduledata.bss-ebss=", hex(fmd.bss), "-", hex(fmd.ebss))
29-
println("\tfirstmoduledata.data-edata=", hex(fmd.data), "-", hex(fmd.edata))
30-
println("\tfirstmoduledata.types-etypes=", hex(fmd.types), "-", hex(fmd.etypes))
31-
println("\tmd.text-etext=", hex(md.text), "-", hex(md.etext))
32-
println("\tmd.bss-ebss=", hex(md.bss), "-", hex(md.ebss))
33-
println("\tmd.data-edata=", hex(md.data), "-", hex(md.edata))
34-
println("\tmd.types-etypes=", hex(md.types), "-", hex(md.etypes))
35-
throw("plugin: new module data overlaps with firstmoduledata")
22+
for pmd := &firstmoduledata; pmd != md; pmd = pmd.next {
23+
if pmd.pluginpath == md.pluginpath {
24+
println("plugin: plugin", md.pluginpath, "already loaded")
25+
throw("plugin: plugin already loaded")
26+
}
27+
28+
if inRange(pmd.text, pmd.etext, md.text, md.etext) ||
29+
inRange(pmd.bss, pmd.ebss, md.bss, md.ebss) ||
30+
inRange(pmd.data, pmd.edata, md.data, md.edata) ||
31+
inRange(pmd.types, pmd.etypes, md.types, md.etypes) {
32+
println("plugin: new module data overlaps with previous moduledata")
33+
println("\tpmd.text-etext=", hex(pmd.text), "-", hex(pmd.etext))
34+
println("\tpmd.bss-ebss=", hex(pmd.bss), "-", hex(pmd.ebss))
35+
println("\tpmd.data-edata=", hex(pmd.data), "-", hex(pmd.edata))
36+
println("\tpmd.types-etypes=", hex(pmd.types), "-", hex(pmd.etypes))
37+
println("\tmd.text-etext=", hex(md.text), "-", hex(md.etext))
38+
println("\tmd.bss-ebss=", hex(md.bss), "-", hex(md.ebss))
39+
println("\tmd.data-edata=", hex(md.data), "-", hex(md.edata))
40+
println("\tmd.types-etypes=", hex(md.types), "-", hex(md.etypes))
41+
throw("plugin: new module data overlaps with previous moduledata")
42+
}
3643
}
3744

3845
// Initialize the freshly loaded module.
@@ -54,7 +61,7 @@ func plugin_lastmoduleinit() map[string]interface{} {
5461
// Because functions are handled specially in the plugin package,
5562
// function symbol names are prefixed here with '.' to avoid
5663
// a dependency on the reflect package.
57-
syms := make(map[string]interface{}, len(md.ptab))
64+
syms = make(map[string]interface{}, len(md.ptab))
5865
for _, ptab := range md.ptab {
5966
symName := resolveNameOff(unsafe.Pointer(md.types), ptab.name)
6067
t := (*_type)(unsafe.Pointer(md.types)).typeOff(ptab.typ)
@@ -68,7 +75,7 @@ func plugin_lastmoduleinit() map[string]interface{} {
6875
}
6976
syms[name] = val
7077
}
71-
return syms
78+
return md.pluginpath, syms
7279
}
7380

7481
// inRange reports whether v0 or v1 are in the range [r0, r1].

src/runtime/symtab.go

+1
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ type moduledata struct {
201201

202202
ptab []ptabEntry
203203

204+
pluginpath string
204205
modulename string
205206
modulehashes []modulehash
206207

0 commit comments

Comments
 (0)