Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin: panic on plugin.Open for different plugins #19358

Closed
rpoletaev opened this issue Mar 2, 2017 · 12 comments
Closed

plugin: panic on plugin.Open for different plugins #19358

rpoletaev opened this issue Mar 2, 2017 · 12 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@rpoletaev
Copy link

What version of Go are you using (go version)?

go version go1.8 linux/amd64

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/roma/projects/go"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build810676719=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"

What did you do?

i have several plugins:
$: tree .
.
├── 6.0
│   ├── export.so
│   └── plugin.go
├── 6.1
│   ├── export.so
│   └── plugin.go
├── 6.2
│   ├── export.so
│   └── plugin.go
├── 6.2.100
│   ├── export.so
│   └── plugin.go
├── 6.3
│   ├── export.so
│   └── plugin.go
├── 6.4
│   ├── export.so
│   └── plugin.go
├── 6.4.6
│   ├── export.so
│   └── plugin.go
├── 7.0
│   ├── export.so
│   └── plugin.go
├── 7.0.9
│   ├── export.so
│   └── plugin.go
└── 7.1
├── export.so
└── plugin.go

each plugin export.so compiled from its own source, like a:

$: cd 6.0/
$: go build -buildmode=plugin -o export.so plugin.go
$: cd ../6.1/
$: go build -buildmode=plugin -o export.so plugin.go
$: cd ../6.2/
$: go build -buildmode=plugin -o export.so plugin.go
...

all plugins has same type names, but this tipes may have diferent fields... or same fields. This may vary from version to version
all plugins has one function with same name
all plugins has diferent size

i tried change plugin names like:

$: cd 6.0/
$: go build -buildmode=plugin -o export6.0.so plugin.go
$: cd ../6.1/
$: go build -buildmode=plugin -o export6.1.so plugin.go
$: cd ../6.2/
$: go build -buildmode=plugin -o export6.2.so plugin.go
...

an second plugin.Open throws panic:

p1, err := plugin.Open("/repository/6.0/export.so")
if err != nil {
     println(err)
}

p2, err := plugin.Open("/repository/6.1/export.so")
if err != nil{
    println(err)
}

What did you expect to see?

i expect that i will have 2 instances diferent plugins

What did you see instead?

panic:

plugin: plugin plugin/unnamed-5e323bb7e1479d9f71342c85ad29e7552ca5f746 already loaded
fatal error: plugin: plugin already loaded

goroutine 1 [running]:
runtime.throw(0x534999, 0x1d)
	/usr/local/go/src/runtime/panic.go:596 +0x95 fp=0xc420045b80 sp=0xc420045b60
plugin.lastmoduleinit(0x1e5a400, 0xc42000fb18, 0x1e5ba70, 0xc, 0x7b52a0)
	/usr/local/go/src/runtime/plugin.go:25 +0xc1a fp=0xc420045cb0 sp=0xc420045b80
plugin.open(0x530dda, 0x6, 0x0, 0x0, 0x0)
	/usr/local/go/src/plugin/plugin_dlopen.go:72 +0x25d fp=0xc420045ef0 sp=0xc420045cb0
plugin.Open(0x530dda, 0x6, 0xc42000c160, 0x0, 0x0)
	/usr/local/go/src/plugin/plugin.go:30 +0x35 fp=0xc420045f28 sp=0xc420045ef0
main.main()
	/data/main.go:39 +0xae fp=0xc420045f88 sp=0xc420045f28
runtime.main()
	/usr/local/go/src/runtime/proc.go:185 +0x20a fp=0xc420045fe0 sp=0xc420045f88
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc420045fe8 sp=0xc420045fe0

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2197 +0x1
exit status 2
@ALTree ALTree changed the title panic on plugin.Open for different plugins plugin: panic on plugin.Open for different plugins Mar 2, 2017
@ALTree ALTree added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Mar 2, 2017
@ALTree
Copy link
Member

ALTree commented Mar 2, 2017

cc @crawshaw

@sparrc
Copy link

sparrc commented Mar 9, 2017

I am having the same issue. For more details, I am building two different files located in different directories, although their file names are both main.go

Here I have steps to reproduce, first you need to setup the following files (note directory names for the two plugins):

main.go

package main

import (
	"plugin"
)

func main() {
	println("opening p1.so")
	_, err := plugin.Open("./p1.so")
	if err != nil {
		println(err.Error())
	}

	println("opening p2.so")
	_, err = plugin.Open("./p2.so")
	if err != nil {
		println(err.Error())
	}
}

p1/main.go

package main

type Example struct{}

func (e *Example) SampleConfig() string { return "" }
func (e *Example) Description() string  { return "example processor" }

p2/main.go

package main

type Example struct{}

func (e *Example) Connect() error       { return nil }
func (e *Example) Close() error         { return nil }
func (e *Example) Description() string  { return "Example output plugin" }
func (e *Example) SampleConfig() string { return "" }
  1. Build the two plugins:
go build -buildmode=plugin -o ./p1.so ./p1/main.go
go build -buildmode=plugin -o ./p2.so ./p2/main.go
  1. Run the main program that will attempt to load the plugins and observe panic:
