Skip to content

Instantly share code, notes, and snippets.

@amlwwalker
Created May 29, 2024 12:53
Show Gist options
  • Save amlwwalker/028a15cce5bd2375d25b9feb2c59c85c to your computer and use it in GitHub Desktop.
Save amlwwalker/028a15cce5bd2375d25b9feb2c59c85c to your computer and use it in GitHub Desktop.
attempt at c-shared go plugins
package main
/*
#cgo LDFLAGS: -ldl
#include <dlfcn.h>
#include <stdlib.h>
typedef void (*initializeFunc)();
typedef void (*handleMessageFunc)(const char* message, char** response);
void* loadSO(const char* path) {
return dlopen(path, RTLD_LAZY);
}
initializeFunc getInitializeFunc(void* handle) {
return (initializeFunc)dlsym(handle, "Initialize");
}
handleMessageFunc getHandleMessageFunc(void* handle) {
return (handleMessageFunc)dlsym(handle, "HandleMessage");
}
void handle_message(handleMessageFunc fn, const char* message, char** response) {
fn(message, response);
}
*/
import "C"
import (
"fmt"
"unsafe"
)
type Plugin struct {
handle unsafe.Pointer
initializeFn unsafe.Pointer
handleMessageFn unsafe.Pointer
}
func LoadPlugin(path string) (*Plugin, error) {
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
handle := C.loadSO(cpath)
if handle == nil {
return nil, fmt.Errorf("failed to load plugin: %s", path)
}
initializeFn := unsafe.Pointer(C.getInitializeFunc(handle))
if initializeFn == nil {
return nil, fmt.Errorf("failed to find Initialize function in plugin: %s", path)
}
handleMessageFn := unsafe.Pointer(C.getHandleMessageFunc(handle))
if handleMessageFn == nil {
return nil, fmt.Errorf("failed to find HandleMessage function in plugin: %s", path)
}
return &Plugin{handle: handle, initializeFn: initializeFn, handleMessageFn: handleMessageFn}, nil
}
func (p *Plugin) Initialize() {
initializeFn := *(*func())(unsafe.Pointer(&p.initializeFn))
initializeFn()
}
func (p *Plugin) SendMessage(message []byte) ([]byte, error) {
cmessage := C.CString(string(message))
defer C.free(unsafe.Pointer(cmessage))
var cresponse *C.char
handleMessageFn := *(*func(*C.char, **C.char))(unsafe.Pointer(&p.handleMessageFn))
handleMessageFn(cmessage, &cresponse)
defer C.free(unsafe.Pointer(cresponse))
response := C.GoString(cresponse)
return []byte(response), nil
}
package main
import (
"fmt"
)
func main() {
plugin, err := LoadPlugin("./examples/example/plugin.so")
if err != nil {
panic(err)
}
plugin.Initialize()
message := []byte("Hello Plugin")
response, err := plugin.SendMessage(message)
if err != nil {
panic(err)
}
fmt.Printf("Received response: %s\n", string(response))
}
package main
import "C"
import "fmt"
//export Initialize
func Initialize() {
fmt.Println("Plugin initialized")
}
//export HandleMessage
func HandleMessage(message *C.char, response **C.char) {
receivedMessage := C.GoString(message)
fmt.Printf("Received message: %s\n", receivedMessage)
responseMessage := "Response to: " + receivedMessage
*response = C.CString(responseMessage)
}
func main() {}
@amlwwalker
Copy link
Author

I am compiling the plugin with

go build -o plugin.so -buildmode=c-shared plugin.go
and the main application with
go build -o main main.go loader.go

@amlwwalker
Copy link
Author

The crash error

unexpected fault address 0xa9014ff4d100c3ff
fatal error: fault
[signal SIGBUS: bus error code=0x1 addr=0xa9014ff4d100c3ff pc=0xa9014ff4d100c3ff]

goroutine 1 gp=0x140000021c0 m=0 mp=0x1024323c0 [running]:
runtime.throw({0x1023784ed?, 0x0?})
        /usr/local/go/src/runtime/panic.go:1023 +0x40 fp=0x14000060e70 sp=0x14000060e40 pc=0x1023190e0
runtime.sigpanic()
        /usr/local/go/src/runtime/signal_unix.go:878 +0x178 fp=0x14000060ed0 sp=0x14000060e70 pc=0x102330e48
