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

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright 2023 The Prime Citizens
     3  //
     4  // Copyright 2017 The Go Authors. All rights reserved.
     5  // Use of this source code is governed by a BSD-style
     6  // license that can be found in the LICENSE file.
     7  
     8  package abi
     9  
    10  import (
    11  	"unsafe"
    12  
    13  	"github.com/primecitizens/pcz/std/core/mark"
    14  )
    15  
    16  type FuncVal struct {
    17  	Fn uintptr
    18  	// variable-size, fn-specific data here
    19  }
    20  
    21  // Layout of in-memory per-function information prepared by linker
    22  // See https://golang.org/s/go12symtab.
    23  // Keep in sync with linker (../cmd/link/internal/ld/pcln.go:/pclntab)
    24  // and with package debug/gosym and with symtab.go in package runtime.
    25  //
    26  // see $GOROOT/src/runtime/runtime2.go#type:_func
    27  type Func struct {
    28  	_ mark.NotInHeap // Only in static data
    29  
    30  	entryOff uint32  // start pc, as offset from moduledata.text/pcHeader.textStart
    31  	nameOff  NameOff // function name, as index into moduledata.funcnametab.
    32  
    33  	args        int32  // in/out args size
    34  	DeferReturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
    35  
    36  	PCSP      uint32
    37  	PCFile    uint32
    38  	PCLn      uint32
    39  	npcdata   uint32
    40  	cuOffset  uint32 // runtime.cutab offset of this function's CU
    41  	StartLine int32  // line number of start of function (func keyword/TEXT directive)
    42  	FuncID    FuncID // set for certain special runtime functions
    43  	Flag      FuncFlag
    44  	_         [1]byte // pad
    45  	nfuncdata uint8   // must be last, must end on a uint32-aligned boundary
    46  
    47  	// The end of the struct is followed immediately by two variable-length
    48  	// arrays that reference the pcdata and funcdata locations for this
    49  	// function.
    50  
    51  	// pcdata contains the offset into moduledata.pctab for the start of
    52  	// that index's table. e.g.,
    53  	// &moduledata.pctab[_func.pcdata[_PCDATA_UnsafePoint]] is the start of
    54  	// the unsafe point table.
    55  	//
    56  	// An offset of 0 indicates that there is no table.
    57  	//
    58  	// pcdata [npcdata]uint32
    59  
    60  	// funcdata contains the offset past moduledata.gofunc which contains a
    61  	// pointer to that index's funcdata. e.g.,
    62  	// *(moduledata.gofunc +  _func.funcdata[_FUNCDATA_ArgsPointerMaps]) is
    63  	// the argument pointer map.
    64  	//
    65  	// An offset of ^uint32(0) indicates that there is no entry.
    66  	//
    67  	// funcdata [nfuncdata]uint32
    68  }
    69  
    70  // IsInlined reports whether f should be re-interpreted as a *funcinl.
    71  func (f *Func) IsInlined() bool {
    72  	return f.entryOff == ^uint32(0) // see comment for funcinl.ones
    73  }
    74  
    75  // Pseudo-Func that is returned for PCs that occur in inlined code.
    76  // A *Func can be either a *_func or a *FuncInl, and they are distinguished
    77  // by the first uintptr.
    78  //
    79  // TODO(austin): Can we merge this with inlinedCall?
    80  type FuncInl struct {
    81  	ones      uint32  // set to ^0 to distinguish from _func
    82  	entry     uintptr // entry of the real (the "outermost") frame
    83  	name      string
    84  	file      string
    85  	line      int32
    86  	startLine int32
    87  }
    88  
    89  type FuncInfo struct {
    90  	*Func
    91  	Datap *ModuleData
    92  }
    93  
    94  func (f FuncInfo) Valid() bool {
    95  	return f.Func != nil
    96  }
    97  
    98  // Name is something like "main.(*T).F".
    99  func (f FuncInfo) Name() string {
   100  	if !f.Valid() {
   101  		return ""
   102  	}
   103  
   104  	return f.Datap.funcName(f.nameOff)
   105  }
   106  
   107  // Entry returns the entry PC for f.
   108  func (f FuncInfo) Entry() uintptr {
   109  	return f.Datap.textAddr(f.entryOff)
   110  }
   111  
   112  // findfuncbucket is an array of these structures.
   113  // Each bucket represents 4096 bytes of the text segment.
   114  // Each subbucket represents 256 bytes of the text segment.
   115  // To find a function given a pc, locate the bucket and subbucket for
   116  // that pc. Add together the idx and subbucket value to obtain a
   117  // function index. Then scan the functab array starting at that
   118  // index to find the target function.
   119  // This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
   120  type findfuncbucket struct {
   121  	idx        uint32
   122  	subbuckets [16]byte
   123  }
   124  
   125  // FindFunc looks up function metadata for a PC.
   126  //
   127  // It is nosplit because it's part of the isgoexception
   128  // implementation.
   129  //
   130  //go:nosplit
   131  func FindFunc(pc uintptr) FuncInfo {
   132  	datap := findModuleForPC(pc)
   133  	if datap == nil {
   134  		return FuncInfo{}
   135  	}
   136  
   137  	const nsub = uintptr(len(findfuncbucket{}.subbuckets))
   138  
   139  	pcOff, ok := datap.textOff(pc)
   140  	if !ok {
   141  		return FuncInfo{}
   142  	}
   143  
   144  	x := uintptr(pcOff) + datap.Text - datap.MinPC // TODO: are datap.text and datap.minpc always equal?
   145  	b := x / pcbucketsize
   146  	i := x % pcbucketsize / (pcbucketsize / nsub)
   147  
   148  	ffb := (*findfuncbucket)(unsafe.Add(unsafe.Pointer(datap.FindFuncTab), b*unsafe.Sizeof(findfuncbucket{})))
   149  	idx := ffb.idx + uint32(ffb.subbuckets[i])
   150  
   151  	// Find the ftab entry.
   152  	for datap.FTab[idx+1].Entryoff <= pcOff {
   153  		idx++
   154  	}
   155  
   156  	funcoff := datap.FTab[idx].Funcoff
   157  	return FuncInfo{
   158  		Func:  (*Func)(unsafe.Pointer(&datap.PCLnTable[funcoff])),
   159  		Datap: datap,
   160  	}
   161  }
   162  
   163  func findModuleForPC(pc uintptr) (md *ModuleData) {
   164  	for i, iter := 0, ModuleIter(0); ; i++ {
   165  		md, ok := iter.Nth(i)
   166  		if !ok {
   167  			break
   168  		}
   169  
   170  		if md.MinPC <= pc && pc < md.MaxPC {
   171  			return md
   172  		}
   173  	}
   174  
   175  	return nil
   176  }