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 //go:build (linux && cgo) || (darwin && cgo) || (freebsd && cgo) || (hurd & 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 func open(name
string) (*Plugin
, error
) {
43 cPath
:= make([]byte, C
.PATH_MAX
+1)
44 cRelName
:= make([]byte, len(name
)+1)
47 (*C
.char
)(unsafe
.Pointer(&cRelName
[0])),
48 (*C
.char
)(unsafe
.Pointer(&cPath
[0]))) == nil {
49 return nil, errors
.New(`plugin.Open("` + name
+ `"): realpath failed`)
52 filepath
:= C
.GoString((*C
.char
)(unsafe
.Pointer(&cPath
[0])))
55 if p
:= plugins
[filepath
]; p
!= nil {
58 return nil, errors
.New(`plugin.Open("` + name
+ `"): ` + p
.err
+ ` (previous failure)`)
64 h
:= C
.pluginOpen((*C
.char
)(unsafe
.Pointer(&cPath
[0])), &cErr
)
67 return nil, errors
.New(`plugin.Open("` + name
+ `"): ` + C
.GoString(cErr
))
69 // TODO(crawshaw): look for plugin note, confirm it is a Go plugin
70 // and it was built with the correct toolchain.
71 if len(name
) > 3 && name
[len(name
)-3:] == ".so" {
72 name
= name
[:len(name
)-3]
75 plugins
= make(map[string]*Plugin
)
77 pluginpath
, syms
, errstr
:= lastmoduleinit()
79 plugins
[filepath
] = &Plugin
{
80 pluginpath
: pluginpath
,
84 return nil, errors
.New(`plugin.Open("` + name
+ `"): ` + errstr
)
86 // This function can be called from the init function of a plugin.
87 // Drop a placeholder in the map so subsequent opens can wait on it.
89 pluginpath
: pluginpath
,
90 loaded
: make(chan struct{}),
95 initStr
:= make([]byte, len(pluginpath
)+len("..inittask")+1) // +1 for terminating NUL
96 copy(initStr
, pluginpath
)
97 copy(initStr
[len(pluginpath
):], "..inittask")
99 initTask
:= C
.pluginLookup(h
, (*C
.char
)(unsafe
.Pointer(&initStr
[0])), &cErr
)
104 // Fill out the value of each plugin symbol.
105 updatedSyms
:= map[string]any
{}
106 for symName
, sym
:= range syms
{
107 isFunc
:= symName
[0] == '.'
109 delete(syms
, symName
)
110 symName
= symName
[1:]
113 fullName
:= pluginpath
+ "." + symName
114 cname
:= make([]byte, len(fullName
)+1)
115 copy(cname
, fullName
)
117 p
:= C
.pluginLookup(h
, (*C
.char
)(unsafe
.Pointer(&cname
[0])), &cErr
)
119 return nil, errors
.New(`plugin.Open("` + name
+ `"): could not find symbol ` + symName
+ `: ` + C
.GoString(cErr
))
121 valp
:= (*[2]unsafe
.Pointer
)(unsafe
.Pointer(&sym
))
123 (*valp
)[1] = unsafe
.Pointer(&p
)
127 // we can't add to syms during iteration as we'll end up processing
128 // some symbols twice with the inability to tell if the symbol is a function
129 updatedSyms
[symName
] = sym
137 func lookup(p
*Plugin
, symName
string) (Symbol
, error
) {
138 if s
:= p
.syms
[symName
]; s
!= nil {
141 return nil, errors
.New("plugin: symbol " + symName
+ " not found in plugin " + p
.pluginpath
)
146 plugins
map[string]*Plugin
149 // lastmoduleinit is defined in package runtime
150 func lastmoduleinit() (pluginpath
string, syms
map[string]any
, errstr
string)
152 // doInit is defined in package runtime
153 //go:linkname doInit runtime.doInit
154 func doInit(t unsafe
.Pointer
) // t should be a *runtime.initTask