main.main()
        /Users/alexwalker/go/src/github.com/amlwwalker/cgo-plugin/main.go:16 +0x48 fp=0x14000060f40 sp=0x14000060ee0 pc=0x102376da8
runtime.main()
        /usr/local/go/src/runtime/proc.go:271 +0x28c fp=0x14000060fd0 sp=0x14000060f40 pc=0x10231badc
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x14000060fd0 sp=0x14000060fd0 pc=0x10234aa44

goroutine 18 gp=0x14000084380 m=nil [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /usr/local/go/src/runtime/proc.go:402 +0xc8 fp=0x1400004a790 sp=0x1400004a770 pc=0x10231bf08
runtime.goparkunlock(...)
        /usr/local/go/src/runtime/proc.go:408
runtime.forcegchelper()
        /usr/local/go/src/runtime/proc.go:326 +0xb8 fp=0x1400004a7d0 sp=0x1400004a790 pc=0x10231bd98
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x1400004a7d0 sp=0x1400004a7d0 pc=0x10234aa44
created by runtime.init.6 in goroutine 1
        /usr/local/go/src/runtime/proc.go:314 +0x24

goroutine 19 gp=0x14000084540 m=nil [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /usr/local/go/src/runtime/proc.go:402 +0xc8 fp=0x1400004af60 sp=0x1400004af40 pc=0x10231bf08
runtime.goparkunlock(...)
        /usr/local/go/src/runtime/proc.go:408
runtime.bgsweep(0x14000092000)
        /usr/local/go/src/runtime/mgcsweep.go:278 +0xa0 fp=0x1400004afb0 sp=0x1400004af60 pc=0x102308970
runtime.gcenable.gowrap1()
        /usr/local/go/src/runtime/mgc.go:203 +0x28 fp=0x1400004afd0 sp=0x1400004afb0 pc=0x1022fcde8
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x1400004afd0 sp=0x1400004afd0 pc=0x10234aa44
created by runtime.gcenable in goroutine 1
        /usr/local/go/src/runtime/mgc.go:203 +0x6c

goroutine 20 gp=0x14000084700 m=nil [GC scavenge wait]:
runtime.gopark(0x14000092000?, 0x10239a2c0?, 0x1?, 0x0?, 0x14000084700?)
        /usr/local/go/src/runtime/proc.go:402 +0xc8 fp=0x1400004b760 sp=0x1400004b740 pc=0x10231bf08
runtime.goparkunlock(...)
        /usr/local/go/src/runtime/proc.go:408
runtime.(*scavengerState).park(0x102431ce0)
        /usr/local/go/src/runtime/mgcscavenge.go:425 +0x5c fp=0x1400004b790 sp=0x1400004b760 pc=0x10230635c
runtime.bgscavenge(0x14000092000)
        /usr/local/go/src/runtime/mgcscavenge.go:653 +0x44 fp=0x1400004b7b0 sp=0x1400004b790 pc=0x1023068b4
runtime.gcenable.gowrap2()
        /usr/local/go/src/runtime/mgc.go:204 +0x28 fp=0x1400004b7d0 sp=0x1400004b7b0 pc=0x1022fcd88
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x1400004b7d0 sp=0x1400004b7d0 pc=0x10234aa44
created by runtime.gcenable in goroutine 1
        /usr/local/go/src/runtime/mgc.go:204 +0xac

goroutine 34 gp=0x14000104700 m=nil [finalizer wait]:
runtime.gopark(0x0?, 0x0?, 0x2f?, 0x0?, 0x10232c568?)
        /usr/local/go/src/runtime/proc.go:402 +0xc8 fp=0x1400004e580 sp=0x1400004e560 pc=0x10231bf08
runtime.runfinq()
        /usr/local/go/src/runtime/mfinal.go:194 +0x108 fp=0x1400004e7d0 sp=0x1400004e580 pc=0x1022fbeb8
runtime.goexit({})
        /usr/local/go/src/runtime/asm_arm64.s:1222 +0x4 fp=0x1400004e7d0 sp=0x1400004e7d0 pc=0x10234aa44
created by runtime.createfing in goroutine 1
        /usr/local/go/src/runtime/mfinal.go:164 +0x80

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment