github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/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  // pathToPrefix converts raw string to the prefix that will be used in the symbol
    53  // table. If modifying, modify the version in internal/obj/sym.go as well.
    54  func pathToPrefix(s string) string {
    55  	slash := lastIndexByte(s, '/')
    56  	// check for chars that need escaping
    57  	n := 0
    58  	for r := 0; r < len(s); r++ {
    59  		if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
    60  			n++
    61  		}
    62  	}
    63  
    64  	// quick exit
    65  	if n == 0 {
    66  		return s
    67  	}
    68  
    69  	// escape
    70  	const hex = "0123456789abcdef"
    71  	p := make([]byte, 0, len(s)+2*n)
    72  	for r := 0; r < len(s); r++ {
    73  		if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
    74  			p = append(p, '%', hex[c>>4], hex[c&0xF])
    75  		} else {
    76  			p = append(p, c)
    77  		}
    78  	}
    79  
    80  	return string(p)
    81  }
    82  
    83  func open(name string) (*Plugin, error) {
    84  	cPath := make([]byte, C.PATH_MAX+1)
    85  	cRelName := make([]byte, len(name)+1)
    86  	copy(cRelName, name)
    87  	if C.realpath(
    88  		(*C.char)(unsafe.Pointer(&cRelName[0])),
    89  		(*C.char)(unsafe.Pointer(&cPath[0]))) == nil {
    90  		return nil, errors.New("plugin.Open(" + name + "): realpath failed")
    91  	}
    92  
    93  	filepath := C.GoString((*C.char)(unsafe.Pointer(&cPath[0])))
    94  
    95  	pluginsMu.Lock()
    96  	if p := plugins[filepath]; p != nil {
    97  		pluginsMu.Unlock()
    98  		<-p.loaded
    99  		return p, nil
   100  	}
   101  	var cErr *C.char
   102  	h := C.pluginOpen((*C.char)(unsafe.Pointer(&cPath[0])), &cErr)
   103  	if h == 0 {
   104  		pluginsMu.Unlock()
   105  		return nil, errors.New("plugin.Open: " + C.GoString(cErr))
   106  	}
   107  	// TODO(crawshaw): look for plugin note, confirm it is a Go plugin
   108  	// and it was built with the correct toolchain.
   109  	if len(name) > 3 && name[len(name)-3:] == ".so" {
   110  		name = name[:len(name)-3]
   111  	}
   112  
   113  	pluginpath, syms, mismatchpkg := lastmoduleinit()
   114  	if mismatchpkg != "" {
   115  		pluginsMu.Unlock()
   116  		return nil, errors.New("plugin.Open: plugin was built with a different version of package " + mismatchpkg)
   117  	}
   118  	if plugins == nil {
   119  		plugins = make(map[string]*Plugin)
   120  	}
   121  	// This function can be called from the init function of a plugin.
   122  	// Drop a placeholder in the map so subsequent opens can wait on it.
   123  	p := &Plugin{
   124  		pluginpath: pluginpath,
   125  		loaded:     make(chan struct{}),
   126  	}
   127  	plugins[filepath] = p
   128  	pluginsMu.Unlock()
   129  
   130  	initStr := make([]byte, len(pluginpath)+6)
   131  	copy(initStr, pluginpath)
   132  	copy(initStr[len(pluginpath):], ".init")
   133  
   134  	initFuncPC := C.pluginLookup(h, (*C.char)(unsafe.Pointer(&initStr[0])), &cErr)
   135  	if initFuncPC != nil {
   136  		initFuncP := &initFuncPC
   137  		initFunc := *(*func())(unsafe.Pointer(&initFuncP))
   138  		initFunc()
   139  	}
   140  
   141  	// Fill out the value of each plugin symbol.
   142  	updatedSyms := map[string]interface{}{}
   143  	for symName, sym := range syms {
   144  		isFunc := symName[0] == '.'
   145  		if isFunc {
   146  			delete(syms, symName)
   147  			symName = symName[1:]
   148  		}
   149  
   150  		fullName := pathToPrefix(pluginpath) + "." + symName
   151  		cname := make([]byte, len(fullName)+1)
   152  		copy(cname, fullName)
   153  
   154  		p := C.pluginLookup(h, (*C.char)(unsafe.Pointer(&cname[0])), &cErr)
   155  		if p == nil {
   156  			return nil, errors.New("plugin.Open: could not find symbol " + symName + ": " + C.GoString(cErr))
   157  		}
   158  		valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&sym))
   159  		if isFunc {
   160  			(*valp)[1] = unsafe.Pointer(&p)
   161  		} else {
   162  			(*valp)[1] = p
   163  		}
   164  		// we can't add to syms during iteration as we'll end up processing
   165  		// some symbols twice with the inability to tell if the symbol is a function
   166  		updatedSyms[symName] = sym
   167  	}
   168  	p.syms = updatedSyms
   169  
   170  	close(p.loaded)
   171  	return p, nil
   172  }
   173  
   174  func lookup(p *Plugin, symName string) (Symbol, error) {
   175  	if s := p.syms[symName]; s != nil {
   176  		return s, nil
   177  	}
   178  	return nil, errors.New("plugin: symbol " + symName + " not found in plugin " + p.pluginpath)
   179  }
   180  
   181  var (
   182  	pluginsMu sync.Mutex
   183  	plugins   map[string]*Plugin
   184  )
   185  
   186  // lastmoduleinit is defined in package runtime
   187  func lastmoduleinit() (pluginpath string, syms map[string]interface{}, mismatchpkg string)