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