$ go run main.go
opening p1.so
opening p2.so
plugin: plugin plugin/unnamed-5639aa0a277d71c2df5be0a51259e16f0815143c already loaded
fatal error: plugin: plugin already loaded

goroutine 1 [running]:
runtime.throw(0x4b2686, 0x1d)
	/usr/local/go/src/runtime/panic.go:596 +0x95 fp=0xc420047b98 sp=0xc420047b78
plugin.lastmoduleinit(0x19b5420, 0xc4200690d0, 0x19b6aa0, 0x14, 0x7040a0)
	/usr/local/go/src/runtime/plugin.go:25 +0xc1a fp=0xc420047cc8 sp=0xc420047b98
plugin.open(0x4af834, 0x7, 0x0, 0x0, 0x0)
	/usr/local/go/src/plugin/plugin_dlopen.go:72 +0x25d fp=0xc420047f08 sp=0xc420047cc8
plugin.Open(0x4af834, 0x7, 0xc420066040, 0x0, 0x0)
	/usr/local/go/src/plugin/plugin.go:30 +0x35 fp=0xc420047f40 sp=0xc420047f08
main.main()
	/vagrant/panic/main.go:15 +0xb6 fp=0xc420047f88 sp=0xc420047f40
runtime.main()
	/usr/local/go/src/runtime/proc.go:185 +0x20a fp=0xc420047fe0 sp=0xc420047f88
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc420047fe8 sp=0xc420047fe0

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2197 +0x1
exit status 2

@sparrc
Copy link

sparrc commented Mar 9, 2017

I can also confirm that the issue here is that the plugins share the same filename main.go. If you change p1/main.go to p1/p1.go then it does not panic or error.

@bradfitz bradfitz added this to the Go1.9Maybe milestone Mar 21, 2017
@LionNatsu
Copy link
Contributor

LionNatsu commented Mar 25, 2017

Whether they are DIFFERENT or not dependents on the Plugin Path which you can specify by -pluginpath during linking.

If no any plugin path has been specified for plugin (which is not in the tree of src/packagename/...), it will use "plugin/unnamed-" + root.Package.Internal.BuildID, the Build ID, as the plugin path.

I searched in the code base and I found func computeBuildID(p *Package) for computing this SHA-1 code. It seems that it must be different Build ID if the code has changed. @sparrc @rpoletaev Could you provide the result of readelf --notes <yourplugin>.so of these so'es?

@LionNatsu
Copy link
Contributor

I did readelf of the example provided by @sparrc.
It can be reproduced here, go 1.8.

lion@Lion-Laptop [ test ] ! readelf -n p1.so p2.so 

File: p1.so

Displaying notes found in: .note.go.buildid
  Owner                 Data size       Description
  Go                   0x00000028       Unknown note type: (0x00000004)
   description data: 34 64 33 38 36 30 38 63 66 63 37 39 36 38 32 37 63 32 33 30 61 38 62 38 61 66 61 63 32 65 30 35 62 64 62 34 62 65 32 37 

Displaying notes found in: .note.gnu.build-id
  Owner                 Data size       Description
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 33f7f53423144c038faf21726f46b01365d955e8

Displaying notes found in: .note.gnu.gold-version
  Owner                 Data size       Description
  GNU                  0x00000009       NT_GNU_GOLD_VERSION (gold version)
    Version: gold 1.12

File: p2.so

Displaying notes found in: .note.go.buildid
  Owner                 Data size       Description
  Go                   0x00000028       Unknown note type: (0x00000004)
   description data: 34 64 33 38 36 30 38 63 66 63 37 39 36 38 32 37 63 32 33 30 61 38 62 38 61 66 61 63 32 65 30 35 62 64 62 34 62 65 32 37 

Displaying notes found in: .note.gnu.build-id
  Owner                 Data size       Description
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 629238271f690121696e34ab1919185981a7ff9c

Displaying notes found in: .note.gnu.gold-version
  Owner                 Data size       Description
  GNU                  0x00000009       NT_GNU_GOLD_VERSION (gold version)
    Version: gold 1.12
lion@Lion-Laptop [ test ] $ 

@LionNatsu
Copy link
Contributor

It can be simpler.

p1/main.go

package main

var V int = 1

p2/main.go

package main

var V int = 2

main.go

package main

import "plugin"
import "os"

func run(path string) {
	myplugin, _ := plugin.Open(path)
	symbol, _ := myplugin.Lookup("V")
	instance := *symbol.(*int)
	println(instance)
}

func main() {
	for _, arg := range os.Args[1:] {
		run(arg)
	}
}

Run in shell

go build -buildmode=plugin -o ./p1.so ./p1/main.go
go build -buildmode=plugin -o ./p2.so ./p2/main.go
go run main.go p1.so
go run main.go p2.so
go run main.go p1.so p2.so
readelf -n p1.so p2.so

Output

