mirror of
https://github.com/golang/go
synced 2024-11-26 22:21:27 -07:00
2580d0e08d
And then revert the bootstrap cmd directories and certain testdata. And adjust tests as needed. Not reverting the changes in std that are bootstrapped, because some of those changes would appear in API docs, and we want to use any consistently. Instead, rewrite 'any' to 'interface{}' in cmd/dist for those directories when preparing the bootstrap copy. A few files changed as a result of running gofmt -w not because of interface{} -> any but because they hadn't been updated for the new //go:build lines. Fixes #49884. Change-Id: Ie8045cba995f65bd79c694ec77a1b3d1fe01bb09 Reviewed-on: https://go-review.googlesource.com/c/go/+/368254 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org>
155 lines
4.0 KiB
Go
155 lines
4.0 KiB
Go
// 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.
|
|
|
|
//go: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]any{}
|
|
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]any, errstr string)
|
|
|
|
// doInit is defined in package runtime
|
|
//go:linkname doInit runtime.doInit
|
|
func doInit(t unsafe.Pointer) // t should be a *runtime.initTask
|