]> Cypherpunks.ru repositories - gostls13.git/commitdiff
runtime, plugin: error not throw on duplicate open
authorDavid Crawshaw <crawshaw@golang.org>
Sat, 2 Sep 2017 16:05:35 +0000 (12:05 -0400)
committerDavid Crawshaw <crawshaw@golang.org>
Sat, 9 Sep 2017 16:26:33 +0000 (16:26 +0000)
Along the way, track bad modules. Make sure they don't end up on
the active modules list, and aren't accidentally reprocessed as
new plugins.

Fixes #19004

Change-Id: I8a5e7bb11f572f7b657a97d521a7f84822a35c07
Reviewed-on: https://go-review.googlesource.com/61171
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
misc/cgo/testplugin/src/host/host.go
misc/cgo/testplugin/test.bash
src/plugin/plugin.go
src/plugin/plugin_dlopen.go
src/runtime/plugin.go
src/runtime/symtab.go

index bba9a621667e9169765ce2e45516948c632a5997..0ca17da3def028bab16a3cb9fdbb1805dd2e62d4 100644 (file)
@@ -136,6 +136,14 @@ func main() {
                log.Fatalf("after loading plugin2, common.X=%d, want %d", got, want)
        }
 
+       _, err = plugin.Open("plugin2-dup.so")
+       if err == nil {
+               log.Fatal(`plugin.Open("plugin2-dup.so"): duplicate open should have failed`)
+       }
+       if s := err.Error(); !strings.Contains(s, "already loaded") {
+               log.Fatal(`plugin.Open("plugin2.so"): error does not mention "already loaded"`)
+       }
+
        _, err = plugin.Open("plugin-mismatch.so")
        if err == nil {
                log.Fatal(`plugin.Open("plugin-mismatch.so"): should have failed`)
@@ -144,6 +152,15 @@ func main() {
                log.Fatalf(`plugin.Open("plugin-mismatch.so"): error does not mention "different version": %v`, s)
        }
 
+       _, err = plugin.Open("plugin2-dup.so")
+       if err == nil {
+               log.Fatal(`plugin.Open("plugin2-dup.so"): duplicate open after bad plugin should have failed`)
+       }
+       _, err = plugin.Open("plugin2.so")
+       if err != nil {
+               log.Fatalf(`plugin.Open("plugin2.so"): second open with same name failed: %v`, err)
+       }
+
        // Test that unexported types with the same names in
        // different plugins do not interfere with each other.
        //
index 7e982c89615f42eed608290af5a81e5f9e880f07..f64be9b0ffe3e9bda7a6091bedfa88bed61b93f9 100755 (executable)
@@ -25,6 +25,7 @@ mkdir sub
 
 GOPATH=$(pwd) go build -buildmode=plugin plugin1
 GOPATH=$(pwd) go build -buildmode=plugin plugin2
+cp plugin2.so plugin2-dup.so
 GOPATH=$(pwd)/altpath go build -buildmode=plugin plugin-mismatch
 GOPATH=$(pwd) go build -buildmode=plugin -o=sub/plugin1.so sub/plugin1
 GOPATH=$(pwd) go build -buildmode=plugin -o=unnamed1.so unnamed1/main.go
index c7744658122a2b727730f55b40f3f6179d220910..c37b65fd824629c1f75701261383d28a8c641e3f 100644 (file)
@@ -20,6 +20,7 @@ package plugin
 // Plugin is a loaded Go plugin.
 type Plugin struct {
        pluginpath string
+       err        string        // set if plugin failed to load
        loaded     chan struct{} // closed when loaded
        syms       map[string]interface{}
 }
index ce66c036c95a0f301673d3a6accd16d079c870b7..37380989d7f57061c3d3960317c23c9dae820ac3 100644 (file)
@@ -87,7 +87,7 @@ func open(name string) (*Plugin, error) {
        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")
+               return nil, errors.New(`plugin.Open("` + name + `"): realpath failed`)
        }
 
        filepath := C.GoString((*C.char)(unsafe.Pointer(&cPath[0])))
@@ -95,6 +95,9 @@ func open(name string) (*Plugin, error) {
        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
        }
@@ -102,22 +105,25 @@ func open(name string) (*Plugin, error) {
        h := C.pluginOpen((*C.char)(unsafe.Pointer(&cPath[0])), &cErr)
        if h == 0 {
                pluginsMu.Unlock()
-               return nil, errors.New("plugin.Open: " + C.GoString(cErr))
+               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]
        }
-
-       pluginpath, syms, mismatchpkg := lastmoduleinit()
-       if mismatchpkg != "" {
-               pluginsMu.Unlock()
-               return nil, errors.New("plugin.Open: plugin was built with a different version of package " + mismatchpkg)
-       }
        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{
@@ -153,7 +159,7 @@ func open(name string) (*Plugin, error) {
 
                p := C.pluginLookup(h, (*C.char)(unsafe.Pointer(&cname[0])), &cErr)
                if p == nil {
-                       return nil, errors.New("plugin.Open: could not find symbol " + symName + ": " + C.GoString(cErr))
+                       return nil, errors.New(`plugin.Open("` + name + `"): could not find symbol ` + symName + `: ` + C.GoString(cErr))
                }
                valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&sym))
                if isFunc {
@@ -184,4 +190,4 @@ var (
 )
 
 // lastmoduleinit is defined in package runtime
-func lastmoduleinit() (pluginpath string, syms map[string]interface{}, mismatchpkg string)
+func lastmoduleinit() (pluginpath string, syms map[string]interface{}, errstr string)
index caecba67f84eb665406aae19823e92d78ab3a12c..5e05be71eced6e8126c3054a8b0d9d425a290e56 100644 (file)
@@ -7,22 +7,29 @@ package runtime
 import "unsafe"
 
 //go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
-func plugin_lastmoduleinit() (path string, syms map[string]interface{}, mismatchpkg string) {
-       md := firstmoduledata.next
+func plugin_lastmoduleinit() (path string, syms map[string]interface{}, errstr string) {
+       var md *moduledata
+       for pmd := firstmoduledata.next; pmd != nil; pmd = pmd.next {
+               if pmd.bad {
+                       md = nil // we only want the last module
+                       continue
+               }
+               md = pmd
+       }
        if md == nil {
                throw("runtime: no plugin module data")
        }
-       for md.next != nil {
-               md = md.next
+       if md.pluginpath == "" {
+               throw("runtime: plugin has empty pluginpath")
        }
        if md.typemap != nil {
-               throw("runtime: plugin already initialized")
+               return "", nil, "plugin already loaded"
        }
 
        for _, pmd := range activeModules() {
                if pmd.pluginpath == md.pluginpath {
-                       println("plugin: plugin", md.pluginpath, "already loaded")
-                       throw("plugin: plugin already loaded")
+                       md.bad = true
+                       return "", nil, "plugin already loaded"
                }
 
                if inRange(pmd.text, pmd.etext, md.text, md.etext) ||
@@ -43,7 +50,8 @@ func plugin_lastmoduleinit() (path string, syms map[string]interface{}, mismatch
        }
        for _, pkghash := range md.pkghashes {
                if pkghash.linktimehash != *pkghash.runtimehash {
-                       return "", nil, pkghash.modulename
+                       md.bad = true
+                       return "", nil, "plugin was built with a different version of package " + pkghash.modulename
                }
        }
 
index e1b41ca4ff8f029d132a5ce6c02ca8c4e2301549..4a68f4eaa0ca481d5911b8af923c388a8f95ee49 100644 (file)
@@ -338,8 +338,8 @@ const (
 // moduledata records information about the layout of the executable
 // image. It is written by the linker. Any changes here must be
 // matched changes to the code in cmd/internal/ld/symtab.go:symtab.
-// moduledata is stored in read-only memory; none of the pointers here
-// are visible to the garbage collector.
+// moduledata is stored in statically allocated non-pointer memory;
+// none of the pointers here are visible to the garbage collector.
 type moduledata struct {
        pclntable    []byte
        ftab         []functab
@@ -371,6 +371,8 @@ type moduledata struct {
 
        typemap map[typeOff]*_type // offset to *_rtype in previous module
 
+       bad bool // module failed to load and should be ignored
+
        next *moduledata
 }
 
@@ -443,6 +445,9 @@ func activeModules() []*moduledata {
 func modulesinit() {
        modules := new([]*moduledata)
        for md := &firstmoduledata; md != nil; md = md.next {
+               if md.bad {
+                       continue
+               }
                *modules = append(*modules, md)
                if md.gcdatamask == (bitvector{}) {
                        md.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(md.gcdata)), md.edata-md.data)