mirror of
https://github.com/golang/go
synced 2024-11-19 16:44:43 -07:00
89d74f5416
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>
129 lines
4.0 KiB
Go
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
|
|
}
|