github.com/primecitizens/pcz/std@v0.2.1/core/abi/moduledata.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright 2023 The Prime Citizens
     3  
     4  package abi
     5  
     6  import (
     7  	"unsafe"
     8  	_ "unsafe" // for go:linkname
     9  
    10  	stdstring "github.com/primecitizens/pcz/std/builtin/string"
    11  	"github.com/primecitizens/pcz/std/core/arch"
    12  	"github.com/primecitizens/pcz/std/core/assert"
    13  	"github.com/primecitizens/pcz/std/core/mark"
    14  )
    15  
    16  var (
    17  	//go:linkname firstmoduledata runtime.firstmoduledata
    18  	firstmoduledata ModuleData // linker symbol
    19  
    20  	//go:linkname lastmoduledatap runtime.lastmoduledatap
    21  	lastmoduledatap *ModuleData // linker symbol
    22  )
    23  
    24  // ModuleIter is an iterator for active modules.
    25  type ModuleIter uintptr
    26  
    27  func (iter *ModuleIter) Nth(i int) (md *ModuleData, ok bool) {
    28  	if i == 0 {
    29  		*iter = ModuleIter(uintptr(unsafe.Pointer(&firstmoduledata)))
    30  	}
    31  
    32  	for md := (*ModuleData)(unsafe.Pointer(uintptr(*iter))); md != nil; md = md.Next {
    33  		if md.Bad {
    34  			continue
    35  		}
    36  
    37  		*iter = ModuleIter(uintptr(unsafe.Pointer(md.Next)))
    38  		return md, true
    39  	}
    40  
    41  	*iter = 0
    42  	return nil, false
    43  }
    44  
    45  func findModuleForType(ptrInModule uintptr) (md *ModuleData) {
    46  	for i, iter := 0, ModuleIter(0); ; i++ {
    47  		md, ok := iter.Nth(i)
    48  		if !ok {
    49  			break
    50  		}
    51  
    52  		if ptrInModule >= md.Types && ptrInModule < md.ETypes {
    53  			return md
    54  		}
    55  	}
    56  
    57  	return nil
    58  }
    59  
    60  func resolveNameOff(ptrInModule unsafe.Pointer, off NameOff) Name {
    61  	if off == 0 {
    62  		return Name{}
    63  	}
    64  
    65  	md := findModuleForType(uintptr(ptrInModule))
    66  	if md == nil {
    67  		// TODO: support runtime name
    68  		// 	// No module found. see if it is a run time name.
    69  		// 	reflectOffsLock()
    70  		// 	res, found := reflectOffs.m[int32(off)]
    71  		// 	reflectOffsUnlock()
    72  		// 	if !found {
    73  		// 		println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
    74  		// 		for next := &firstmoduledata; next != nil; next = next.next {
    75  		// 			println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
    76  		// 		}
    77  		// 		throw("runtime: name offset base pointer out of range")
    78  		// 	}
    79  		//
    80  		// 	return Name{(*byte)(res)}
    81  		return Name{}
    82  	}
    83  
    84  	res := md.Types + uintptr(off)
    85  	if res > md.ETypes {
    86  		println("runtime: nameOff", hex(off), "out of range", hex(md.Types), "-", hex(md.ETypes))
    87  		assert.Throw("runtime:", "name", "offset", "out", "of", "range")
    88  	}
    89  
    90  	return Name{Bytes: (*byte)(unsafe.Pointer(res))}
    91  }
    92  
    93  // resolveTypeOff resolves an *rtype offset from a base type.
    94  // The (*rtype).typeOff method is a convenience wrapper for this function.
    95  // Implemented in the runtime package.
    96  func resolveTypeOff(ptrInModule unsafe.Pointer, off TypeOff) *Type {
    97  	if off == 0 || off == -1 {
    98  		// -1 is the sentinel value for unreachable code.
    99  		// See cmd/link/internal/ld/data.go:relocsym.
   100  		return nil
   101  	}
   102  
   103  	md := findModuleForType(uintptr(ptrInModule))
   104  	if md == nil {
   105  		// TODO: support runtime name
   106  		// reflectOffsLock()
   107  		// res := reflectOffs.m[int32(off)]
   108  		// reflectOffsUnlock()
   109  		// if res == nil {
   110  		// 	println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:")
   111  		// 	for next := &firstmoduledata; next != nil; next = next.next {
   112  		// 		println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
   113  		// 	}
   114  		// 	throw("runtime: type offset base pointer out of range")
   115  		// }
   116  		// return (*_type)(res)
   117  		return nil
   118  	}
   119  
   120  	// TODO: initialize md.typemap (see $GOROOT/src/runtime/type.go#func.typelinksinit)
   121  	// if t := md.typemap[off]; t != nil {
   122  	// 	return t
   123  	// }
   124  
   125  	res := md.Types + uintptr(off)
   126  	if res > md.ETypes {
   127  		println("runtime: typeOff", hex(off), "out of range", hex(md.Types), "-", hex(md.ETypes))
   128  		assert.Throw("runtime:", "type", "offset", "out", "of", "range")
   129  	}
   130  	return (*Type)(unsafe.Pointer(res))
   131  }
   132  
   133  type hex = uint64
   134  
   135  //go:linkname unreachableMethod runtime.unreachableMethod
   136  func unreachableMethod()
   137  
   138  // resolveTextOff resolves a function pointer offset from a base type.
   139  // The (*rtype).textOff method is a convenience wrapper for this function.
   140  // Implemented in the runtime package.
   141  func resolveTextOff(rtype unsafe.Pointer, off TextOff) unsafe.Pointer {
   142  	if off == -1 {
   143  		// -1 is the sentinel value for unreachable code.
   144  		// See cmd/link/internal/ld/data.go:relocsym.
   145  		return unsafe.Pointer(FuncPCABIInternal(unreachableMethod))
   146  	}
   147  
   148  	md := findModuleForType(uintptr(rtype))
   149  	if md == nil {
   150  		// TODO: support runtime type.
   151  		// reflectOffsLock()
   152  		// res := reflectOffs.m[int32(off)]
   153  		// reflectOffsUnlock()
   154  		// if res == nil {
   155  		// 	println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:")
   156  		// 	for next := &firstmoduledata; next != nil; next = next.next {
   157  		// 		println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
   158  		// 	}
   159  		// 	throw("runtime: text offset base pointer out of range")
   160  		// }
   161  		// return res
   162  
   163  		assert.Throw("runtime:", "text", "offset", "base", "pointer", "out", "of", "range")
   164  	}
   165  
   166  	res := md.textAddr(uint32(off))
   167  	return unsafe.Pointer(res)
   168  }
   169  
   170  type GetItabResult uint8
   171  
   172  const (
   173  	GetItabResult_OK GetItabResult = iota
   174  	GetItabResult_UncommonType
   175  	GetItabResult_
   176  )
   177  
   178  func GetItab(inter *InterfaceType, typ *Type) (*Itab, GetItabResult) {
   179  	if len(inter.Methods) == 0 {
   180  		assert.Throw("internal", "error:", "misuse", "of", "itab")
   181  	}
   182  
   183  	// easy case
   184  	if typ.TFlag&TFlagUncommon == 0 {
   185  		return nil, GetItabResult_UncommonType
   186  	}
   187  
   188  	var m *Itab
   189  	// 	// First, look in the existing table to see if we can find the itab we need.
   190  	// 	// This is by far the most common case, so do it without locks.
   191  	// 	// Use atomic to ensure we see any previous writes done by the thread
   192  	// 	// that updates the itabTable field (with atomic.Storep in itabAdd).
   193  	// 	t := (*itabTableType)(atomic.Loadp(unsafe.Pointer(&itabTable)))
   194  	// 	if m = t.find(inter, typ); m != nil {
   195  	// 		goto finish
   196  	// 	}
   197  	//
   198  	// 	// Not found.  Grab the lock and try again.
   199  	// 	lock(&itabLock)
   200  	// 	if m = itabTable.find(inter, typ); m != nil {
   201  	// 		unlock(&itabLock)
   202  	// 		goto finish
   203  	// 	}
   204  	//
   205  	// 	// Entry doesn't exist yet. Make a new entry & add it.
   206  	// 	m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.Methods)-1)*goarch.PtrSize, 0, &memstats.other_sys))
   207  	// 	m.inter = inter
   208  	// 	m._type = typ
   209  	// 	// The hash is used in type switches. However, compiler statically generates itab's
   210  	// 	// for all interface/type pairs used in switches (which are added to itabTable
   211  	// 	// in itabsinit). The dynamically-generated itab's never participate in type switches,
   212  	// 	// and thus the hash is irrelevant.
   213  	// 	// Note: m.hash is _not_ the hash used for the runtime itabTable hash table.
   214  	// 	m.hash = 0
   215  	// 	m.init()
   216  	// 	itabAdd(m)
   217  	// 	unlock(&itabLock)
   218  	// finish:
   219  	if m.Fun[0] != 0 {
   220  		return m, GetItabResult_OK
   221  	}
   222  
   223  	// this can only happen if the conversion
   224  	// was already done once using the , ok form
   225  	// and we have a cached negative result.
   226  	// The cached result doesn't record which
   227  	// interface function was missing, so initialize
   228  	// the itab again to get the missing function name.
   229  	return m, GetItabResult_
   230  }
   231  
   232  // ModuleData records information about the layout of the executable
   233  // image. It is written by the linker. Any changes here must be
   234  // matched changes to the code in cmd/link/internal/ld/symtab.go:symtab.
   235  // moduledata is stored in statically allocated non-pointer memory;
   236  // none of the pointers here are visible to the garbage collector.
   237  //
   238  // See $GOROOT/src/runtime/symtab.go#type:moduledata
   239  type ModuleData struct {
   240  	_ mark.NotInHeap // Only in static data
   241  
   242  	PCHeader     *PCHeader
   243  	FuncNameTab  []byte
   244  	CuTab        []uint32
   245  	FileTab      []byte
   246  	PCTab        []byte
   247  	PCLnTable    []byte
   248  	FTab         []FuncTab
   249  	FindFuncTab  uintptr
   250  	MinPC, MaxPC uintptr
   251  
   252  	Text, Etext           uintptr
   253  	NOPTRdata, ENOPTRdata uintptr
   254  	Data, Edata           uintptr
   255  	Bss, Ebss             uintptr
   256  	NOPTRbss, ENOPTRbss   uintptr
   257  	CovCtrs, ECovCtrs     uintptr // since go1.20
   258  	End, GCdata, GCbss    uintptr
   259  	Types, ETypes         uintptr
   260  	ROData                uintptr
   261  	GoFunc                uintptr // go.func.*
   262  
   263  	TextSectMap []TextSect
   264  
   265  	// .Types + .TypeLinks[i] = uintptr(unsafe.Pointer(*Type))
   266  	//
   267  	// The linker will leave a table of all the typelinks for
   268  	// types in the binary, so the runtime can find them.
   269  	//
   270  	// When buildmode=shared, all types are in typelinks so the
   271  	// runtime can deduplicate type pointers.
   272  	//
   273  	// Ref: ${GOROOT}/src/cmd/compile/internal/reflectdata/reflect.go#func:writeType
   274  	TypeLinks []int32 // offsets from types
   275  	ITabLinks []*Itab
   276  
   277  	PTab []PTabEntry
   278  
   279  	PluginPath string
   280  	PkgHashes  []ModuleHash
   281  
   282  	// This slice records the initializing tasks that need to be
   283  	// done to start up the program. It is built by the linker.
   284  	InitTasks []*InitTask // since go1.21
   285  
   286  	ModuleName   string
   287  	ModuleHashes []ModuleHash
   288  
   289  	HasMain uint8 // 1 if module contains the main function, 0 otherwise
   290  
   291  	GCdataMask, GCbssMask BitVector
   292  
   293  	TypeMap map[TypeOff]*Type // offset to *_rtype in previous module
   294  
   295  	Bad bool // module failed to load and should be ignored
   296  
   297  	Next *ModuleData
   298  }
   299  
   300  // funcName returns the string at nameOff in the function name table.
   301  func (md *ModuleData) funcName(off NameOff) string {
   302  	if off == 0 {
   303  		return ""
   304  	}
   305  
   306  	return stdstring.FromByteArray(&md.FuncNameTab[off])
   307  }
   308  
   309  // textAddr returns md.text + off, with special handling for multiple text sections.
   310  // off is a (virtual) offset computed at internal linking time,
   311  // before the external linker adjusts the sections' base addresses.
   312  //
   313  // The text, or instruction stream is generated as one large buffer.
   314  // The off (offset) for a function is its offset within this buffer.
   315  // If the total text size gets too large, there can be issues on platforms like ppc64
   316  // if the target of calls are too far for the call instruction.
   317  // To resolve the large text issue, the text is split into multiple text sections
   318  // to allow the linker to generate long calls when necessary.
   319  // When this happens, the vaddr for each text section is set to its offset within the text.
   320  // Each function's offset is compared against the section vaddrs and ends to determine the containing section.
   321  // Then the section relative offset is added to the section's
   322  // relocated baseaddr to compute the function address.
   323  //
   324  // It is nosplit because it is part of the findfunc implementation.
   325  //
   326  //go:nosplit
   327  func (md *ModuleData) textAddr(off32 uint32) uintptr {
   328  	off := uintptr(off32)
   329  	res := md.Text + off
   330  	if len(md.TextSectMap) > 1 {
   331  		for i, sect := range md.TextSectMap {
   332  			// For the last section, include the end address (etext), as it is included in the functab.
   333  			if off >= sect.VAddr && off < sect.End || (i == len(md.TextSectMap)-1 && off == sect.End) {
   334  				res = sect.BaseAddr + off - sect.VAddr
   335  				break
   336  			}
   337  		}
   338  		if res > md.Etext && arch.IsWasm != 0 { // on wasm, functions do not live in the same address space as the linear memory
   339  			println("runtime: textAddr", hex(res), "out of range", hex(md.Text), "-", hex(md.Etext))
   340  			assert.Throw("runtime:", "text", "offset", "out", "of", "range")
   341  		}
   342  	}
   343  
   344  	return res
   345  }
   346  
   347  // textOff is the opposite of textAddr. It converts a PC to a (virtual) offset
   348  // to md.text, and returns if the PC is in any Go text section.
   349  //
   350  // It is nosplit because it is part of the findfunc implementation.
   351  //
   352  //go:nosplit
   353  func (md *ModuleData) textOff(pc uintptr) (uint32, bool) {
   354  	res := uint32(pc - md.Text)
   355  	if len(md.TextSectMap) > 1 {
   356  		for i, sect := range md.TextSectMap {
   357  			if sect.BaseAddr > pc {
   358  				// pc is not in any section.
   359  				return 0, false
   360  			}
   361  			end := sect.BaseAddr + (sect.End - sect.VAddr)
   362  			// For the last section, include the end address (etext), as it is included in the functab.
   363  			if i == len(md.TextSectMap) {
   364  				end++
   365  			}
   366  			if pc < end {
   367  				res = uint32(pc - sect.BaseAddr + sect.VAddr)
   368  				break
   369  			}
   370  		}
   371  	}
   372  	return res, true
   373  }
   374  
   375  // PCHeader holds data used by the pclntab lookups.
   376  type PCHeader struct {
   377  	// 0xfffffff1 (go1.20), 0xfffffff0 (go1.18) 0xfffffffa (go1.16) 0xfffffffb (go1.2)
   378  	Magic          uint32
   379  	pad1, pad2     uint8   // 0,0
   380  	MinLC          uint8   // min instruction size
   381  	PtrSize        uint8   // size of a ptr in bytes
   382  	NFunc          int     // number of functions in the module
   383  	NFiles         uint    // number of entries in the file tab
   384  	TextStart      uintptr // base for function entry PC offsets in this module, equal to moduledata.text
   385  	FuncNameOffset uintptr // offset to the funcnametab variable from pcHeader
   386  	CuOffset       uintptr // offset to the cutab variable from pcHeader
   387  	FileTabOffset  uintptr // offset to the filetab variable from pcHeader
   388  	PCTabOffset    uintptr // offset to the pctab variable from pcHeader
   389  	PCLnOffset     uintptr // offset to the pclntab variable from pcHeader
   390  }
   391  
   392  type FuncTab struct {
   393  	Entryoff uint32 // relative to runtime.text
   394  	Funcoff  uint32
   395  }
   396  
   397  // Mapping information for secondary text sections
   398  type TextSect struct {
   399  	VAddr    uintptr // prelinked section vaddr
   400  	End      uintptr // vaddr + section length
   401  	BaseAddr uintptr // relocated section address
   402  }
   403  
   404  // A PTabEntry is generated by the compiler for each exported function
   405  // and global variable in the main package of a plugin. It is used to
   406  // initialize the plugin module's symbol map.
   407  type PTabEntry struct {
   408  	Name NameOff
   409  	Type TypeOff
   410  }
   411  
   412  const minfunc = 16                 // minimum function size
   413  const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table
   414  
   415  // A ModuleHash is used to compare the ABI of a new module or a
   416  // package in a new module with the loaded program.
   417  //
   418  // For each shared library a module links against, the linker creates an entry in the
   419  // moduledata.modulehashes slice containing the name of the module, the abi hash seen
   420  // at link time and a pointer to the runtime abi hash. These are checked in
   421  // moduledataverify1 below.
   422  //
   423  // For each loaded plugin, the pkghashes slice has a ModuleHash of the
   424  // newly loaded package that can be used to check the plugin's version of
   425  // a package against any previously loaded version of the package.
   426  // This is done in plugin.lastmoduleinit.
   427  type ModuleHash struct {
   428  	ModuleName   string
   429  	LinktimeHash string
   430  	RuntimeHash  *string
   431  }
   432  
   433  // Information from the compiler about the layout of stack frames.
   434  // Note: this type must agree with reflect.bitVector.
   435  type BitVector struct {
   436  	N        int32 // # of bits
   437  	ByteData *uint8
   438  }
   439  
   440  // layout of Itab known to compilers
   441  // allocated in non-garbage-collected memory
   442  // Needs to be in sync with
   443  // ../cmd/compile/internal/reflectdata/reflect.go:/^func.WriteTabs.
   444  type Itab struct {
   445  	Inter *InterfaceType
   446  	Type  *Type
   447  	Hash  uint32 // copy of _type.hash. Used for type switches.
   448  	_     [4]byte
   449  
   450  	// actual length of Fun = len(.Inter.Methods)
   451  	// fun[0]==0 means _type does not implement inter.
   452  	Fun [1]uintptr
   453  }
   454  
   455  func (m *Itab) FuncPCs() []unsafe.Pointer {
   456  	return unsafe.Slice((*unsafe.Pointer)(unsafe.Pointer(&m.Fun[0])), len(m.Inter.Methods))
   457  }
   458  
   459  // init fills in the m.fun array with all the code pointers for
   460  // the m.inter/m._type pair. If the type does not implement the interface,
   461  // it sets m.fun[0] to 0 and returns the name of an interface function that is missing.
   462  // It is ok to call this multiple times on the same m, even concurrently.
   463  func (m *Itab) Init() string {
   464  	inter := m.Inter
   465  	typ := m.Type
   466  	x := typ.Uncommon()
   467  
   468  	// both inter and typ have method sorted by name,
   469  	// and interface names are unique,
   470  	// so can iterate over both in lock step;
   471  	// the loop is O(ni+nt) not O(ni*nt).
   472  	ni := len(inter.Methods)
   473  	nt := int(x.Mcount)
   474  	xmhdr := unsafe.Slice((*Method)(unsafe.Add(unsafe.Pointer(x), uintptr(x.Moff))), nt)
   475  	j := 0
   476  	methods := unsafe.Slice((*unsafe.Pointer)(unsafe.Pointer(&m.Fun[0])), ni)
   477  	var fun0 unsafe.Pointer
   478  imethods:
   479  	for k := 0; k < ni; k++ {
   480  		i := &inter.Methods[k]
   481  		itype := inter.Type.TypeOff(i.Typ)
   482  		name := inter.Type.NameOff(i.Name)
   483  		iname := name.Name()
   484  		ipkg := pkgPath(name)
   485  		if len(ipkg) == 0 {
   486  			ipkg = inter.PkgPath.Name()
   487  		}
   488  		for ; j < nt; j++ {
   489  			t := &xmhdr[j]
   490  			tname := typ.NameOff(t.Name)
   491  			if typ.TypeOff(t.Mtyp) == itype && tname.Name() == iname {
   492  				pkgPath := pkgPath(tname)
   493  				if len(pkgPath) == 0 {
   494  					pkgPath = typ.NameOff(x.PkgPath).Name()
   495  				}
   496  				if tname.IsExported() || pkgPath == ipkg {
   497  					if m != nil {
   498  						ifn := typ.TextOff(t.Ifn)
   499  						if k == 0 {
   500  							fun0 = ifn // we'll set m.fun[0] at the end
   501  						} else {
   502  							methods[k] = ifn
   503  						}
   504  					}
   505  					continue imethods
   506  				}
   507  			}
   508  		}
   509  		// didn't find method
   510  		m.Fun[0] = 0
   511  		return iname
   512  	}
   513  	m.Fun[0] = uintptr(fun0)
   514  	return ""
   515  }
   516  
   517  func pkgPath(n Name) string {
   518  	if n.Bytes == nil || *n.Data(0)&(1<<2) == 0 {
   519  		return ""
   520  	}
   521  	i, l := n.ReadVarint(1)
   522  	off := 1 + i + l
   523  	if *n.Data(0)&(1<<1) != 0 {
   524  		i2, l2 := n.ReadVarint(off)
   525  		off += i2 + l2
   526  	}
   527  	var nameOff NameOff
   528  	copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.Data(off)))[:])
   529  	pkgPathName := resolveNameOff(unsafe.Pointer(n.Bytes), nameOff)
   530  	return pkgPathName.Name()
   531  }
   532  
   533  // An InitTask represents the set of initializations that need to be done for a package.
   534  // Keep in sync with ../../test/noinit.go:InitTask
   535  //
   536  // see $GOROOT/src/runtime/proc.go#type:initTask
   537  type InitTask struct {
   538  	State uint32 // 0 = uninitialized, 1 = in progress, 2 = done
   539  	NFns  uint32
   540  	// followed by nfns pcs, uintptr sized, one per init function to run
   541  	_fns [0]uintptr // len(_fns) = NFns
   542  }
   543  
   544  // Nth returns the i-th init() function in the InitTask.
   545  //
   546  // i MUST be in range [0, tsk.NFns).
   547  func (tsk *InitTask) Nth(i uint32) func() {
   548  	p := unsafe.Add(
   549  		unsafe.Pointer(tsk), unsafe.Offsetof(tsk._fns)+uintptr(i)*arch.PtrSize,
   550  	)
   551  	return *(*func())(unsafe.Pointer(&p))
   552  }