|  | // 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 darwin,cgo hurd,cgo | 
|  |  | 
|  | package plugin | 
|  |  | 
|  | /* | 
|  | #cgo linux LDFLAGS: -ldl | 
|  | #include <dlfcn.h> | 
|  | #include <limits.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <stdio.h> | 
|  |  | 
|  | static uintptr_t pluginOpen(const char* path, char** err) { | 
|  | void* h = dlopen(path, RTLD_NOW|RTLD_GLOBAL); | 
|  | if (h == NULL) { | 
|  | *err = (char*)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 = (char*)dlerror(); | 
|  | } | 
|  | return r; | 
|  | } | 
|  | */ | 
|  | import "C" | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "sync" | 
|  | "unsafe" | 
|  | ) | 
|  |  | 
|  | func open(name string) (*Plugin, error) { | 
|  | cPath := make([]byte, C.PATH_MAX+1) | 
|  | cRelName := make([]byte, len(name)+1) | 
|  | copy(cRelName, name) | 
|  | if C.realpath( | 
|  | (*C.char)(unsafe.Pointer(&cRelName[0])), | 
|  | (*C.char)(unsafe.Pointer(&cPath[0]))) == nil { | 
|  | return nil, errors.New(`plugin.Open("` + name + `"): realpath failed`) | 
|  | } | 
|  |  | 
|  | filepath := C.GoString((*C.char)(unsafe.Pointer(&cPath[0]))) | 
|  |  | 
|  | pluginsMu.Lock() | 
|  | if p := plugins[filepath]; p != nil { | 
|  | pluginsMu.Unlock() | 
|  | if p.err != "" { | 
|  | return nil, errors.New(`plugin.Open("` + name + `"): ` + p.err + ` (previous failure)`) | 
|  | } | 
|  | <-p.loaded | 
|  | return p, nil | 
|  | } | 
|  | var cErr *C.char | 
|  | h := C.pluginOpen((*C.char)(unsafe.Pointer(&cPath[0])), &cErr) | 
|  | if h == 0 { | 
|  | pluginsMu.Unlock() | 
|  | return nil, errors.New(`plugin.Open("` + name + `"): ` + C.GoString(cErr)) | 
|  | } | 
|  | // TODO(crawshaw): look for plugin note, confirm it is a Go plugin | 
|  | // and it was built with the correct toolchain. | 
|  | if len(name) > 3 && name[len(name)-3:] == ".so" { | 
|  | name = name[:len(name)-3] | 
|  | } | 
|  | if plugins == nil { | 
|  | plugins = make(map[string]*Plugin) | 
|  | } | 
|  | pluginpath, syms, errstr := lastmoduleinit() | 
|  | if errstr != "" { | 
|  | plugins[filepath] = &Plugin{ | 
|  | pluginpath: pluginpath, | 
|  | err:        errstr, | 
|  | } | 
|  | pluginsMu.Unlock() | 
|  | return nil, errors.New(`plugin.Open("` + name + `"): ` + errstr) | 
|  | } | 
|  | // 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{ | 
|  | pluginpath: pluginpath, | 
|  | loaded:     make(chan struct{}), | 
|  | } | 
|  | plugins[filepath] = p | 
|  | pluginsMu.Unlock() | 
|  |  | 
|  | initStr := make([]byte, len(pluginpath)+6) | 
|  | copy(initStr, pluginpath) | 
|  | copy(initStr[len(pluginpath):], ".init") | 
|  |  | 
|  | initFuncPC := C.pluginLookup(h, (*C.char)(unsafe.Pointer(&initStr[0])), &cErr) | 
|  | if initFuncPC != nil { | 
|  | initFuncP := &initFuncPC | 
|  | initFunc := *(*func())(unsafe.Pointer(&initFuncP)) | 
|  | initFunc() | 
|  | } | 
|  |  | 
|  | // Fill out the value of each plugin symbol. | 
|  | updatedSyms := map[string]interface{}{} | 
|  | for symName, sym := range syms { | 
|  | isFunc := symName[0] == '.' | 
|  | if isFunc { | 
|  | delete(syms, symName) | 
|  | symName = symName[1:] | 
|  | } | 
|  |  | 
|  | fullName := pluginpath + "." + symName | 
|  | cname := make([]byte, len(fullName)+1) | 
|  | copy(cname, fullName) | 
|  |  | 
|  | p := C.pluginLookup(h, (*C.char)(unsafe.Pointer(&cname[0])), &cErr) | 
|  | if p == nil { | 
|  | return nil, errors.New(`plugin.Open("` + name + `"): 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 | 
|  | } | 
|  | // we can't add to syms during iteration as we'll end up processing | 
|  | // some symbols twice with the inability to tell if the symbol is a function | 
|  | updatedSyms[symName] = sym | 
|  | } | 
|  | p.syms = updatedSyms | 
|  |  | 
|  | 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.pluginpath) | 
|  | } | 
|  |  | 
|  | var ( | 
|  | pluginsMu sync.Mutex | 
|  | plugins   map[string]*Plugin | 
|  | ) | 
|  |  | 
|  | // lastmoduleinit is defined in package runtime | 
|  | func lastmoduleinit() (pluginpath string, syms map[string]interface{}, errstr string) |