--- /dev/null
+// 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.
+
+package common
+
+var X int
+
+func init() {
+ X = 4
+}
--- /dev/null
+// 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.
+
+package main
+
+// // No C code required.
+import "C"
+
+// The common package imported here does not match the common package
+// imported by plugin1. A program that attempts to load plugin1 and
+// plugin-mismatch should produce an error.
+import "common"
+
+func ReadCommonX() int {
+ return common.X
+}
"log"
"path/filepath"
"plugin"
+ "strings"
"common"
)
log.Fatalf("after loading plugin2, common.X=%d, want %d", got, want)
}
+ _, err = plugin.Open("plugin-mismatch.so")
+ if err == nil {
+ log.Fatal(`plugin.Open("plugin-mismatch.so"): should have failed`)
+ }
+ if s := err.Error(); !strings.Contains(s, "different version") {
+ log.Fatalf(`plugin.Open("plugin-mismatch.so"): error does not mention "different version": %v`, s)
+ }
+
fmt.Println("PASS")
}
GOPATH=$(pwd) go build -buildmode=plugin plugin1
GOPATH=$(pwd) go build -buildmode=plugin plugin2
+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 host
goto out
}
- if Buildmode == BuildmodeShared {
+ if Buildmode == BuildmodeShared || Buildmode == BuildmodePlugin || ctxt.Syms.ROLookup("plugin.Open", 0) != nil {
before := f.Offset()
pkgdefBytes := make([]byte, atolwhex(arhdr.size))
if _, err := io.ReadFull(f, pkgdefBytes); err != nil {
Addaddr(ctxt, abihashgostr, hashsym)
adduint(ctxt, abihashgostr, uint64(hashsym.Size))
}
+ if Buildmode == BuildmodePlugin || ctxt.Syms.ROLookup("plugin.Open", 0) != nil {
+ for _, l := range ctxt.Library {
+ s := ctxt.Syms.Lookup("go.link.pkghashbytes."+l.Pkg, 0)
+ s.Attr |= AttrReachable
+ s.Type = obj.SRODATA
+ s.Size = int64(len(l.hash))
+ s.P = []byte(l.hash)
+ str := ctxt.Syms.Lookup("go.link.pkghash."+l.Pkg, 0)
+ str.Attr |= AttrReachable
+ str.Type = obj.SRODATA
+ Addaddr(ctxt, str, s)
+ adduint(ctxt, str, uint64(len(l.hash)))
+ }
+ }
nsections := textsectionmap(ctxt)
}
if Buildmode == BuildmodePlugin {
addgostring(ctxt, moduledata, "go.link.thispluginpath", *flagPluginPath)
+
+ pkghashes := ctxt.Syms.Lookup("go.link.pkghashes", 0)
+ pkghashes.Attr |= AttrReachable
+ pkghashes.Attr |= AttrLocal
+ pkghashes.Type = obj.SRODATA
+
+ for i, l := range ctxt.Library {
+ // pkghashes[i].name
+ addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkgname.%d", i), l.Pkg)
+ // pkghashes[i].linktimehash
+ addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkglinkhash.%d", i), string(l.hash))
+ // pkghashes[i].runtimehash
+ hash := ctxt.Syms.ROLookup("go.link.pkghash."+l.Pkg, 0)
+ Addaddr(ctxt, pkghashes, hash)
+ }
+ Addaddr(ctxt, moduledata, pkghashes)
+ adduint(ctxt, moduledata, uint64(len(ctxt.Library)))
+ adduint(ctxt, moduledata, uint64(len(ctxt.Library)))
} else {
+ adduint(ctxt, moduledata, 0) // pluginpath
+ adduint(ctxt, moduledata, 0)
+ adduint(ctxt, moduledata, 0) // pkghashes slice
adduint(ctxt, moduledata, 0)
adduint(ctxt, moduledata, 0)
}
name = name[:len(name)-3]
}
- pluginpath, syms := lastmoduleinit()
+ 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)
}
)
// lastmoduleinit is defined in package runtime
-func lastmoduleinit() (pluginpath string, syms map[string]interface{})
+func lastmoduleinit() (pluginpath string, syms map[string]interface{}, mismatchpkg string)
import "unsafe"
//go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
-func plugin_lastmoduleinit() (path string, syms map[string]interface{}) {
+func plugin_lastmoduleinit() (path string, syms map[string]interface{}, mismatchpkg string) {
md := firstmoduledata.next
if md == nil {
throw("runtime: no plugin module data")
throw("plugin: new module data overlaps with previous moduledata")
}
}
+ for _, pkghash := range md.pkghashes {
+ if pkghash.linktimehash != *pkghash.runtimehash {
+ return "", nil, pkghash.modulename
+ }
+ }
// Initialize the freshly loaded module.
modulesinit()
}
syms[name] = val
}
- return md.pluginpath, syms
+ return md.pluginpath, syms, ""
}
// inRange reports whether v0 or v1 are in the range [r0, r1].
ptab []ptabEntry
- pluginpath string
+ pluginpath string
+ pkghashes []modulehash
+
modulename string
modulehashes []modulehash
next *moduledata
}
+// A modulehash is used to compare the ABI of a new module or a
+// package in a new module with the loaded program.
+//
// For each shared library a module links against, the linker creates an entry in the
// moduledata.modulehashes slice containing the name of the module, the abi hash seen
// at link time and a pointer to the runtime abi hash. These are checked in
// moduledataverify1 below.
+//
+// For each loaded plugin, the the pkghashes slice has a modulehash of the
+// newly loaded package that can be used to check the plugin's version of
+// a package against any previously loaded version of the package.
+// This is done in plugin.lastmoduleinit.
type modulehash struct {
modulename string
linktimehash string