go: plugin: convert iface to type failed with "types from different scopes"

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

$ go version
go version devel go1.18-14e812bfc5 Fri Sep 17 00:31:49 2021 +0000 linux/amd64

Does this issue reproduce with the latest release?

yes

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

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/root/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/root/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/mnt/d/00.Tool/00.golang/go-master"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/mnt/d/00.Tool/00.golang/go-master/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="devel go1.18-14e812bfc5 Fri Sep 17 00:31:49 2021 +0000"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/mnt/d/01.Project/03.corego_performance/demo/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 -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build691445209=. -gno-record-gcc-switches"

What did you do?

pluginassert/go.mod:

module pluginassert

go 1.18

pluginassert/inter/iface.go:

package inter

type A interface {
	Method1()
}

type B struct{}

func (b *B) Method1() {
	println("Method1 in B")
}

pluginassert/main.go:

package main

import (
	"log"
	"plugin"
	"pluginassert/inter"
)

func main() {
	var a interface{}
	a = &inter.B{}
	switch x := a.(type) {
	case inter.A:
		x.Method1()
	default:
		log.Fatalln("assert x to A failed")
	}

	p, err := plugin.Open("./pluginassert.so")
	if err != nil {
		log.Fatalln(err)
	}
	sym, err := p.Lookup("TestFunc")
	if err != nil {
		log.Fatalln(err)
	}

	testFunc := sym.(func(inter.A))
	switch x := a.(type) {
	case inter.A:
		testFunc(x)
	default:
		log.Fatalln("assert x to A failed")
	}
}

pluginassert/plugin.go:

package main

import (
	"pluginassert/inter"
)

func TestFunc(a inter.A) {
	b := a.(*inter.B)
	b.Method1()
}

~/pluginassert $ go build -buildmode=plugin ~/pluginassert $ go build ~/pluginassert $ ./pluginassert

What did you expect to see?

Method1 in B
Method1 in B

What did you see instead?

Method1 in B
panic: interface conversion: inter.A is *inter.B, not *inter.B (types from different scopes)

goroutine 1 [running]:
pluginassert.TestFunc({0x7fe0643fdff8, 0x6406b8})
        pluginassert/plugin.go:8 +0x67
main.main()
        pluginassert/main.go:31 +0x204

There is no symbol go.itab.*pluginassert/inter.B,pluginassert/inter.A in main binary. So it will invoke persistentalloc in function getitab in first switch-case to allocate an itab.

But unfortunately, after plugin is loaded, the itab go.itab.*pluginassert/inter.B,pluginassert/inter.A was added to itabTable. When the TestFunc was invoked in second switch-case, x is a iface with itab which was allocated in first switch-case.

In plugin, the implement of a.(*inter.B) is to compare the address of a.tab and go.itab.*pluginassert/inter.B,pluginassert/inter.A. Certainly they are different. So we get this panic.

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Comments: 21 (14 by maintainers)

Most upvoted comments

If we had our own dynamic linker…