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: can't open the same plugin with different names, like dlopen #29525

Open
JesseGuoX opened this issue Jan 3, 2019 · 15 comments
Open

plugin: can't open the same plugin with different names, like dlopen #29525

JesseGuoX opened this issue Jan 3, 2019 · 15 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@JesseGuoX
Copy link

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

$ go version
go version go1.11.4 linux/386

Does this issue reproduce with the latest release?

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

go env Output
$ go env
GOARCH="386"
GOBIN=""
GOCACHE="/home/pw/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="386"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/pw/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_386"
GCCGO="gccgo"
GO386="sse2"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/pw/Documents/hello/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m32 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build808633565=/tmp/go-build -gno-record-gcc-switches"

What did you do?

go build -buildmode=plugin -o scanner.so scanner.go
cp scanner.so scanner2.so
package main

import (
	"fmt"
	"os"
	"plugin"
)


type Greeter interface {
    Greet()
}


func main() {


	plug, err := plugin.Open("./plugins/scanner.so")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
	}

	symGreeter, err := plug.Lookup("Greeter")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
	}

	var greeter Greeter
    greeter, ok := symGreeter.(Greeter)
    if !ok {
        fmt.Println("unexpected type from module symbol")
        os.Exit(1)
	}

	plug2, err := plugin.Open("./plugins/scanner2.so")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
	}

	symGreeter2, err := plug2.Lookup("Greeter")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
	}

	var greeter2 Greeter
    greeter2, ok2 := symGreeter2.(Greeter)
    if !ok2 {
        fmt.Println("unexpected type from module symbol")
        os.Exit(1)
    }

    // 4. use the module
    greeter.Greet()
    greeter2.Greet()

}

What did you expect to see?

Same plugin copyed to different names can plugin.Open successfully and have different instances.
For example, I have a lot of scanners connect to my device and the number of scanners is uncertain,
the scanners have same protocol but the serial port is different, so I need to load the same scanner plugin and pass different serial port to the plugin to scan,so it is not suitable to complie different plugin by 'pluginpath', if I need 100 scanners I need to complie 100 times. Why can't just like dlopen distinguish by names?

What did you see instead?

plugin.Open("./plugins/scanner2.so"): plugin already loaded
@mvdan
Copy link
Member

mvdan commented Jan 3, 2019

I presume it's because of this bit of doc:

When a plugin is first opened, the init functions of all packages not already part of the program are called. The main function is not run. A plugin is only initialized once, and cannot be closed.

If you could load the same plugin twice with different paths, you'd effectively run the same init function twice. That seems to go against how plugins should be used, but the documentation isn't terribly clear about this edge case.

/cc @ianlancetaylor @cherrymui as per https://dev.golang.org/owners/

@mvdan mvdan added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jan 3, 2019
@mvdan mvdan changed the title why golang plugin can't open same plugin by different name like 'dlopen'? plugin: can't open the same plugin with different names, like dlopen Jan 3, 2019
@JesseGuoX
Copy link
Author

Thank you @mvdan , I am a newcomer to golang. I am evaluating whether golang is suitable for current arm embedded projects. Is plugin not implemented for linux/arm yet?

@cherrymui
Copy link
Member

@Jexbat Plugin is supported on Linux/ARM.

@JesseGuoX
Copy link
Author

@cherrymui GOOS=linux GOARCH=arm go build and return:plugin: not implemented.

$ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped

I found same issue #19569, then I enable cgo like

CGO_ENABLED=1 GOOS=linux GOARCH=arm CC="arm-poky-linux-gnueabi-gcc" CXX="arm-poky-linux-gnueabi-g++" LD="arm-poky-linux-gnueabi-ld.gold" CGO_CFLAGS="-march=armv7-a -mfpu=neon  -mfloat-abi=hard -mcpu=cortex-a9 --sysroot=/opt/fsl-imx-fb/4.1.15-2.0.1/sysroots/cortexa9hf-neon-poky-linux-gnueabi" CGO_CXXFLAGS="-march=armv7-a -mfpu=neon  -mfloat-abi=hard -mcpu=cortex-a9 --sysroot=/opt/fsl-imx-fb/4.1.15-2.0.1/sysroots/cortexa9hf-neon-poky-linux-gnueabi" CGO_LDFLAGS="  --sysroot=/opt/fsl-imx-fb/4.1.15-2.0.1/sysroots/cortexa9hf-neon-poky-linux-gnueabi" go build

