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: loading plugin leads to 'fatal error: invalid runtime symbol table' with some stdlib packages #18190

Closed
mccolljr opened this issue Dec 3, 2016 · 36 comments
Labels
FrozenDueToAge help wanted NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@mccolljr
Copy link

mccolljr commented Dec 3, 2016

Please answer these questions before submitting your issue. Thanks!

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

$ go version
go version go1.8beta1 darwin/amd64

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

$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/mccolljr/Documents/dev/go"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.8beta1/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.8beta1/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/rg/pwv319qj009dkq19ryb8x3m00000gn/T/go-build046386885=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
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?

pg.go

attempted builds (same error for each):
go build --buildmode=plugin pg.go
go build --gcflags "-dynlink" --buildmode=plugin pg.go

package main

import (
	"log"
	"net/http"

	"html/template"
)

var Handler http.Handler

func init() {
	Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		p, err := template.ParseFiles("x.tpl")
		if err != nil {
			log.Fatalln(err)
		}

		p.ExecuteTemplate(w, "x.tpl", nil)
	})
}
x.tpl
TEST!
main.go

attempted builds (same error for each):
go build main.go && ./main
go build --gcflags "-dynlink" main.go && ./main

package main

import (
	"fmt"
	"net/http"
	"plugin"
)

func main() {
	p, err := plugin.Open("pg.so")

	if err != nil {
		panic(err)
	}

	sym, err := p.Lookup("Handler")

	if err != nil {
		panic(err)
	}

	fmt.Println("starting...")

	http.ListenAndServe(":8080", *(sym.(*http.Handler)))
}

note
This program works correctly when you change pg.go to:

package main

import (
	"fmt"
	"net/http"
)

var Handler http.Handler

func init() {
	Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "TEST!")
	})
}

What did you expect to see?

terminal:

$ ./main
starting...

browser:

TEST!

What did you see instead?

runtime: invalid pc-encoded table f=gE#?? pc=0x40d92c1 targetpc=0x53dde9d tab=[0/0]0x0
	value=-1 until pc=0x40d9290
	value=0 until pc=0x40d9290
	value=0 until pc=0x40d9290
	value=228 until pc=0x40d92c1
	value=230 until pc=0x40d92c1
fatal error: invalid runtime symbol table

runtime stack:
invalid spdelta r; runtime.sizeclass int32; runtime.large bool } 0x405b9c0 0x5329fe5 0x3f3b4 -1
r; runtime.sizeclass int32; runtime.large bool }(0x100, 0x7fff5fbfef9800, 0x534336700)
	?:0 +0x12ce625

goroutine 1 [copystack]:
invalid spdelta gE#?? 0x40d9290 0x53dde9d 0x78c52 -1
gE#??(0xc4200ce64000, 0xa00, 0xa00, 0xc42006ed9800, 0xc420014db000)
	?:0 +0x1304c0d fp=0xc420137b6f sp=0xc420137b68

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
	/usr/local/Cellar/go/1.8beta1/libexec/src/runtime/asm_amd64.s:2184 +0x1
exit status 2
@mccolljr mccolljr changed the title using loading plugin using stdlib package html/template fails: 'fatal error: invalid runtime symbol table' Dec 3, 2016
@mccolljr mccolljr changed the title loading plugin using stdlib package html/template fails: 'fatal error: invalid runtime symbol table' plugin: loading plugin leads to 'fatal error: invalid runtime symbol table' with some stdlib packages Dec 4, 2016
@mccolljr
Copy link
Author

mccolljr commented Dec 4, 2016

If I change the import from html/template to text/template, the panic is deferred until the first attempted connection

$ ./main
starting...
runtime: invalid pc-encoded table f=me.sudog pc=0x405b9fb targetpc=0x582b7ef tab=[0/0]0x0
	value=39 until pc=0x405b9f3
	value=-1 until pc=0x405b9fb
fatal error: invalid runtime symbol table

runtime stack:
invalid spdelta me.sudog 0x405b990 0x5829f55 0x3e014 -1
me.sudog(0x100, 0x70000010381000, 0x58432d700)
	?:0 +0x17ce5c5

goroutine 5 [copystack]:
invalid spdelta me.sudog 0x405b990 0x582b7ef 0x3e014 -1
me.sudog(0x430ff3000, 0xc42001c41800, 0x5afadef00)
	?:0 +0x17cfe5f fp=0xc42004d35f sp=0xc42004d358
created by net/http.(*Server).Serve
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/http/server.go:2656 +0x2ce

goroutine 1 [IO wait]:
net.runtime_pollWait(0x48b78b0, 0x72, 0x0)
	/usr/local/Cellar/go/1.8beta1/libexec/src/runtime/netpoll.go:164 +0x59
