This repo contains some examples of alternatives to the plugin package in Go's standard library, inspired by https://github.com/vladimirvivien/go-cshared-examples.
- Run the demo via:
go build -o build/c-shared-plug.so -buildmode=c-shared ./plugins/c-shared-plug/main.go
go run ./cmd/go-dlopen/
package main
// #cgo CFLAGS: -g -Wall
// #include "main.h"
// #include <stdlib.h>
import "C"
import (
"fmt"
"math/rand"
"unsafe"
"github.com/e-nikolov/scratch/pkg/shared"
)
func main() {
fmt.Printf("Main Go - %q\n", shared.GlobalVariable)
LoadPlugin("./build/c-shared-plug.so")
a, b := rand.Int()%100, rand.Int()%100
fmt.Printf("%d + %d = %d\n", a, b, Add(a, b))
c := rand.Float64()
fmt.Printf("cos(%.5f) = %.5f\n", c, Cosine(c))
vals := rand.Perm(10)
fmt.Println(vals)
Sort(vals)
fmt.Println(vals)
Log("Hello from Go")
fmt.Printf("Main Go - %q\n", shared.GlobalVariable)
fmt.Printf("\n%v\n", StartHTTPServer(":8080", "Hello from the main Go"))
}
func LoadPlugin(path string) {
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
C.LoadPlugin(C.go_str{cpath, C.longlong(len(path))})
}
func Add(a, b int) int {
return int(C.Add(C.longlong(a), C.longlong(b)))
}
func Cosine(x float64) float64 {
return float64(C.Cosine(C.double(x)))
}
func Sort(vals []int) {
C.Sort(C.go_slice{unsafe.Pointer(&vals[0]), C.longlong(len(vals)), C.longlong(cap(vals))})
}
func Log(msg string) int {
cs := C.CString(msg)
defer C.free(unsafe.Pointer(cs))
return int(C.Log(C.go_str{cs, C.longlong(len(msg))}))
}
func StartHTTPServer(addr string, message string) error {
caddr := C.CString(addr)
defer C.free(unsafe.Pointer(caddr))
cmsg := C.CString(message)
defer C.free(unsafe.Pointer(cmsg))
e := C.StartHTTPServer(caddr, cmsg)
er := C.GoString(e)
// defer C.free(unsafe.Pointer(e.p))
if er != "" {
return fmt.Errorf(er)
}
return nil
}
package main
import "C"
import (
"fmt"
"math"
"net/http"
"sort"
"sync"
"github.com/e-nikolov/scratch/pkg/shared"
)
var count int
var mtx sync.Mutex
func init() {
shared.GlobalVariable = "Does init() get called?"
}
//export Add
func Add(a, b int) int {
fmt.Printf("Go Plugin - %q\n", shared.GlobalVariable)
shared.GlobalVariable = "Value Modified By a Plugin"
fmt.Printf("Go Plugin - %q\n", shared.GlobalVariable)
return a + b
}
//export Cosine
func Cosine(x float64) float64 {
return math.Cos(x)
}
//export Sort
func Sort(vals []int) {
sort.Ints(vals)
}
//export Log
func Log(msg string) int {
mtx.Lock()
defer mtx.Unlock()
fmt.Println(msg + "; from Go")
count++
return count
}
//export StartHTTPServer
func StartHTTPServer(cAddr *C.char, cMessage *C.char) (err *C.char) {
addr := C.GoString(cAddr)
message := C.GoString(cMessage)
fmt.Printf("\nStarting HTTP Server at %q\n", addr)
e := http.ListenAndServe(addr, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "HTTP Server Demo Inside a Go plugin\n\nMesage from the main program: %q\nPath: %q\n", message, r.URL.Path)
}))
if e != nil {
return C.CString(e.Error())
}
return nil
}
func main() {}
- Plugin Loader in C
#include "main.h"
#include <stdio.h>
#include <dlfcn.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
go_int (*add)(go_int, go_int);
go_int Add(go_int a, go_int b) {
return add(a, b);
}
go_float64 (*cosine)(go_float64);
go_float64 Cosine(go_float64 x) {
return cosine(x);
}
void (*sort)(go_slice);
void Sort(go_slice vals) {
sort(vals);
}
go_int (*logg)(go_str);
go_int Log(go_str msg) {
char *c_msg = malloc(msg.len + 10);
memcpy(c_msg, msg.p, msg.len);
memcpy(c_msg + msg.len, "; from C", 8);
go_str c_msg_str = {c_msg, msg.len + 8};
go_int ret = logg(c_msg_str);
free(c_msg);
return ret;
}
char* (*startHTTPServer)(char* addr, char* message);
char* StartHTTPServer(char* addr, char* message) {
return startHTTPServer(addr, message);
}
int LoadPlugin(go_str path) {
void *handle;
char *error;
// use dlopen to load shared object
handle = dlopen(path.p, RTLD_LAZY);
if (!handle) {
fputs (dlerror(), stderr);
exit(1);
}
// resolve Add symbol and assign to fn ptr
add = dlsym(handle, "Add");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
// resolve Cosine symbol
cosine = dlsym(handle, "Cosine");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
// resolve Sort symbol
sort = dlsym(handle, "Sort");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
// resolve Log symbol
logg = dlsym(handle, "Log");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
// resolve StartHTTPServer symbol
startHTTPServer = dlsym(handle, "StartHTTPServer");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
// close file handle when done
dlclose(handle);
return 0;
}