Compile is succuss but when I exec it return No such file or directory.

$ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 2.6.32, BuildID[sha1]=cd89bbf6e60242498f31375ee63e89ef7ca9c1a3, not stripped

Could it be cross-compile toolchain problem?

@ianlancetaylor
Copy link
Contributor

I assume you are copying the executable built with GOARCH=arm to an ARM system in order to run it. If you are getting "No such file or directory" on that system then the problem is almost certainly a disagreement about the location of the dynamic linker. You can use readelf -l EXECUTABLE to see the dynamic linker that it requests; look for the "Requesting program interpreter" line. See if that file exists on your ARM system. (When using cgo the dynamic linker is set by the C linker, and is not controlled by the Go tools.)

@JesseGuoX
Copy link
Author

@ianlancetaylor sovled with ln -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3, thanks.
But I am still confused with plugin, if I compiled same plugin with different -pluginpath does two plugins share the same variable when I calling same func in two plugins?

hello.go

package main

import (
	"fmt"
	"os"
	"plugin"
)

type Greeter interface {
	Greet()
}

func main() {

	plug, err := plugin.Open("./plugins/scanner.so")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	symGreeter, err := plug.Lookup("Greeter")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	var greeter Greeter
	greeter, ok := symGreeter.(Greeter)
	if !ok {
		fmt.Println("unexpected type from module symbol")
		os.Exit(1)
	}
	greeter.Greet()
	print("=======\n")

	plug2, err := plugin.Open("./plugins/scanner2.so")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	symGreeter2, err := plug2.Lookup("Greeter")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	var greeter2 Greeter
	greeter2, ok2 := symGreeter2.(Greeter)
	if !ok2 {
		fmt.Println("unexpected type from module symbol")
		os.Exit(1)
	}

	greeter2.Greet()

}

scanner.go

package main

import "fmt"

type greeting string

var count int

func (g greeting) Greet() {
	count++
	fmt.Printf("Hello Universe %d\n", count)
}

// exported as symbol named "Greeter"
var Greeter greeting

scanner.go compiled with two different -pluginpath:

...  go build -ldflags "-pluginpath=p2" -buildmode=plugin -o scanner2.so scanner.go
...  go build -ldflags "-pluginpath=p1" -buildmode=plugin -o scanner.so scanner.go

result is:

root@imx6dlsabresd:/tmp# ls plugins/
scanner.so  scanner2.so
root@imx6dlsabresd:/tmp# ./hello
Hello Universe 1
=======
Hello Universe 2
root@imx6dlsabresd:/tmp#

Does it should be Hello Universe 1 and Hello Universe 1? It seems like two different plugins share same variable count.

@JesseGuoX
Copy link
Author

I found change file name can solve this situation.Change scanner.go to scanner2.go then compile withoutpluginpath. result is:

root@imx6dlsabresd:/tmp# ./hello
Hello Universe 1
=======
Hello Universe 1

I think pluginpath just resolved plugin.open problem and still share variable in it.

@witwit
Copy link

witwit commented Aug 19, 2019

Hello,
Old thread, but still confused about a detail:
GOOS=linux GOARCH=arm does not work for plugins? Plugins do not run on linux/arm ? I have built a simple example, that builds a simple plugin like:
CC=arm-linux-gnueabi-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-pluginpath=blah" -buildmode=plugin -o ./arm-dist/reader.linux.arm.so /app/plugins/rea der/...

No errors,

however when a main app on a raspberrypi tries to open the plugin, it says: could not open ./arm-dist/caller.linux.arm.so plugin: not implemented

is this the case? or did I miss something? somewhat of a showstopper...

