1
0
mirror of https://github.com/golang/go synced 2024-11-19 16:44:43 -07:00
go/src/runtime/plugin.go
Keith Randall 89d74f5416 cmd/compile: set itab function pointers at compile time
I noticed that we don't set an itab's function pointers at compile
time. Instead, we currently do it at executable startup.

Set the function pointers at compile time instead. This shortens
startup time. It has no effect on normal binary size. Object files
will have more relocations, but that isn't a big deal.

For PIE there are additional pointers that will need to be adjusted at
load time. There are already other pointers in an itab that need to be
adjusted, so the cache line will already be paged in. There might be
some binary size overhead to mark these pointers. The "go test -c
-buildmode=pie net/http" binary is 0.18% bigger.

Update #20505

Change-Id: I267c82489915b509ff66e512fc7319b2dd79b8f7
Reviewed-on: https://go-review.googlesource.com/44341
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Martin Möhrmann <moehrmann@google.com>
2017-08-15 19:00:03 +00:00

129 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.
package runtime
import "unsafe"
//go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
func plugin_lastmoduleinit() (path string, syms map[string]interface{}, mismatchpkg string) {
md := firstmoduledata.next
if md == nil {
throw("runtime: no plugin module data")
}
for md.next != nil {
md = md.next
}
if md.typemap != nil {
throw("runtime: plugin already initialized")
}
for _, pmd := range activeModules() {
if pmd.pluginpath == md.pluginpath {
println("plugin: plugin", md.pluginpath, "already loaded")
throw("plugin: plugin already loaded")
}
if inRange(pmd.text, pmd.etext, md.text, md.etext) ||
inRange(pmd.bss, pmd.ebss, md.bss, md.ebss) ||
inRange(pmd.data, pmd.edata, md.data, md.edata) ||
inRange(pmd.types, pmd.etypes, md.types, md.etypes) {
println("plugin: new module data overlaps with previous moduledata")
println("\tpmd.text-etext=", hex(pmd.text), "-", hex(pmd.etext))
println("\tpmd.bss-ebss=", hex(pmd.bss), "-", hex(pmd.ebss))
println("\tpmd.data-edata=", hex(pmd.data), "-", hex(pmd.edata))
println("\tpmd.types-etypes=", hex(pmd.types), "-", hex(pmd.etypes))
println("\tmd.text-etext=", hex(md.text), "-", hex(md.etext))
println("\tmd.bss-ebss=", hex(md.bss), "-", hex(md.ebss))
println("\tmd.data-edata=", hex(md.data), "-", hex(md.edata))
println("\tmd.types-etypes=", hex(md.types), "-", hex(md.etypes))
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()
typelinksinit()
pluginftabverify(md)
moduledataverify1(md)
lock(&itabLock)
for _, i := range md.itablinks {
itabAdd(i)
}
unlock(&itabLock)
// Build a map of symbol names to symbols. Here in the runtime
// we fill out the first word of the interface, the type. We
// pass these zero value interfaces to the plugin package,
// where the symbol value is filled in (usually via cgo).
//
// Because functions are handled specially in the plugin package,
// function symbol names are prefixed here with '.' to avoid
// a dependency on the reflect package.
syms = make(map[string]interface{}, len(md.ptab))
for _, ptab := range md.ptab {
symName := resolveNameOff(unsafe.Pointer(md.types), ptab.name)
t := (*_type)(unsafe.Pointer(md.types)).typeOff(ptab.typ)
var val interface{}
valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&val))
(*valp)[0] = unsafe.Pointer(t)
name := symName.name()
if t.kind&kindMask == kindFunc {
name = "." + name
}
syms[name] = val
}
return md.pluginpath, syms, ""
}
func pluginftabverify(md *moduledata) {
badtable := false
for i := 0; i < len(md.ftab); i++ {
entry := md.ftab[i].entry
if md.minpc <= entry && entry <= md.maxpc {
continue
}
f := funcInfo{(*_func)(unsafe.Pointer(&md.pclntable[md.ftab[i].funcoff])), md}
name := funcname(f)
// A common bug is f.entry has a relocation to a duplicate
// function symbol, meaning if we search for its PC we get
// a valid entry with a name that is useful for debugging.
name2 := "none"
entry2 := uintptr(0)
f2 := findfunc(entry)
if f2.valid() {
name2 = funcname(f2)
entry2 = f2.entry
}
badtable = true
println("ftab entry outside pc range: ", hex(entry), "/", hex(entry2), ": ", name, "/", name2)
}
if badtable {
throw("runtime: plugin has bad symbol table")
}
}
// inRange reports whether v0 or v1 are in the range [r0, r1].
func inRange(r0, r1, v0, v1 uintptr) bool {
return (v0 >= r0 && v0 <= r1) || (v1 >= r0 && v1 <= r1)
}
// A ptabEntry is generated by the compiler for each exported function
// and global variable in the main package of a plugin. It is used to
// initialize the plugin module's symbol map.
type ptabEntry struct {
name nameOff
typ typeOff
}