mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
plugin: new package for loading plugins
Includes a linux implementation. Change-Id: Iacc2ed7da760ae9deebc928adf2b334b043b07ec Reviewed-on: https://go-review.googlesource.com/27823 Run-TryBot: David Crawshaw <crawshaw@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
c87528d776
commit
0cbb12f0bb
5 changed files with 264 additions and 0 deletions
125
src/plugin/plugin_dlopen.go
Normal file
125
src/plugin/plugin_dlopen.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux,cgo
|
||||
|
||||
package plugin
|
||||
|
||||
/*
|
||||
#cgo linux LDFLAGS: -ldl
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static uintptr_t pluginOpen(const char* path, char** err) {
|
||||
void* h = dlopen(path, RTLD_NOW|RTLD_GLOBAL);
|
||||
if (h == NULL) {
|
||||
*err = dlerror();
|
||||
}
|
||||
return (uintptr_t)h;
|
||||
}
|
||||
|
||||
static void* pluginLookup(uintptr_t h, const char* name, char** err) {
|
||||
void* r = dlsym((void*)h, name);
|
||||
if (r == NULL) {
|
||||
*err = dlerror();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func open(name string) (*Plugin, error) {
|
||||
pluginsMu.Lock()
|
||||
cRelName := C.CString(name)
|
||||
cPath := C.realpath(cRelName, nil)
|
||||
C.free(unsafe.Pointer(cRelName))
|
||||
defer C.free(unsafe.Pointer(cPath))
|
||||
path := C.GoString(cPath)
|
||||
if p := plugins[path]; p != nil {
|
||||
pluginsMu.Unlock()
|
||||
<-p.loaded
|
||||
return p, nil
|
||||
}
|
||||
var cErr *C.char
|
||||
h := C.pluginOpen(cPath, &cErr)
|
||||
if h == 0 {
|
||||
pluginsMu.Unlock()
|
||||
return nil, errors.New("plugin.Open: " + C.GoString(cErr))
|
||||
}
|
||||
// TODO(crawshaw): look for plugin note, confirm it is a Go plugin
|
||||
// and it was built with the correct toolchain.
|
||||
// TODO(crawshaw): get full plugin name from note.
|
||||
if len(name) > 3 && name[len(name)-3:] == ".so" {
|
||||
name = name[:len(name)-3]
|
||||
}
|
||||
syms := lastmoduleinit()
|
||||
if plugins == nil {
|
||||
plugins = make(map[string]*Plugin)
|
||||
}
|
||||
// This function can be called from the init function of a plugin.
|
||||
// Drop a placeholder in the map so subsequent opens can wait on it.
|
||||
p := &Plugin{
|
||||
name: name,
|
||||
loaded: make(chan struct{}),
|
||||
syms: syms,
|
||||
}
|
||||
plugins[path] = p
|
||||
pluginsMu.Unlock()
|
||||
|
||||
initStr := C.CString(name + ".init")
|
||||
initFuncPC := C.pluginLookup(h, initStr, &cErr)
|
||||
C.free(unsafe.Pointer(initStr))
|
||||
if initFuncPC != nil {
|
||||
initFuncP := &initFuncPC
|
||||
initFunc := *(*func())(unsafe.Pointer(&initFuncP))
|
||||
initFunc()
|
||||
}
|
||||
|
||||
// Fill out the value of each plugin symbol.
|
||||
for symName, sym := range syms {
|
||||
isFunc := symName[0] == '.'
|
||||
if isFunc {
|
||||
delete(syms, symName)
|
||||
symName = symName[1:]
|
||||
}
|
||||
|
||||
cname := C.CString(name + "." + symName)
|
||||
p := C.pluginLookup(h, cname, &cErr)
|
||||
C.free(unsafe.Pointer(cname))
|
||||
if p == nil {
|
||||
return nil, errors.New("plugin.Open: could not find symbol " + symName + ": " + C.GoString(cErr))
|
||||
}
|
||||
valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&sym))
|
||||
if isFunc {
|
||||
(*valp)[1] = unsafe.Pointer(&p)
|
||||
} else {
|
||||
(*valp)[1] = p
|
||||
}
|
||||
syms[symName] = sym
|
||||
}
|
||||
close(p.loaded)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func lookup(p *Plugin, symName string) (Symbol, error) {
|
||||
if s := p.syms[symName]; s != nil {
|
||||
return s, nil
|
||||
}
|
||||
return nil, errors.New("plugin: symbol " + symName + " not found in plugin " + p.name)
|
||||
}
|
||||
|
||||
var (
|
||||
pluginsMu sync.Mutex
|
||||
plugins map[string]*Plugin
|
||||
)
|
||||
|
||||
func lastmoduleinit() map[string]interface{} // in package runtime
|
||||
Loading…
Add table
Add a link
Reference in a new issue