Source file src/runtime/plugin.go

     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  
     5  package runtime
     6  
     7  import (
     8  	"internal/abi"
     9  	"unsafe"
    10  )
    11  
    12  //go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
    13  func 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  
    99  func 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].
   130  func 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.
   137  type ptabEntry struct {
   138  	name nameOff
   139  	typ  typeOff
   140  }
   141  

View as plain text