| // 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 freebsd,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)+len("..inittask")+1) // +1 for terminating NUL | 
 | 	copy(initStr, pluginpath) | 
 | 	copy(initStr[len(pluginpath):], "..inittask") | 
 |  | 
 | 	initTask := C.pluginLookup(h, (*C.char)(unsafe.Pointer(&initStr[0])), &cErr) | 
 | 	if initTask != nil { | 
 | 		doInit(initTask) | 
 | 	} | 
 |  | 
 | 	// 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) | 
 |  | 
 | // doInit is defined in package runtime | 
 | //go:linkname doInit runtime.doInit | 
 | func doInit(t unsafe.Pointer) // t should be a *runtime.initTask |