golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.9k stars 17.65k forks source link

plugin: report the version of Go used to build a plugin when a mismatch occurs #63290

Open mfreeman451 opened 1 year ago

mfreeman451 commented 1 year ago

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

$ go version
go version go1.21.1 linux/amd64

Does this issue reproduce with the latest release?

Y

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

go env Output
$ go env
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/mfreeman/.cache/go-build'
GOENV='/home/mfreeman/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOOS='linux'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.21.1'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build4277136579=/tmp/go-build -gno-record-gcc-switches'

What did you do?

go build -buildmode=plugin -o ./handler.so ./main.go

Then when I tried to use it with another program:

2023/09/29 02:11:29 main.go:26: plugin error: plugin.Open("/plugin/handler"): plugin was built with a different version of package google.golang.org/protobuf/internal/pragma

What did you expect to see?

It should tell me what the different version is so I can match it.

What did you see instead?

Nothing other than a not very useful message telling me that I have the wrong version, but not what the correct version should be.

cherrymui commented 1 year ago

You can run go version -m handler.so and go version -m <main_executable> to find out the versions and how they differ. Would that be helpful?

mfreeman451 commented 1 year ago

I was dealing with a blackbox situation where I didn't have access to the program loading my shared library, all I can see is that there was a version mismatch.

On Fri, Sep 29, 2023 at 10:48 AM cherrymui @.***> wrote:

You can run go version -m handler.so and go version -m to find out the versions and how they differ. Would that be helpful?

— Reply to this email directly, view it on GitHub https://github.com/golang/go/issues/63290#issuecomment-1741104730, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAN4Z2TGR36GF4NP2QRNJJTX43UVHANCNFSM6AAAAAA5L2FRQE . You are receiving this because you authored the thread.Message ID: @.***>

quantonganh commented 1 year ago

You can run go version -m handler.so and go version -m <main_executable> to find out the versions and how they differ. Would that be helpful?

I'm afraid that there are some cases where go version -m cannot help.

├── go.mod
├── go.sum
├── main.go
├── plugin
│   ├── go.mod
│   ├── go.sum
│   ├── myplugin.so
│   └── vendor
└── vendor
    ├── golang.org
    └── modules.txt

plugin/myplugin.go:

package main

import (
    "fmt"

    _ "golang.org/x/xerrors"
)

var ExportedVariable int = 42

func ExportedFunction() {
    fmt.Println("Hello from the plugin!")
}

main.go:

package main

import (
    "fmt"
    "plugin"

    _ "golang.org/x/xerrors"
)

func main() {
    p, err := plugin.Open("plugin/myplugin.so")
    if err != nil {
        fmt.Println("Error opening plugin:", err)
        return
    }

    exportedVar, err := p.Lookup("ExportedVariable")
    if err != nil {
        fmt.Println("Error looking up variable:", err)
        return
    }

    varValue, ok := exportedVar.(*int)
    if !ok {
        fmt.Println("Error getting variable value")
        return
    }

    fmt.Println("Exported variable:", *varValue)

    exportedFunc, err := p.Lookup("ExportedFunction")
    if err != nil {
        fmt.Println("Error looking up function:", err)
        return
    }

    exportedFunc.(func())()
}

Build the plugin:

$ cd plugin
$ go build -buildmode=plugin -mod=vendor -o myplugin.so myplugin.go

Build the main program:

$ cd ..
$ go build -mod=vendor -o main main.go
$ ./main
Error opening plugin: plugin.Open("plugin/myplugin"): plugin was built with a different version of package golang.org/x/xerrors/internal
$ go version -m plugin/myplugin.so
plugin/myplugin.so: devel go1.22-5873bd1d7e Mon Oct 16 03:29:27 2023 +0000
        path    command-line-arguments
        dep     golang.org/x/xerrors    v0.0.0-20231012003039-104605ab7028      h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
        build   -buildmode=plugin
        build   -compiler=gc
        build   CGO_ENABLED=1
        build   CGO_CFLAGS=
        build   CGO_CPPFLAGS=
        build   CGO_CXXFLAGS=
        build   CGO_LDFLAGS=
        build   GOARCH=arm64
        build   GOOS=darwin

$ go version -m main
main: devel go1.22-5873bd1d7e Mon Oct 16 03:29:27 2023 +0000
        path    command-line-arguments
        dep     golang.org/x/xerrors    v0.0.0-20231012003039-104605ab7028      h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
        build   -buildmode=exe
        build   -compiler=gc
        build   CGO_ENABLED=1
        build   CGO_CFLAGS=
        build   CGO_CPPFLAGS=
        build   CGO_CXXFLAGS=
        build   CGO_LDFLAGS=
        build   GOARCH=arm64
        build   GOOS=darwin

It should tell me what the different version is so I can match it.

The root cause might be something else: the full file path, the build flags, ... not the package version. Moreover, I'm not sure if we can tell what the different versions are. I tried to make a change here:

diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go
index 40dfefde17..33022c60f2 100644
--- a/src/runtime/plugin.go
+++ b/src/runtime/plugin.go
@@ -51,7 +51,7 @@ func plugin_lastmoduleinit() (path string, syms map[string]any, initTasks []*ini
        for _, pkghash := range md.pkghashes {
                if pkghash.linktimehash != *pkghash.runtimehash {
                        md.bad = true
-                       return "", nil, nil, "plugin was built with a different version of package " + pkghash.modulename
+                       return "", nil, nil, "plugin was built with a different version of package " + pkghash.modulename + ": got " + *pkghash.runtimehash + ", want " + pkghash.linktimehash
                }
        }

but what I got is something like this:

Error opening plugin: plugin.Open("plugin/myplugin"): plugin was built with a different version of package golang.org/x/xerrors/internal: got %uV�┌, ┬▒┼├ NWIꘓ├