net.(*pollDesc).wait(0xc4200564c8, 0x72, 0x0, 0xc4200c0720)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/fd_poll_runtime.go:75 +0x38
net.(*pollDesc).waitRead(0xc4200564c8, 0xffffffffffffffff, 0x0)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/fd_poll_runtime.go:80 +0x34
net.(*netFD).accept(0xc420056460, 0x0, 0x44395e0, 0xc4200c0720)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/fd_unix.go:430 +0x1e5
net.(*TCPListener).accept(0xc4200fb510, 0xc42008c800, 0x42a9220, 0xffffffffffffffff)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/tcpsock_posix.go:136 +0x2e
net.(*TCPListener).AcceptTCP(0xc4200fb510, 0xc42004bdb0, 0xc42004bdb8, 0xc42004bda8)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/tcpsock.go:215 +0x49
net/http.tcpKeepAliveListener.Accept(0xc4200fb510, 0x430fa10, 0xc42008c780, 0x443d8a0, 0xc420014f00)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/http/server.go:3032 +0x2f
net/http.(*Server).Serve(0xc42009a2c0, 0x443d420, 0xc4200fb510, 0x0, 0x0)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/http/server.go:2631 +0x228
net/http.(*Server).ListenAndServe(0xc42009a2c0, 0xc42009a2c0, 0x0)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/http/server.go:2573 +0xb0
net/http.ListenAndServe(0x42fecfc, 0x5, 0x5c2d5e0, 0x5b08d28, 0x0, 0x0)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/http/server.go:2775 +0x7f
main.main()
	/Users/mccolljr/Documents/dev/go/src/gitlab.com/mccolljr_/plugins/main.go:24 +0x18c

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
	/usr/local/Cellar/go/1.8beta1/libexec/src/runtime/asm_amd64.s:2184 +0x1

goroutine 6 [IO wait]:
net.runtime_pollWait(0x48b77f0, 0x72, 0x5)
	/usr/local/Cellar/go/1.8beta1/libexec/src/runtime/netpoll.go:164 +0x59