@randall77
Copy link
Contributor

@witwit: That should work. One thing you might be hitting - your main app also needs to be compiled with CGO_ENABLED=1.

@cherrymui
Copy link
Member

@witwit this doesn't seem to be related to this issue, so I recommend discussing this somewhere else.

For your problem, how do you build the program that opens the plugin? In particular, is it built with cgo enabled?

@witwit
Copy link

witwit commented Aug 19, 2019

Oh, thank you, that got me a bit further. CGO_ENABLED=1 was missing for the main app, d'oh!
Now I am stuck at a different error message

could not open /home/pi/plugged/reader.linux.arm.so plugin.Open("/home/pi/plugged/reader.linux.arm.so"): /home/pi/plugged/reader.linux.arm.so: cannot open shared object file: No such file or directory

@witwit
Copy link

witwit commented Aug 19, 2019

@witwit this doesn't seem to be related to this issue, so I recommend discussing this somewhere else.

For your problem, how do you build the program that opens the plugin? In particular, is it built with cgo enabled?

yes, sorry. I truggle to find any infos on this at all. I will post a question on stack overflow.

@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jul 7, 2022
@seankhliao seankhliao added this to the Unplanned milestone Aug 20, 2022
@exfly
Copy link

exfly commented Dec 14, 2022

# plugin code in plugin/hello.go
package main

import "fmt"

var V int

func F() { fmt.Printf("Hello, number %d\n", V) }


# main in main.go
package main

import "plugin"

func main() {
	p, err := plugin.Open("plugin/hello.so")
	if err != nil {
		panic(err)
	}

	v, err := p.Lookup("V")
	if err != nil {
		panic(err)
	}
	f, err := p.Lookup("F")
	if err != nil {
		panic(err)
	}
	*v.(*int) = 7
	f.(func())() // prints "Hello, number 7"

	p, err = plugin.Open("plugin/hello2.so")
	if err != nil {
		panic(err)
	}
	v, err = p.Lookup("V")
	if err != nil {
		panic(err)
	}
	f, err = p.Lookup("F")
	if err != nil {
		panic(err)
	}
	_ = v
	// *v.(*int) = 7
	f.(func())() // prints "Hello, number 7"
}
go build -trimpath -ldflags "-pluginpath=plugin/hot-$(date +%s)" -buildmode=plugin -o plugin/hello.so plugin/hello.go
go build -trimpath -ldflags "-pluginpath=plugin/hot-$(date +%s)" -buildmode=plugin -o plugin/hello2.so plugin/hello.go

go run -trimpath main.go
panic: plugin.Open("plugin/hello"): could not find symbol V: dlsym(0x100205980, plugin/hot-1670990324.V): symbol not found

goroutine 1 [running]:
main.main()
        ./main.go:8 +0x4ae
exit status 2

nm plugin/hello.so| grep '.V'
00000000001d6e68 S _plugin/unnamed-b2203a0be6431d575e0f9179980c3c58f09e7987.V
000000000007e200 t _reflect.(*MapIter).Value

Is there something wrong with compiling the symbol table?

@titpetric
Copy link

titpetric commented Nov 17, 2023

Ok, as this is a duplicate, do we have some work arounds for more modern go versions that would allow us to:

  1. explicitly set the plugin name?
  2. --buildvcs=true to build a unique plugin while respecting -trimpath?

I particularly mention this as buildvcs is a recent addition, and there's -pluginpath #19418 ; seems both options have no effect in plugin builds. Trimpath for plugins should, if anything, replace the plugin path with name.$(date +%s) or some equivalent?

Is there any approach considered in resolving this in the toolchain? Would we need a proposal to discuss a change, submit patches, or how can we move this forward?

@titpetric
Copy link

titpetric commented Nov 17, 2023

@ianlancetaylor @cherrymui as cmd/link and plugins package owners, I would appreciate your input and perhaps guidance towards contributing a patch, priorities permitting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
Status: Triage Backlog
Development

No branches or pull requests

10 participants