1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package runtime
6
7import "unsafe"
8
9//go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
10func plugin_lastmoduleinit() (path string, syms map[string]interface{}, errstr string) {
11	var md *moduledata
12	for pmd := firstmoduledata.next; pmd != nil; pmd = pmd.next {
13		if pmd.bad {
14			md = nil // we only want the last module
15			continue
16		}
17		md = pmd
18	}
19	if md == nil {
20		throw("runtime: no plugin module data")
21	}
22	if md.pluginpath == "" {
23		throw("runtime: plugin has empty pluginpath")
24	}
25	if md.typemap != nil {
26		return "", nil, "plugin already loaded"
27	}
28
29	for _, pmd := range activeModules() {
30		if pmd.pluginpath == md.pluginpath {
31			md.bad = true
32			return "", nil, "plugin already loaded"
33		}
34
35		if inRange(pmd.text, pmd.etext, md.text, md.etext) ||
36			inRange(pmd.bss, pmd.ebss, md.bss, md.ebss) ||
37			inRange(pmd.data, pmd.edata, md.data, md.edata) ||
38			inRange(pmd.types, pmd.etypes, md.types, md.etypes) {
39			println("plugin: new module data overlaps with previous moduledata")
40			println("\tpmd.text-etext=", hex(pmd.text), "-", hex(pmd.etext))
41			println("\tpmd.bss-ebss=", hex(pmd.bss), "-", hex(pmd.ebss))
42			println("\tpmd.data-edata=", hex(pmd.data), "-", hex(pmd.edata))
43			println("\tpmd.types-etypes=", hex(pmd.types), "-", hex(pmd.etypes))
44			println("\tmd.text-etext=", hex(md.text), "-", hex(md.etext))
45			println("\tmd.bss-ebss=", hex(md.bss), "-", hex(md.ebss))
46			println("\tmd.data-edata=", hex(md.data), "-", hex(md.edata))
47			println("\tmd.types-etypes=", hex(md.types), "-", hex(md.etypes))
48			throw("plugin: new module data overlaps with previous moduledata")
49		}
50	}
51	for _, pkghash := range md.pkghashes {
52		if pkghash.linktimehash != *pkghash.runtimehash {
53			md.bad = true
54			return "", nil, "plugin was built with a different version of package " + pkghash.modulename
55		}
56	}
57
58	// Initialize the freshly loaded module.
59	modulesinit()
60	typelinksinit()
61
62	pluginftabverify(md)
63	moduledataverify1(md)
64
65	lock(&itabLock)
66	for _, i := range md.itablinks {
67		itabAdd(i)
68	}
69	unlock(&itabLock)
70
71	// Build a map of symbol names to symbols. Here in the runtime
72	// we fill out the first word of the interface, the type. We
73	// pass these zero value interfaces to the plugin package,
74	// where the symbol value is filled in (usually via cgo).
75	//
76	// Because functions are handled specially in the plugin package,
77	// function symbol names are prefixed here with '.' to avoid
78	// a dependency on the reflect package.
79	syms = make(map[string]interface{}, len(md.ptab))
80	for _, ptab := range md.ptab {
81		symName := resolveNameOff(unsafe.Pointer(md.types), ptab.name)
82		t := (*_type)(unsafe.Pointer(md.types)).typeOff(ptab.typ)
83		var val interface{}
84		valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&val))
85		(*valp)[0] = unsafe.Pointer(t)
86
87		name := symName.name()
88		if t.kind&kindMask == kindFunc {
89			name = "." + name
90		}
91		syms[name] = val
92	}
93	return md.pluginpath, syms, ""
94}
95
96func pluginftabverify(md *moduledata) {
97	badtable := false
98	for i := 0; i < len(md.ftab); i++ {
99		entry := md.ftab[i].entry
100		if md.minpc <= entry && entry <= md.maxpc {
101			continue
102		}
103
104		f := funcInfo{(*_func)(unsafe.Pointer(&md.pclntable[md.ftab[i].funcoff])), md}
105		name := funcname(f)
106
107		// A common bug is f.entry has a relocation to a duplicate
108		// function symbol, meaning if we search for its PC we get
109		// a valid entry with a name that is useful for debugging.
110		name2 := "none"
111		entry2 := uintptr(0)
112		f2 := findfunc(entry)
113		if f2.valid() {
114			name2 = funcname(f2)
115			entry2 = f2.entry
116		}
117		badtable = true
118		println("ftab entry outside pc range: ", hex(entry), "/", hex(entry2), ": ", name, "/", name2)
119	}
120	if badtable {
121		throw("runtime: plugin has bad symbol table")
122	}
123}
124
125// inRange reports whether v0 or v1 are in the range [r0, r1].
126func inRange(r0, r1, v0, v1 uintptr) bool {
127	return (v0 >= r0 && v0 <= r1) || (v1 >= r0 && v1 <= r1)
128}
129
130// A ptabEntry is generated by the compiler for each exported function
131// and global variable in the main package of a plugin. It is used to
132// initialize the plugin module's symbol map.
133type ptabEntry struct {
134	name nameOff
135	typ  typeOff
136}
137