1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // +build linux,cgo darwin,cgo
10 #cgo linux LDFLAGS: -ldl
18 static uintptr_t pluginOpen(const char* path, char** err) {
19 void* h = dlopen(path, RTLD_NOW|RTLD_GLOBAL);
21 *err = (char*)dlerror();
26 static void* pluginLookup(uintptr_t h, const char* name, char** err) {
27 void* r = dlsym((void*)h, name);
29 *err = (char*)dlerror();
42 // avoid a dependency on strings
43 func lastIndexByte(s
string, c
byte) int {
44 for i
:= len(s
) - 1; i
>= 0; i
-- {
52 func open(name
string) (*Plugin
, error
) {
53 cPath
:= make([]byte, C
.PATH_MAX
+1)
54 cRelName
:= make([]byte, len(name
)+1)
57 (*C
.char
)(unsafe
.Pointer(&cRelName
[0])),
58 (*C
.char
)(unsafe
.Pointer(&cPath
[0]))) == nil {
59 return nil, errors
.New(`plugin.Open("` + name
+ `"): realpath failed`)
62 filepath
:= C
.GoString((*C
.char
)(unsafe
.Pointer(&cPath
[0])))
65 if p
:= plugins
[filepath
]; p
!= nil {
68 return nil, errors
.New(`plugin.Open("` + name
+ `"): ` + p
.err
+ ` (previous failure)`)
74 h
:= C
.pluginOpen((*C
.char
)(unsafe
.Pointer(&cPath
[0])), &cErr
)
77 return nil, errors
.New(`plugin.Open("` + name
+ `"): ` + C
.GoString(cErr
))
79 // TODO(crawshaw): look for plugin note, confirm it is a Go plugin
80 // and it was built with the correct toolchain.
81 if len(name
) > 3 && name
[len(name
)-3:] == ".so" {
82 name
= name
[:len(name
)-3]
85 plugins
= make(map[string]*Plugin
)
87 pluginpath
, syms
, errstr
:= lastmoduleinit()
89 plugins
[filepath
] = &Plugin
{
90 pluginpath
: pluginpath
,
94 return nil, errors
.New(`plugin.Open("` + name
+ `"): ` + errstr
)
96 // This function can be called from the init function of a plugin.
97 // Drop a placeholder in the map so subsequent opens can wait on it.
99 pluginpath
: pluginpath
,
100 loaded
: make(chan struct{}),
102 plugins
[filepath
] = p
105 initStr
:= make([]byte, len(pluginpath
)+6)
106 copy(initStr
, pluginpath
)
107 copy(initStr
[len(pluginpath
):], ".init")
109 initFuncPC
:= C
.pluginLookup(h
, (*C
.char
)(unsafe
.Pointer(&initStr
[0])), &cErr
)
110 if initFuncPC
!= nil {
111 initFuncP
:= &initFuncPC
112 initFunc
:= *(*func())(unsafe
.Pointer(&initFuncP
))
116 // Fill out the value of each plugin symbol.
117 updatedSyms
:= map[string]interface{}{}
118 for symName
, sym
:= range syms
{
119 isFunc
:= symName
[0] == '.'
121 delete(syms
, symName
)
122 symName
= symName
[1:]
125 fullName
:= pluginpath
+ "." + symName
126 cname
:= make([]byte, len(fullName
)+1)
127 copy(cname
, fullName
)
129 p
:= C
.pluginLookup(h
, (*C
.char
)(unsafe
.Pointer(&cname
[0])), &cErr
)
131 return nil, errors
.New(`plugin.Open("` + name
+ `"): could not find symbol ` + symName
+ `: ` + C
.GoString(cErr
))
133 valp
:= (*[2]unsafe
.Pointer
)(unsafe
.Pointer(&sym
))
135 (*valp
)[1] = unsafe
.Pointer(&p
)
139 // we can't add to syms during iteration as we'll end up processing
140 // some symbols twice with the inability to tell if the symbol is a function
141 updatedSyms
[symName
] = sym
149 func lookup(p
*Plugin
, symName
string) (Symbol
, error
) {
150 if s
:= p
.syms
[symName
]; s
!= nil {
153 return nil, errors
.New("plugin: symbol " + symName
+ " not found in plugin " + p
.pluginpath
)
158 plugins
map[string]*Plugin
161 // lastmoduleinit is defined in package runtime
162 func lastmoduleinit() (pluginpath
string, syms
map[string]interface{}, errstr
string)