net.(*pollDesc).wait(0xc420056538, 0x72, 0x443a960, 0x4437478)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/fd_poll_runtime.go:75 +0x38
net.(*pollDesc).waitRead(0xc420056538, 0xc420016691, 0x1)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/fd_poll_runtime.go:80 +0x34
net.(*netFD).Read(0xc4200564d0, 0xc420016691, 0x1, 0x1, 0x0, 0x443a960, 0x4437478)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/fd_unix.go:250 +0x1b7
net.(*conn).Read(0xc4200fb518, 0xc420016691, 0x1, 0x1, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/net.go:181 +0x70
net/http.(*connReader).backgroundRead(0xc420016680)
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/http/server.go:644 +0x58
created by net/http.(*connReader).startBackgroundRead
	/usr/local/Cellar/go/1.8beta1/libexec/src/net/http/server.go:640 +0x87

goroutine 7 [running]:
	goroutine running on other thread; stack unavailable
created by text/template/parse.lex
	/usr/local/Cellar/go/1.8beta1/libexec/src/text/template/parse/lex.go:224 +0x181

@ianlancetaylor ianlancetaylor added this to the Go1.9 milestone Dec 5, 2016
@crawshaw
Copy link
Member

crawshaw commented Dec 6, 2016

I assume this is another dynamic relocation bug on macOS.

@ianlancetaylor do you think we should remove the macOS plugin support from 1.8? It clearly needs more testing, and it seems a bit late in the cycle to be resolving issues like this.

@ianlancetaylor
Copy link
Contributor

I'm fine with that.

@krisnova
Copy link

krisnova commented Dec 7, 2016

Is there an open issue for tracking the progress of this feature in 1.9 now that we are pushing this back? Just curious - I would like to follow along if this is getting hammered on for future releases.

@crawshaw
Copy link
Member

crawshaw commented Dec 7, 2016

@kris-nova you can create one if you like. This is just a spare time project for me, so I'm not generating much of a paper trail.

The program described above is failing executing the init functions of the plugin. I got lucky experimenting with it and extracted this partial stacktrace:

panic: runtime error: slice bounds out of range
fatal error: panic on system stack

runtime stack:
invalid spdelta  0x4033690 0x5027595 0x20898 -1
invalid spdelta  0x4033690 0x5027595 0x20898 -1
()
        ?:0 +0xff3f05

goroutine 1 [copystack]:
strings.NewReplacer(0xc4200be000, 0xa, 0xa, 0xc4200b89a8)
        /Users/crawshaw/go/src/strings/replace.go:23 +0x5bd fp=0xc4200c7b78 sp=0xc4200c7b70
html.init()
        /Users/crawshaw/go/src/html/escape.go:172 +0x24ea fp=0xc4200c7bd8 sp=0xc4200c7b78
html/template.init()
        /Users/crawshaw/go/src/html/template/url.go:106 +0x71 fp=0xc4200c7c58 sp=0xc4200c7bd8
panic: runtime error: slice bounds out of range
fatal error: panic on system stack

Indeed, an empty plugin doing nothing other than import _ "html" fails in its init code. So there's some bad codegen in there somewhere.

@crawshaw
Copy link
Member

crawshaw commented Dec 7, 2016

Here's a simplified plugin that produces this error on plugin.Open:

package main
        
type r struct {
        r interface{}
}       
        
type b [256][]byte                                                                                   
        
func newr() *r {
        v := b{}
        return &r{r: &v} 
}                       
                
var escaper *r  
                
func init() {   
        escaper = newr()
}               
        
func F() {
}

@crawshaw
Copy link
Member

crawshaw commented Dec 7, 2016

Followed a few false leads. A simpler example. If the host program calls plugin.Open then executes the symbol F, then this is all that's necessary to see a failure:

package main

func F() {
        _ = make([]byte, 1<<21)
}

@crawshaw
Copy link
Member

crawshaw commented Dec 8, 2016

Checkpoint of what I've learned so far.

When gentraceback is called, it finds a *_func using a PC from one module, but whose entry field is from another module:

md0: minpc= 0x40026c0 maxpc= 0x40555a0
md1: minpc= 0x5801bf0 maxpc= 0x5842860
gentraceback, f.entry= 0x40514b0
gentraceback, frame.pc= 0x581a244

The result is that the frame.pc cannot be used as a targetpc when using the pclntable derived from f.entry.

I believe this is cased by the fact that *_func entry is filled by an R_ADDR which the linker turns into a dynamic relocation, so for a runtime function it points to the original module. But the second module, from the plugin, also has the runtime functions, and somewhere there's a direct CALL to that version of that function.

We don't see this on linux because all appropriate function calls go through the PLT/GOT. We are missing such function call when GOOS=darwin.

@gopherbot
Copy link

CL https://golang.org/cl/34196 mentions this issue.

@crawshaw
Copy link
Member

crawshaw commented Dec 9, 2016

CL 34196 fixes the problem I described above, but it exposes another bug executing strings.NewReplacer() in a plugin:

runtime: pcdata is 6 and 3 locals stack map entries for runtime.mallocgc (targetpc=92323189)
fatal error: bad symbol table

runtime stack:
runtime.throw(0x40726b0, 0x10)
        /Users/crawshaw/go/src/runtime/panic.go:596 +0x95
runtime.adjustframe(0x7fff5fbff418, 0x7fff5fbff518, 0x7fff5fbff201)
        /Users/crawshaw/go/src/runtime/stack.go:674 +0x6de
runtime.gentraceback(0xffffffffffffffff, 0xc4200a2338, 0x0, 0xc4200001a0, 0x0, 0x0, 0x7fffffff, 0x4076528, 0x7fff5fbff518, 0x0, ...)
        /Users/crawshaw/go/src/runtime/traceback.go:378 +0x10a0
runtime.copystack(0xc4200001a0, 0x4000, 0x4510001)
        /Users/crawshaw/go/src/runtime/stack.go:932 +0x372
runtime.newstack(0x0)
        /Users/crawshaw/go/src/runtime/stack.go:1098 +0x34d
runtime.morestack()
        /Users/crawshaw/go/src/runtime/asm_amd64.s:385 +0x86

goroutine 1 [copystack]:
runtime.heapBitsSetType(0xc420010170, 0x10, 0x10, 0x5865820)
        /Users/crawshaw/go/src/runtime/mbitmap.go:893 +0x66b fp=0xc4200a2340 sp=0xc4200a2338
runtime.mallocgc(0x10, 0x5865820, 0x1, 0xc42000a500)
        /Users/crawshaw/go/src/runtime/malloc.go:704 +0x616 fp=0xc4200a23e0 sp=0xc4200a2340
runtime.newobject(0x5865820, 0xc42000a500)
        /Users/crawshaw/go/src/runtime/malloc.go:801 +0x38 fp=0xc4200a2410 sp=0xc4200a23e0
strings.NewReplacer(0x0, 0x0, 0x0, 0x5852486)
        /Users/crawshaw/go/src/strings/replace.go:54 +0x1bd fp=0xc4200a3c98 sp=0xc4200a2410
junk2.init.1()
        /Users/crawshaw/Dropbox/src/junk2/junk2.go:6 +0x3c fp=0xc4200a3cc8 sp=0xc4200a3c98
invalid spdelta e.mheap; runtime.s *runtime.mspan; runtime.acct int32 } 0x4050af0 0x5852751 0x2743c -1
e.mheap; runtime.s *runtime.mspan; runtime.acct int32 }(0x4053b4300, 0x460029000, 0x460029000, 0xc42000e01000, 0x585270000, 0x500, 0xc42004fda000, 0xa00, 0x0, 0x0, ...)
        ?:0 +0x1801c61 fp=0xc4200a3ccf sp=0xc4200a3cc8

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
        /Users/crawshaw/go/src/runtime/asm_amd64.s:2184 +0x1

The busted function name suggests another relocation has gone wrong. I'm wondering about setaddrplus in pcln.go. How does it work? Maybe it doesn't?

gopherbot pushed a commit that referenced this issue Dec 10, 2016
The pclntable contains pointers to functions. If the function symbol
is exported in a plugin, and there is a matching symbol in the host
binary, then the pclntable of a plugin ends up pointing at the
function in the host module.

