mirror of
https://github.com/golang/go
synced 2024-11-26 09:48:14 -07:00
cmd/link, runtime, plugin: versioning
In plugins and every program that opens a plugin, include a hash of every imported package. There are two versions of each hash: one local and one exported. As the program starts and plugins are loaded, the first exported symbol for each package becomes the canonical version. Any subsequent plugin's local package hash symbol has to match the canonical version. Fixes #17832 Change-Id: I4e62c8e1729d322e14b1673bada40fa7a74ea8bc Reviewed-on: https://go-review.googlesource.com/33161 Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
a145890059
commit
03da2690c9
11
misc/cgo/testplugin/altpath/src/common/common.go
Normal file
11
misc/cgo/testplugin/altpath/src/common/common.go
Normal file
@ -0,0 +1,11 @@
|
||||
// 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
|
||||
}
|
17
misc/cgo/testplugin/altpath/src/plugin-mismatch/main.go
Normal file
17
misc/cgo/testplugin/altpath/src/plugin-mismatch/main.go
Normal file
@ -0,0 +1,17 @@
|
||||
// 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
|
||||
}
|
@ -9,6 +9,7 @@ import (
|
||||
"log"
|
||||
"path/filepath"
|
||||
"plugin"
|
||||
"strings"
|
||||
|
||||
"common"
|
||||
)
|
||||
@ -104,5 +105,13 @@ func main() {
|
||||
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")
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ mkdir sub
|
||||
|
||||
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
|
||||
|
||||
|
@ -720,7 +720,7 @@ func objfile(ctxt *Link, lib *Library) {
|
||||
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 {
|
||||
|
@ -530,6 +530,20 @@ func (ctxt *Link) symtab() {
|
||||
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)
|
||||
|
||||
@ -604,7 +618,28 @@ func (ctxt *Link) symtab() {
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
@ -69,7 +69,11 @@ func open(name string) (*Plugin, error) {
|
||||
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)
|
||||
}
|
||||
@ -131,4 +135,4 @@ var (
|
||||
)
|
||||
|
||||
// lastmoduleinit is defined in package runtime
|
||||
func lastmoduleinit() (pluginpath string, syms map[string]interface{})
|
||||
func lastmoduleinit() (pluginpath string, syms map[string]interface{}, mismatchpkg string)
|
||||
|
@ -7,7 +7,7 @@ package runtime
|
||||
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")
|
||||
@ -41,6 +41,11 @@ func plugin_lastmoduleinit() (path string, syms map[string]interface{}) {
|
||||
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()
|
||||
@ -74,7 +79,7 @@ func plugin_lastmoduleinit() (path string, syms map[string]interface{}) {
|
||||
}
|
||||
syms[name] = val
|
||||
}
|
||||
return md.pluginpath, syms
|
||||
return md.pluginpath, syms, ""
|
||||
}
|
||||
|
||||
// inRange reports whether v0 or v1 are in the range [r0, r1].
|
||||
|
@ -203,6 +203,8 @@ type moduledata struct {
|
||||
ptab []ptabEntry
|
||||
|
||||
pluginpath string
|
||||
pkghashes []modulehash
|
||||
|
||||
modulename string
|
||||
modulehashes []modulehash
|
||||
|
||||
@ -213,10 +215,18 @@ type moduledata struct {
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user