aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/plugin.go
blob: 80869e1b1c1a5cb69502d26ee2b11ec323f7c9e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// 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(&ifaceLock)
	for _, i := range md.itablinks {
		additab(i, true, false)
	}
	unlock(&ifaceLock)

	// 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 := (*_func)(unsafe.Pointer(&md.pclntable[md.ftab[i].funcoff]))
		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 != nil {
			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
}