This doesn't work because the traceback code expects the pointer to
be in the same module space as the PC value.

So don't export functions that might overlap with the host binary.
This way the pointer stays in its module.

Updates #18190

Change-Id: Ifb77605b35fb0a1e7edeecfd22b1e335ed4bb392
Reviewed-on: https://go-review.googlesource.com/34196
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
@crawshaw
Copy link
Member

Found the problem. After applying CL 34196, there are still a handful of ftab entries in the plugin pclntable whose entry relocations resolve to the version in the host binary. Specifically, they are the exported C symbols defined in runtime and runtime/cgo: crosscall2, _cgo_panic, etc.

These cause a problem when tracing back through the stack because the linear search logic in runtime.findfunc gets caught on them:

// linear search to find func with pc >= entry.
for datap.ftab[idx+1].entry <= pc {
        idx++
}

The right solution is to not include the runtime in the plugin (#17150). A smaller, potentially-not-incorrect solution is to avoid exporting C symbols from plugins. I'm going to see if that breaks anything.

@gopherbot
Copy link

CL https://golang.org/cl/34199 mentions this issue.

@wendigo
Copy link

wendigo commented Dec 10, 2016

@crawshaw after pulling https://golang.org/cl/34199 I no longer got invalid spdelta but now I can see another error:

runtime: bad pointer in frame plugin.open at 0xc420103600: 0x3b
fatal error: invalid pointer found on stack

should I report it in separate issue?

@crawshaw
Copy link
Member

This one is fine. What program are you running?

@wendigo
Copy link

wendigo commented Dec 10, 2016

I'm running tests in go-bind-plugin (https://github.com/wendigo/go-bind-plugin) under macOS:

go get github.com/wendigo/go-bind-plugin
cd $GOPATH/src/github.com/wendigo/go-bind-plugin
go test -v ./cli

ends with (when pulled https://golang.org/cl/34199):

runtime: bad pointer in frame plugin.open at 0xc420103600: 0x3b
fatal error: invalid pointer found on stack

runtime stack:
runtime.throw(0x62c6f1d, 0x1e)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/panic.go:596 +0x95 fp=0x7fff5fbfee60 sp=0x7fff5fbfee40
runtime.adjustpointers(0xc4201035e0, 0x7fff5fbfef50, 0x7fff5fbff298, 0x42ce9c0)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/stack.go:609 +0x26a fp=0x7fff5fbfeec8 sp=0x7fff5fbfee60
runtime.adjustframe(0x7fff5fbff198, 0x7fff5fbff298, 0x7fff5fbff001)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/stack.go:681 +0x5c1 fp=0x7fff5fbfef70 sp=0x7fff5fbfeec8
runtime.gentraceback(0xffffffffffffffff, 0xc420103418, 0x0, 0xc4200c0340, 0x0, 0x0, 0x7fffffff, 0x62cebe0, 0x7fff5fbff298, 0x0, ...)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/traceback.go:378 +0x10a0 fp=0x7fff5fbff1f8 sp=0x7fff5fbfef70
runtime.copystack(0xc4200c0340, 0x4000, 0x4478001)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/stack.go:932 +0x372 fp=0x7fff5fbff3d0 sp=0x7fff5fbff1f8
runtime.newstack(0x0)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/stack.go:1098 +0x34d fp=0x7fff5fbff548 sp=0x7fff5fbff3d0
runtime.morestack()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/asm_amd64.s:385 +0x86 fp=0x7fff5fbff558 sp=0x7fff5fbff548

goroutine 5 [copystack]:
strings.NewReplacer(0xc4200135c0, 0x4, 0x4, 0x0)
	/Users/mateusz.gajewski/Desktop/go/src/strings/replace.go:23 +0x5bd fp=0xc420103420 sp=0xc420103418
mime/multipart.init()
	/Users/mateusz.gajewski/Desktop/go/src/mime/multipart/writer.go:121 +0x13c fp=0xc420103460 sp=0xc420103420
net/http.init()
	/Users/mateusz.gajewski/Desktop/go/src/net/http/transport.go:2184 +0x9e fp=0xc4201034e0 sp=0xc420103460
github.com/wendigo/go-bind-plugin/cli/internal/test_fixtures/complex_plugin.init()
	github.com/wendigo/go-bind-plugin/cli/internal/test_fixtures/complex_plugin/_obj/_cgo_import.go:4 +0x56 fp=0xc4201034f0 sp=0xc4201034e0
plugin.open(0xc420013240, 0x3b, 0x0, 0x0, 0x0)
	/Users/mateusz.gajewski/Desktop/go/src/plugin/plugin_dlopen.go:96 +0x933 fp=0xc420103730 sp=0xc4201034f0
plugin.Open(0xc420013240, 0x3b, 0x0, 0x0, 0x35b35a5040adc38)
	/Users/mateusz.gajewski/Desktop/go/src/plugin/plugin.go:30 +0x35 fp=0xc420103768 sp=0xc420103730
github.com/wendigo/go-bind-plugin/cli.loadPlugin(0xc420013240, 0x3b, 0xc42008e980, 0x8, 0x8, 0x1, 0x0, 0x4)
	/Users/mateusz.gajewski/Desktop/Projects/src/github.com/wendigo/go-bind-plugin/cli/plugin.go:35 +0x4d fp=0xc420103830 sp=0xc420103768
github.com/wendigo/go-bind-plugin/cli.(*Cli).GenerateFile(0xc4200be380, 0x3b, 0xc42001cb40)
	/Users/mateusz.gajewski/Desktop/Projects/src/github.com/wendigo/go-bind-plugin/cli/cli.go:137 +0x306 fp=0xc420103ab8 sp=0xc420103830
github.com/wendigo/go-bind-plugin/cli_test.generatePluginWithCli(0xc420013240, 0x3b, 0xc42001cb40, 0x27, 0xc420013200, 0x3b, 0x41fa797, 0xb, 0x1010101, 0x41f919c, ...)
	/Users/mateusz.gajewski/Desktop/Projects/src/github.com/wendigo/go-bind-plugin/cli/cli_test.go:90 +0xf3 fp=0xc420103b58 sp=0xc420103ab8
github.com/wendigo/go-bind-plugin/cli_test.TestWillGenerateComplexPluginWithoutErrors(0xc420074820)
	/Users/mateusz.gajewski/Desktop/Projects/src/github.com/wendigo/go-bind-plugin/cli/cli_test.go:63 +0x578 fp=0xc420103fa8 sp=0xc420103b58
testing.tRunner(0xc420074820, 0x4206570)
	/Users/mateusz.gajewski/Desktop/go/src/testing/testing.go:656 +0x93 fp=0xc420103fd0 sp=0xc420103fa8
runtime.goexit()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/asm_amd64.s:2184 +0x1 fp=0xc420103fd8 sp=0xc420103fd0
created by testing.(*T).Run
	/Users/mateusz.gajewski/Desktop/go/src/testing/testing.go:693 +0x2c4

goroutine 1 [chan receive]:
runtime.gopark(0x4206a98, 0xc420018ad8, 0x41fac35, 0xc, 0xc420013117, 0x3)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:261 +0x13a fp=0xc420051b20 sp=0xc420051af0
runtime.goparkunlock(0xc420018ad8, 0x41fac35, 0xc, 0x17, 0x3)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:267 +0x5e fp=0xc420051b60 sp=0xc420051b20
runtime.chanrecv(0x41b2e40, 0xc420018a80, 0x0, 0xc420051c01, 0x40db214)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/chan.go:513 +0x371 fp=0xc420051c00 sp=0xc420051b60
runtime.chanrecv1(0x41b2e40, 0xc420018a80, 0x0)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/chan.go:395 +0x35 fp=0xc420051c38 sp=0xc420051c00
testing.(*T).Run(0xc420074750, 0x4202885, 0x2a, 0x4206570, 0x4058ae5)
	/Users/mateusz.gajewski/Desktop/go/src/testing/testing.go:694 +0x2ee fp=0xc420051ce0 sp=0xc420051c38
testing.runTests.func1(0xc420074750)
	/Users/mateusz.gajewski/Desktop/go/src/testing/testing.go:877 +0x67 fp=0xc420051d30 sp=0xc420051ce0
testing.tRunner(0xc420074750, 0xc420051de0)
	/Users/mateusz.gajewski/Desktop/go/src/testing/testing.go:656 +0x93 fp=0xc420051d58 sp=0xc420051d30
testing.runTests(0xc42000cba0, 0x42f85f0, 0x1, 0x1, 0x30)
	/Users/mateusz.gajewski/Desktop/go/src/testing/testing.go:883 +0x29d fp=0xc420051e10 sp=0xc420051d58
testing.(*M).Run(0xc420051f20, 0xc420051f20)
	/Users/mateusz.gajewski/Desktop/go/src/testing/testing.go:818 +0xfc fp=0xc420051f00 sp=0xc420051e10
main.main()
	github.com/wendigo/go-bind-plugin/cli/_test/_testmain.go:44 +0xf7 fp=0xc420051f88 sp=0xc420051f00
runtime.main()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:185 +0x20a fp=0xc420051fe0 sp=0xc420051f88
runtime.goexit()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/asm_amd64.s:2184 +0x1 fp=0xc420051fe8 sp=0xc420051fe0

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/asm_amd64.s:2184 +0x1 fp=0xc420040fe8 sp=0xc420040fe0

goroutine 2 [force gc (idle)]:
runtime.gopark(0x4206a98, 0x42fec60, 0x41fb6ec, 0xf, 0x4206914, 0x1)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:261 +0x13a fp=0xc42002e768 sp=0xc42002e738
runtime.goparkunlock(0x42fec60, 0x41fb6ec, 0xf, 0xc420000114, 0x1)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:267 +0x5e fp=0xc42002e7a8 sp=0xc42002e768
runtime.forcegchelper()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:226 +0x9e fp=0xc42002e7e0 sp=0xc42002e7a8
runtime.goexit()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/asm_amd64.s:2184 +0x1 fp=0xc42002e7e8 sp=0xc42002e7e0
created by runtime.init.4
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:215 +0x35

goroutine 3 [GC sweep wait]:
runtime.gopark(0x4206a98, 0x42fee20, 0x41faed8, 0xd, 0x401e814, 0x1)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:261 +0x13a fp=0xc42002ef58 sp=0xc42002ef28
runtime.goparkunlock(0x42fee20, 0x41faed8, 0xd, 0x14, 0x1)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:267 +0x5e fp=0xc42002ef98 sp=0xc42002ef58
runtime.bgsweep(0xc42005c000)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/mgcsweep.go:56 +0xb6 fp=0xc42002efd8 sp=0xc42002ef98
runtime.goexit()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/asm_amd64.s:2184 +0x1 fp=0xc42002efe0 sp=0xc42002efd8
created by runtime.gcenable
	/Users/mateusz.gajewski/Desktop/go/src/runtime/mgc.go:209 +0x61

goroutine 4 [finalizer wait]:
runtime.gopark(0x4206a98, 0x43194d8, 0x41fb342, 0xe, 0x14, 0x1)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:261 +0x13a fp=0xc42002f718 sp=0xc42002f6e8
runtime.goparkunlock(0x43194d8, 0x41fb342, 0xe, 0x14, 0x1)
	/Users/mateusz.gajewski/Desktop/go/src/runtime/proc.go:267 +0x5e fp=0xc42002f758 sp=0xc42002f718
runtime.runfinq()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/mfinal.go:161 +0xb2 fp=0xc42002f7e0 sp=0xc42002f758
runtime.goexit()
	/Users/mateusz.gajewski/Desktop/go/src/runtime/asm_amd64.s:2184 +0x1 fp=0xc42002f7e8 sp=0xc42002f7e0
created by runtime.createfing
	/Users/mateusz.gajewski/Desktop/go/src/runtime/mfinal.go:142 +0x62
exit status 2
FAIL	github.com/wendigo/go-bind-plugin/cli	8.421s

Current tip without CL 34199 passes both on linux and macOS: https://travis-ci.org/wendigo/go-bind-plugin

@crawshaw
Copy link
Member

That's a lot of code. Can you extract a smaller reproduction?

@wendigo
Copy link

wendigo commented Dec 10, 2016

Unfortunately it fails when running the tests - I can't extract smaller example.

It won't fail either when net/http package is not referenced in cli/internal/test_fixtures/complex_plugin/plugin.go: you can check the previous version with git show 5c928eeb105ea4cf5130b2b98f52cbdfa93ab715

@crawshaw
Copy link
Member

Lots of meddling with the debugging in plugin.open eventually let me replicate the failure in a simpler plugin.

With stackDebug = 4, it very much looks like a non-pointer value in a stack pointer slot during copystack. The stack map itself looks reasonable. It very suspiciously always happens in plugin.open, the first function in the stack frame that comes from the other (host) module. No clear idea yet what's happening. But now the plugin module does pass moduledataverify, which is nice.

@crawshaw
Copy link
Member

It looks like the stack map is wrong. What it thinks is a pointer slot is not one.

I assume this has something to do with two copies of the runtime existing simultaneously. I spent a while trying to work out which part, to no luck. I looked into removing the runtime from the plugin on macOS, but this requires dynamic relocations to several runtime functions and values (like duffcopy and algarray) which the linker does not know how to generate.

So as I'm not going to teach the linker how mach-o relocations are encoded by 1.8, I'll remove darwin support for plugins.

@wendigo
Copy link

wendigo commented Dec 14, 2016

Sad to read that @crawshaw

gopherbot pushed a commit that referenced this issue Dec 14, 2016
Explicitly filter any C-only cgo functions out of pclntable,
which allows them to be duplicated with the host binary.

Updates #18190.

Change-Id: I50d8706777a6133b3e95f696bc0bc586b84faa9e
Reviewed-on: https://go-review.googlesource.com/34199
Reviewed-by: Ian Lance Taylor <iant@golang.org>
@gopherbot
Copy link

CL https://golang.org/cl/34391 mentions this issue.

gopherbot pushed a commit that referenced this issue Dec 15, 2016
We are seeing a bad stack map in #18190. In a copystack, it is
mistaking a slot for a pointer.

Presumably this is caused either by our fledgling dynlink support on
darwin, or a consequence of having two copies of the runtime in the
process. But I have been unable to work out which in the 1.8 window,
so pushing darwin support to 1.9 or later.

Change-Id: I7fa4d2dede75033d9a428f24c1837a4613bd2639
Reviewed-on: https://go-review.googlesource.com/34391
Reviewed-by: Ian Lance Taylor <iant@golang.org>
@zoni
Copy link

zoni commented Jan 8, 2017

I just hit this same error with go1.8beta2 but on linux/amd64 rather than darwin:

# plugin.go

package main

func Greet() string {
	return "Hello world"
}
# main.go

package main

import (
	"fmt"
	"plugin"
)

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

	greetSymbol, err := p.Lookup("Greet")
	if err != nil {
		panic(err)
	}

	greet := greetSymbol.(func() string)

	fmt.Println(greet())
}

Building the plugin works fine:

➔  go build -buildmode=plugin plugin.go

➔  ls -l
total 1544
-rw-rw-r-- 1 zoni zoni      60 Jan  8 23:44 plugin.go
-rw-rw-r-- 1 zoni zoni 1576680 Jan  8 23:45 plugin.so

➔  file plugin.so
plugin.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=fc2ec03a4fca7e2b78aa1cb8bd82a9e1c99a0554, not stripped

Attempting to build and run the main program fails with runtime: invalid pc-encoded table and fatal error: invalid runtime symbol table however:

➔  go build main.go && ./main                     
runtime: invalid pc-encoded table f=plugin/unnamed-edc678b706133ad3a92b4a06b80f87d3e85a0e4b.init pc=0x7f23028ad849 targetpc=0x7f23028ad8f0 tab=[0/0]0x0
	value=62 until pc=0x7f23028ad849
fatal error: invalid runtime symbol table

goroutine 1 [running]:
runtime.throw(0x533dc4, 0x1c)
	/usr/local/go/src/runtime/panic.go:596 +0x95 fp=0xc420049750 sp=0xc420049730
runtime.pcvalue(0x7f2302af69b0, 0x22459, 0x7f23028ad8f0, 0xc4200499a8, 0xc420049b01, 0x0)
	/usr/local/go/src/runtime/symtab.go:565 +0x4e5 fp=0xc420049900 sp=0xc420049750
runtime.moduledataverify1(0x7f2302af92c0)
	/usr/local/go/src/runtime/symtab.go:377 +0x42f fp=0xc420049b38 sp=0xc420049900
plugin.lastmoduleinit(0xb38400, 0xc42000e028, 0xb39450, 0x34, 0x7b4f40)
	/usr/local/go/src/runtime/plugin.go:55 +0x315 fp=0xc420049c68 sp=0xc420049b38
plugin.open(0x530b28, 0x9, 0x0, 0x0, 0x0)
	/usr/local/go/src/plugin/plugin_dlopen.go:72 +0x25d fp=0xc420049ea8 sp=0xc420049c68
plugin.Open(0x530b28, 0x9, 0xc420049f78, 0x465b9c, 0xc42006a058)
	/usr/local/go/src/plugin/plugin.go:30 +0x35 fp=0xc420049ee0 sp=0xc420049ea8
main.main()
	/home/zoni/Workspace/go/src/pluginblogpost/main.go:9 +0x48 fp=0xc420049f88 sp=0xc420049ee0
runtime.main()
	/usr/local/go/src/runtime/proc.go:185 +0x20a fp=0xc420049fe0 sp=0xc420049f88
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2184 +0x1 fp=0xc420049fe8 sp=0xc420049fe0

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

@fsenart
Copy link

fsenart commented Jan 8, 2017

@zoni it seems you've forgotten the import "C" in your main.

@zoni
Copy link

zoni commented Jan 8, 2017

Right you are, I did! I completely overlooked the fact that was necessary, importing C indeed resolves it. Apologies for muddling this issue.

@rpoletaev
Copy link

same error

sudo docker run -it --entrypoint bash golang:1.8rc1

env

root@bfaf4f96bda4:/go/src/github.com/rpoletaev/pluginTest# go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/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-build570883191=/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"
go get github.com/rpoletaev/pluginTest
go build -buildmode=plugin -o myplugin.so myplugin.go
root@bfaf4f96bda4:/go/src/github.com/rpoletaev/pluginTest# ls -l
total 1572
-rw-r--r-- 1 root root     292 Jan 18 10:35 main.go
-rw-r--r-- 1 root root      55 Jan 18 10:35 myplugin.go
-rw-r--r-- 1 root root 1599936 Jan 18 10:37 myplugin.so
root@bfaf4f96bda4:/go/src/github.com/rpoletaev/pluginTest# go run main.go
runtime: invalid pc-encoded table f=plugin/unnamed-ecbfa36efed6dd29c5180e0a84c3d8885a6f0bec.init pc=0x7fe7f5f02d79 targetpc=0x7fe7f5f02e20 tab=[0/0]0x0
	value=62 until pc=0x7fe7f5f02d79
fatal error: invalid runtime symbol table

goroutine 1 [running]:
runtime.throw(0x534710, 0x1c)
	/usr/local/go/src/runtime/panic.go:596 +0x95 fp=0xc420047748 sp=0xc420047728
runtime.pcvalue(0x7fe7f614c980, 0x22489, 0x7fe7f5f02e20, 0xc4200479a0, 0xc420047b01, 0x0)
	/usr/local/go/src/runtime/symtab.go:565 +0x4e5 fp=0xc4200478f8 sp=0xc420047748
runtime.moduledataverify1(0x7fe7f614f2c0)
	/usr/local/go/src/runtime/symtab.go:377 +0x42f fp=0xc420047b30 sp=0xc4200478f8
plugin.lastmoduleinit(0x22d2400, 0xc42000e028, 0x22d3450, 0x33, 0x7b52a0)
	/usr/local/go/src/runtime/plugin.go:55 +0x315 fp=0xc420047c60 sp=0xc420047b30
plugin.open(0x531cbc, 0xd, 0x0, 0x0, 0x0)
	/usr/local/go/src/plugin/plugin_dlopen.go:72 +0x25d fp=0xc420047ea0 sp=0xc420047c60
plugin.Open(0x531cbc, 0xd, 0x0, 0xc420047f78, 0x465fbc)
	/usr/local/go/src/plugin/plugin.go:30 +0x35 fp=0xc420047ed8 sp=0xc420047ea0
main.main()
	/go/src/github.com/rpoletaev/pluginTest/main.go:10 +0x48 fp=0xc420047f88 sp=0xc420047ed8
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

main.go

package main

import (
	"C"
	"fmt"
	"plugin"
)

func main() {
	p, err := plugin.Open("./myplugin.so")
	failOnError(err)
	add, err := p.Lookup("Add")
	failOnError(err)
	sum := add.(func(int, int) int)(1, 2)
	fmt.Println(sum)
}

func failOnError(err error) {
	if err != nil {
		panic(err)
	}
}

myplugin.go

package main

func Add(x, y int) int {
    return x+y
}

@crawshaw
Copy link
Member

Those "invalid symbols table" errors in 1.8rc1 on linux were fixed in golang.org/cl/35190, and so they should be fixed by the coming 1.8rc2. If you have the time, please test against HEAD.

I'm keeping this bug open for darwin on 1.9.

@ghost
Copy link

ghost commented May 9, 2017

Anything I can do to help?

@bradfitz bradfitz modified the milestones: Go1.9, Go1.10 Jun 7, 2017
@bradfitz bradfitz added help wanted NeedsFix The path to resolution is known, but the work has not been done. labels Jun 28, 2017
@mccolljr
Copy link
Author

mccolljr commented Jul 11, 2017

I'd like to take a look (not sure how much help I can actually be), but I'm not sure where in the codebase I should start looking. If anyone can give me a general idea as to where the code that handles this lives, that would be awesome.

edit
By 'handles this', I mean the code that handles relocations for darwin. I should have been more clear.

@hirochachacha
Copy link
Contributor

I just finished investigation. I'm going to send CLs. Thanks.

@gopherbot
Copy link

Change https://golang.org/cl/59370 mentions this issue: cmd/link: refactor some code

@gopherbot
Copy link

Change https://golang.org/cl/59372 mentions this issue: cmd/go: re-enable buildmode=plugin on darwin/amd64

@gopherbot
Copy link

Change https://golang.org/cl/59373 mentions this issue: cmd/link: fix warning for buildmode=plugin

@gopherbot
Copy link

Change https://golang.org/cl/59417 mentions this issue: cmd/link: refactor addlib

gopherbot pushed a commit that referenced this issue Aug 29, 2017
Without this CL, the system linker complains about absolute addressing
in type..eqfunc.*.

Updates #18190

Change-Id: I68db37a7f4c96b16a9c13baffc0f043a3048df6d
Reviewed-on: https://go-review.googlesource.com/59373
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
gopherbot pushed a commit that referenced this issue Aug 29, 2017
* extract pkgname() and findlib() from the function for #18190.
* rename const pkgname to const pkgdef to avoid confliction.

Change-Id: Ie62509bfbddcf19cf92b5b12b598679a069e6e74
Reviewed-on: https://go-review.googlesource.com/59417
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
@hirochachacha
Copy link
Contributor

x.c

#include <stdio.h>

void foo(void) {
	puts("x");
}

void x() {
	foo();
}

y.c

#include <stdio.h>

void foo(void) {
	puts("y");
}

void y() {
	foo();
}

c.c

#include <stdio.h>

void x(void);
void y(void);

int main(void) {
	x();
	y();
	return 0;
}
$ cc -dynamiclib -o x.so x.c
$ cc -dynamiclib -o y.so y.c
$ cc c.c x.so y.so
$ ./a.out
x
y
$ cc -dynamiclib -o x.so x.c -flat_namespace
$ cc -dynamiclib -o y.so y.c -flat_namespace
$ cc c.c x.so y.so
$ ./a.out
x
x

I thought I was misunderstanding. At least, this is the root cause of pclntab.
I tried to remove runtime duplication without thinking.
I'll rethink about the problem from scratch.

@hirochachacha
Copy link
Contributor

plugins allows to update data which don't exist at compile time.
So the above comment is wrong. We need to use flat namespace.

@gopherbot
Copy link

Change https://golang.org/cl/61091 mentions this issue: cmd/go, cmd/link, cmd/dist: re-enable plugin mode on darwin/amd64

@golang golang locked and limited conversation to collaborators Sep 10, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge help wanted NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests