github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/src/plugin/plugin_dlopen.go (about) 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. 4 5 // +build linux,cgo darwin,cgo 6 7 package plugin 8 9 /* 10 #cgo linux LDFLAGS: -ldl 11 #include <dlfcn.h> 12 #include <limits.h> 13 #include <stdlib.h> 14 #include <stdint.h> 15 16 #include <stdio.h> 17 18 static uintptr_t pluginOpen(const char* path, char** err) { 19 void* h = dlopen(path, RTLD_NOW|RTLD_GLOBAL); 20 if (h == NULL) { 21 *err = (char*)dlerror(); 22 } 23 return (uintptr_t)h; 24 } 25 26 static void* pluginLookup(uintptr_t h, const char* name, char** err) { 27 void* r = dlsym((void*)h, name); 28 if (r == NULL) { 29 *err = (char*)dlerror(); 30 } 31 return r; 32 } 33 */ 34 import "C" 35 36 import ( 37 "errors" 38 "sync" 39 "unsafe" 40 ) 41 42 // avoid a dependency on strings 43 func lastIndexByte(s string, c byte) int { 44 for i := len(s) - 1; i >= 0; i-- { 45 if s[i] == c { 46 return i 47 } 48 } 49 return -1 50 } 51 52 func open(name string) (*Plugin, error) { 53 cPath := make([]byte, C.PATH_MAX+1) 54 cRelName := make([]byte, len(name)+1) 55 copy(cRelName, name) 56 if C.realpath( 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`) 60 } 61 62 filepath := C.GoString((*C.char)(unsafe.Pointer(&cPath[0]))) 63 64 pluginsMu.Lock() 65 if p := plugins[filepath]; p != nil { 66 pluginsMu.Unlock() 67 if p.err != "" { 68 return nil, errors.New(`plugin.Open("` + name + `"): ` + p.err + ` (previous failure)`) 69 } 70 <-p.loaded 71 return p, nil 72 } 73 var cErr *C.char 74 h := C.pluginOpen((*C.char)(unsafe.Pointer(&cPath[0])), &cErr) 75 if h == 0 { 76 pluginsMu.Unlock() 77 return nil, errors.New(`plugin.Open("` + name + `"): ` + C.GoString(cErr)) 78 } 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] 83 } 84 if plugins == nil { 85 plugins = make(map[string]*Plugin) 86 } 87 pluginpath, syms, errstr := lastmoduleinit() 88 if errstr != "" { 89 plugins[filepath] = &Plugin{ 90 pluginpath: pluginpath, 91 err: errstr, 92 } 93 pluginsMu.Unlock() 94 return nil, errors.New(`plugin.Open("` + name + `"): ` + errstr) 95 } 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. 98 p := &Plugin{ 99 pluginpath: pluginpath, 100 loaded: make(chan struct{}), 101 } 102 plugins[filepath] = p 103 pluginsMu.Unlock() 104 105 initStr := make([]byte, len(pluginpath)+6) 106 copy(initStr, pluginpath) 107 copy(initStr[len(pluginpath):], ".init") 108 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)) 113 initFunc() 114 } 115 116 // Fill out the value of each plugin symbol. 117 updatedSyms := map[string]interface{}{} 118 for symName, sym := range syms { 119 isFunc := symName[0] == '.' 120 if isFunc { 121 delete(syms, symName) 122 symName = symName[1:] 123 } 124 125 fullName := pluginpath + "." + symName 126 cname := make([]byte, len(fullName)+1) 127 copy(cname, fullName) 128 129 p := C.pluginLookup(h, (*C.char)(unsafe.Pointer(&cname[0])), &cErr) 130 if p == nil { 131 return nil, errors.New(`plugin.Open("` + name + `"): could not find symbol ` + symName + `: ` + C.GoString(cErr)) 132 } 133 valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&sym)) 134 if isFunc { 135 (*valp)[1] = unsafe.Pointer(&p) 136 } else { 137 (*valp)[1] = p 138 } 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 142 } 143 p.syms = updatedSyms 144 145 close(p.loaded) 146 return p, nil 147 } 148 149 func lookup(p *Plugin, symName string) (Symbol, error) { 150 if s := p.syms[symName]; s != nil { 151 return s, nil 152 } 153 return nil, errors.New("plugin: symbol " + symName + " not found in plugin " + p.pluginpath) 154 } 155 156 var ( 157 pluginsMu sync.Mutex 158 plugins map[string]*Plugin 159 ) 160 161 // lastmoduleinit is defined in package runtime 162 func lastmoduleinit() (pluginpath string, syms map[string]interface{}, errstr string)