diff options
| author | David Crawshaw <crawshaw@golang.org> | 2016-11-12 06:24:36 -0500 |
|---|---|---|
| committer | David Crawshaw <crawshaw@golang.org> | 2016-11-15 16:14:27 +0000 |
| commit | 03da2690c9fefdbaff613f9ccc224b5f0abfbe16 (patch) | |
| tree | 548d28619823ab21f19ea6be1b35f979ce879396 /src | |
| parent | a145890059e9c7aae870e1b9e74b204b6c8bc8d5 (diff) | |
| download | go-03da2690c9fefdbaff613f9ccc224b5f0abfbe16.tar.xz | |
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>
Diffstat (limited to 'src')
| -rw-r--r-- | src/cmd/link/internal/ld/lib.go | 2 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/symtab.go | 35 | ||||
| -rw-r--r-- | src/plugin/plugin_dlopen.go | 8 | ||||
| -rw-r--r-- | src/runtime/plugin.go | 9 | ||||
| -rw-r--r-- | src/runtime/symtab.go | 12 |
5 files changed, 60 insertions, 6 deletions
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 8c2d31c841..e4c34750c7 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -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 { diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 323136c6f9..98ce3ad79b 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -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) } diff --git a/src/plugin/plugin_dlopen.go b/src/plugin/plugin_dlopen.go index f4addde74c..c5b0a4721c 100644 --- a/src/plugin/plugin_dlopen.go +++ b/src/plugin/plugin_dlopen.go @@ -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) diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go index 7907936e14..845bf76e92 100644 --- a/src/runtime/plugin.go +++ b/src/runtime/plugin.go @@ -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]. diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index bba3ccfc20..686af08ef0 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -202,7 +202,9 @@ type moduledata struct { ptab []ptabEntry - pluginpath string + 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 |
