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