lion@Lion-Laptop [ test ] ! go build -buildmode=plugin -o ./p1.so ./p1/main.go
lion@Lion-Laptop [ test ] $ go build -buildmode=plugin -o ./p2.so ./p2/main.go
lion@Lion-Laptop [ test ] $ go run main.go p1.so
1
lion@Lion-Laptop [ test ] $ go run main.go p2.so
2
lion@Lion-Laptop [ test ] $ go run main.go p1.so p2.so
1
plugin: plugin plugin/unnamed-4d38608cfc796827c230a8b8afac2e05bdb4be27 already loaded
fatal error: plugin: plugin already loaded

goroutine 1 [running]:
runtime.throw(0x4df83d, 0x1d)
        /usr/lib/go/src/runtime/panic.go:596 +0x95 fp=0xc42004db20 sp=0xc42004db00
plugin.lastmoduleinit(0x1c04400, 0xc42000f400, 0x1c05a70, 0x15, 0x53fa60)
        /usr/lib/go/src/runtime/plugin.go:25 +0xc1a fp=0xc42004dc50 sp=0xc42004db20
plugin.open(0x7ffd4dd6d1bc, 0x5, 0x0, 0x0, 0x0)
        /usr/lib/go/src/plugin/plugin_dlopen.go:72 +0x25d fp=0xc42004de90 sp=0xc42004dc50
plugin.Open(0x7ffd4dd6d1bc, 0x5, 0x4dc21d, 0x1, 0xc42008a088)
        /usr/lib/go/src/plugin/plugin.go:30 +0x35 fp=0xc42004dec8 sp=0xc42004de90
main.run(0x7ffd4dd6d1bc, 0x5)
        /home/lion/test/main.go:7 +0x4a fp=0xc42004df50 sp=0xc42004dec8
main.main()
        /home/lion/test/main.go:15 +0x7e fp=0xc42004df88 sp=0xc42004df50
runtime.main()
        /usr/lib/go/src/runtime/proc.go:185 +0x20a fp=0xc42004dfe0 sp=0xc42004df88
runtime.goexit()
        /usr/lib/go/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc42004dfe8 sp=0xc42004dfe0

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
        /usr/lib/go/src/runtime/asm_amd64.s:2197 +0x1
exit status 2
lion@Lion-Laptop [ test ] ! readelf -n p1.so p2.so

File: p1.so

Displaying notes found in: .note.go.buildid
  Owner                 Data size       Description
  Go                   0x00000028       Unknown note type: (0x00000004)
   description data: 34 64 33 38 36 30 38 63 66 63 37 39 36 38 32 37 63 32 33 30 61 38 62 38 61 66 61 63 32 65 30 35 62 64 62 34 62 65 32 37 

Displaying notes found in: .note.gnu.build-id
  Owner                 Data size       Description
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 4d0fb1a1f39d057afd3242d403fa85cf323ab445

Displaying notes found in: .note.gnu.gold-version
  Owner                 Data size       Description
  GNU                  0x00000009       NT_GNU_GOLD_VERSION (gold version)
    Version: gold 1.12

File: p2.so

Displaying notes found in: .note.go.buildid
  Owner                 Data size       Description
  Go                   0x00000028       Unknown note type: (0x00000004)
   description data: 34 64 33 38 36 30 38 63 66 63 37 39 36 38 32 37 63 32 33 30 61 38 62 38 61 66 61 63 32 65 30 35 62 64 62 34 62 65 32 37 

Displaying notes found in: .note.gnu.build-id
  Owner                 Data size       Description
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: a4a9b5ffd0b4bf09a995c8519307d21da97d0d64

Displaying notes found in: .note.gnu.gold-version
  Owner                 Data size       Description
  GNU                  0x00000009       NT_GNU_GOLD_VERSION (gold version)
    Version: gold 1.12
lion@Lion-Laptop [ test ] $ 

@LionNatsu
Copy link
Contributor

Build ID only checks
1). the file names (of Go sources, C/C++ headers, S files etc.)
2). the content of zversion.go
3). the Build ID of dependencies.
if I'm not mistaken.

So it's no wonder that the Build ID, a SHA-1, collision happens, if they have the same dependencies and the same file name (main.go) .

@LionNatsu
Copy link
Contributor

Relevant to #19004

@bradfitz bradfitz modified the milestones: Go1.9Maybe, Go1.10 Jun 7, 2017
@gopherbot
Copy link

Change https://golang.org/cl/61170 mentions this issue: cmd/go: add source file contents to plugin hash

@radu-matei
Copy link

radu-matei commented Dec 14, 2017

This works in 1.10-rc

Thanks!

@ALTree
Copy link
Member

ALTree commented Dec 14, 2017

@radu-matei the fix will be in go1.10 (scheduled for release next February).

@radu-matei
Copy link

Awesome!
Bumped into the issue and solved it with 1.10-rc - just wanted to confirm it works, as there has been no activity on this for a while.

Thanks a lot!

radu-matei added a commit to radu-matei/azure-functions-golang-worker that referenced this issue Dec 16, 2017
@golang golang locked and limited conversation to collaborators Dec 16, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

7 participants