...
Run Format

Source file src/plugin/plugin_dlopen.go

Documentation: plugin

  // 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
  
  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"
  )
  
  // avoid a dependency on strings
  func lastIndexByte(s string, c byte) int {
  	for i := len(s) - 1; i >= 0; i-- {
  		if s[i] == c {
  			return i
  		}
  	}
  	return -1
  }
  
  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)
  

View as plain text