Source file src/runtime/symtab.go

     1  // Copyright 2014 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/goarch"
     9  	"runtime/internal/atomic"
    10  	"runtime/internal/sys"
    11  	"unsafe"
    12  )
    13  
    14  // Frames may be used to get function/file/line information for a
    15  // slice of PC values returned by Callers.
    16  type Frames struct {
    17  	// callers is a slice of PCs that have not yet been expanded to frames.
    18  	callers []uintptr
    19  
    20  	// frames is a slice of Frames that have yet to be returned.
    21  	frames     []Frame
    22  	frameStore [2]Frame
    23  }
    24  
    25  // Frame is the information returned by Frames for each call frame.
    26  type Frame struct {
    27  	// PC is the program counter for the location in this frame.
    28  	// For a frame that calls another frame, this will be the
    29  	// program counter of a call instruction. Because of inlining,
    30  	// multiple frames may have the same PC value, but different
    31  	// symbolic information.
    32  	PC uintptr
    33  
    34  	// Func is the Func value of this call frame. This may be nil
    35  	// for non-Go code or fully inlined functions.
    36  	Func *Func
    37  
    38  	// Function is the package path-qualified function name of
    39  	// this call frame. If non-empty, this string uniquely
    40  	// identifies a single function in the program.
    41  	// This may be the empty string if not known.
    42  	// If Func is not nil then Function == Func.Name().
    43  	Function string
    44  
    45  	// File and Line are the file name and line number of the
    46  	// location in this frame. For non-leaf frames, this will be
    47  	// the location of a call. These may be the empty string and
    48  	// zero, respectively, if not known.
    49  	File string
    50  	Line int
    51  
    52  	// startLine is the line number of the beginning of the function in
    53  	// this frame. Specifically, it is the line number of the func keyword
    54  	// for Go functions. Note that //line directives can change the
    55  	// filename and/or line number arbitrarily within a function, meaning
    56  	// that the Line - startLine offset is not always meaningful.
    57  	//
    58  	// This may be zero if not known.
    59  	startLine int
    60  
    61  	// Entry point program counter for the function; may be zero
    62  	// if not known. If Func is not nil then Entry ==
    63  	// Func.Entry().
    64  	Entry uintptr
    65  
    66  	// The runtime's internal view of the function. This field
    67  	// is set (funcInfo.valid() returns true) only for Go functions,
    68  	// not for C functions.
    69  	funcInfo funcInfo
    70  }
    71  
    72  // CallersFrames takes a slice of PC values returned by Callers and
    73  // prepares to return function/file/line information.
    74  // Do not change the slice until you are done with the Frames.
    75  func CallersFrames(callers []uintptr) *Frames {
    76  	f := &Frames{callers: callers}
    77  	f.frames = f.frameStore[:0]
    78  	return f
    79  }
    80  
    81  // Next returns a Frame representing the next call frame in the slice
    82  // of PC values. If it has already returned all call frames, Next
    83  // returns a zero Frame.
    84  //
    85  // The more result indicates whether the next call to Next will return
    86  // a valid Frame. It does not necessarily indicate whether this call
    87  // returned one.
    88  //
    89  // See the Frames example for idiomatic usage.
    90  func (ci *Frames) Next() (frame Frame, more bool) {
    91  	for len(ci.frames) < 2 {
    92  		// Find the next frame.
    93  		// We need to look for 2 frames so we know what
    94  		// to return for the "more" result.
    95  		if len(ci.callers) == 0 {
    96  			break
    97  		}
    98  		pc := ci.callers[0]
    99  		ci.callers = ci.callers[1:]
   100  		funcInfo := findfunc(pc)
   101  		if !funcInfo.valid() {
   102  			if cgoSymbolizer != nil {
   103  				// Pre-expand cgo frames. We could do this
   104  				// incrementally, too, but there's no way to
   105  				// avoid allocation in this case anyway.
   106  				ci.frames = append(ci.frames, expandCgoFrames(pc)...)
   107  			}
   108  			continue
   109  		}
   110  		f := funcInfo._Func()
   111  		entry := f.Entry()
   112  		if pc > entry {
   113  			// We store the pc of the start of the instruction following
   114  			// the instruction in question (the call or the inline mark).
   115  			// This is done for historical reasons, and to make FuncForPC
   116  			// work correctly for entries in the result of runtime.Callers.
   117  			pc--
   118  		}
   119  		name := funcname(funcInfo)
   120  		startLine := f.startLine()
   121  		if inldata := funcdata(funcInfo, _FUNCDATA_InlTree); inldata != nil {
   122  			inltree := (*[1 << 20]inlinedCall)(inldata)
   123  			// Non-strict as cgoTraceback may have added bogus PCs
   124  			// with a valid funcInfo but invalid PCDATA.
   125  			ix := pcdatavalue1(funcInfo, _PCDATA_InlTreeIndex, pc, nil, false)
   126  			if ix >= 0 {
   127  				// Note: entry is not modified. It always refers to a real frame, not an inlined one.
   128  				f = nil
   129  				ic := inltree[ix]
   130  				name = funcnameFromNameOff(funcInfo, ic.nameOff)
   131  				startLine = ic.startLine
   132  				// File/line from funcline1 below are already correct.
   133  			}
   134  		}
   135  		ci.frames = append(ci.frames, Frame{
   136  			PC:        pc,
   137  			Func:      f,
   138  			Function:  name,
   139  			Entry:     entry,
   140  			startLine: int(startLine),
   141  			funcInfo:  funcInfo,
   142  			// Note: File,Line set below
   143  		})
   144  	}
   145  
   146  	// Pop one frame from the frame list. Keep the rest.
   147  	// Avoid allocation in the common case, which is 1 or 2 frames.
   148  	switch len(ci.frames) {
   149  	case 0: // In the rare case when there are no frames at all, we return Frame{}.
   150  		return
   151  	case 1:
   152  		frame = ci.frames[0]
   153  		ci.frames = ci.frameStore[:0]
   154  	case 2:
   155  		frame = ci.frames[0]
   156  		ci.frameStore[0] = ci.frames[1]
   157  		ci.frames = ci.frameStore[:1]
   158  	default:
   159  		frame = ci.frames[0]
   160  		ci.frames = ci.frames[1:]
   161  	}
   162  	more = len(ci.frames) > 0
   163  	if frame.funcInfo.valid() {
   164  		// Compute file/line just before we need to return it,
   165  		// as it can be expensive. This avoids computing file/line
   166  		// for the Frame we find but don't return. See issue 32093.
   167  		file, line := funcline1(frame.funcInfo, frame.PC, false)
   168  		frame.File, frame.Line = file, int(line)
   169  	}
   170  	return
   171  }
   172  
   173  // runtime_FrameStartLine returns the start line of the function in a Frame.
   174  //
   175  //go:linkname runtime_FrameStartLine runtime/pprof.runtime_FrameStartLine
   176  func runtime_FrameStartLine(f *Frame) int {
   177  	return f.startLine
   178  }
   179  
   180  // runtime_expandFinalInlineFrame expands the final pc in stk to include all
   181  // "callers" if pc is inline.
   182  //
   183  //go:linkname runtime_expandFinalInlineFrame runtime/pprof.runtime_expandFinalInlineFrame
   184  func runtime_expandFinalInlineFrame(stk []uintptr) []uintptr {
   185  	if len(stk) == 0 {
   186  		return stk
   187  	}
   188  	pc := stk[len(stk)-1]
   189  	tracepc := pc - 1
   190  
   191  	f := findfunc(tracepc)
   192  	if !f.valid() {
   193  		// Not a Go function.
   194  		return stk
   195  	}
   196  
   197  	inldata := funcdata(f, _FUNCDATA_InlTree)
   198  	if inldata == nil {
   199  		// Nothing inline in f.
   200  		return stk
   201  	}
   202  
   203  	// Treat the previous func as normal. We haven't actually checked, but
   204  	// since this pc was included in the stack, we know it shouldn't be
   205  	// elided.
   206  	lastFuncID := funcID_normal
   207  
   208  	// Remove pc from stk; we'll re-add it below.
   209  	stk = stk[:len(stk)-1]
   210  
   211  	// See inline expansion in gentraceback.
   212  	var cache pcvalueCache
   213  	inltree := (*[1 << 20]inlinedCall)(inldata)
   214  	for {
   215  		// Non-strict as cgoTraceback may have added bogus PCs
   216  		// with a valid funcInfo but invalid PCDATA.
   217  		ix := pcdatavalue1(f, _PCDATA_InlTreeIndex, tracepc, &cache, false)
   218  		if ix < 0 {
   219  			break
   220  		}
   221  		if inltree[ix].funcID == funcID_wrapper && elideWrapperCalling(lastFuncID) {
   222  			// ignore wrappers
   223  		} else {
   224  			stk = append(stk, pc)
   225  		}
   226  		lastFuncID = inltree[ix].funcID
   227  		// Back up to an instruction in the "caller".
   228  		tracepc = f.entry() + uintptr(inltree[ix].parentPc)
   229  		pc = tracepc + 1
   230  	}
   231  
   232  	// N.B. we want to keep the last parentPC which is not inline.
   233  	stk = append(stk, pc)
   234  
   235  	return stk
   236  }
   237  
   238  // expandCgoFrames expands frame information for pc, known to be
   239  // a non-Go function, using the cgoSymbolizer hook. expandCgoFrames
   240  // returns nil if pc could not be expanded.
   241  func expandCgoFrames(pc uintptr) []Frame {
   242  	arg := cgoSymbolizerArg{pc: pc}
   243  	callCgoSymbolizer(&arg)
   244  
   245  	if arg.file == nil && arg.funcName == nil {
   246  		// No useful information from symbolizer.
   247  		return nil
   248  	}
   249  
   250  	var frames []Frame
   251  	for {
   252  		frames = append(frames, Frame{
   253  			PC:       pc,
   254  			Func:     nil,
   255  			Function: gostring(arg.funcName),
   256  			File:     gostring(arg.file),
   257  			Line:     int(arg.lineno),
   258  			Entry:    arg.entry,
   259  			// funcInfo is zero, which implies !funcInfo.valid().
   260  			// That ensures that we use the File/Line info given here.
   261  		})
   262  		if arg.more == 0 {
   263  			break
   264  		}
   265  		callCgoSymbolizer(&arg)
   266  	}
   267  
   268  	// No more frames for this PC. Tell the symbolizer we are done.
   269  	// We don't try to maintain a single cgoSymbolizerArg for the
   270  	// whole use of Frames, because there would be no good way to tell
   271  	// the symbolizer when we are done.
   272  	arg.pc = 0
   273  	callCgoSymbolizer(&arg)
   274  
   275  	return frames
   276  }
   277  
   278  // NOTE: Func does not expose the actual unexported fields, because we return *Func
   279  // values to users, and we want to keep them from being able to overwrite the data
   280  // with (say) *f = Func{}.
   281  // All code operating on a *Func must call raw() to get the *_func
   282  // or funcInfo() to get the funcInfo instead.
   283  
   284  // A Func represents a Go function in the running binary.
   285  type Func struct {
   286  	opaque struct{} // unexported field to disallow conversions
   287  }
   288  
   289  func (f *Func) raw() *_func {
   290  	return (*_func)(unsafe.Pointer(f))
   291  }
   292  
   293  func (f *Func) funcInfo() funcInfo {
   294  	return f.raw().funcInfo()
   295  }
   296  
   297  func (f *_func) funcInfo() funcInfo {
   298  	// Find the module containing fn. fn is located in the pclntable.
   299  	// The unsafe.Pointer to uintptr conversions and arithmetic
   300  	// are safe because we are working with module addresses.
   301  	ptr := uintptr(unsafe.Pointer(f))
   302  	var mod *moduledata
   303  	for datap := &firstmoduledata; datap != nil; datap = datap.next {
   304  		if len(datap.pclntable) == 0 {
   305  			continue
   306  		}
   307  		base := uintptr(unsafe.Pointer(&datap.pclntable[0]))
   308  		if base <= ptr && ptr < base+uintptr(len(datap.pclntable)) {
   309  			mod = datap
   310  			break
   311  		}
   312  	}
   313  	return funcInfo{f, mod}
   314  }
   315  
   316  // PCDATA and FUNCDATA table indexes.
   317  //
   318  // See funcdata.h and ../cmd/internal/objabi/funcdata.go.
   319  const (
   320  	_PCDATA_UnsafePoint   = 0
   321  	_PCDATA_StackMapIndex = 1
   322  	_PCDATA_InlTreeIndex  = 2
   323  	_PCDATA_ArgLiveIndex  = 3
   324  
   325  	_FUNCDATA_ArgsPointerMaps    = 0
   326  	_FUNCDATA_LocalsPointerMaps  = 1
   327  	_FUNCDATA_StackObjects       = 2
   328  	_FUNCDATA_InlTree            = 3
   329  	_FUNCDATA_OpenCodedDeferInfo = 4
   330  	_FUNCDATA_ArgInfo            = 5
   331  	_FUNCDATA_ArgLiveInfo        = 6
   332  	_FUNCDATA_WrapInfo           = 7
   333  
   334  	_ArgsSizeUnknown = -0x80000000
   335  )
   336  
   337  const (
   338  	// PCDATA_UnsafePoint values.
   339  	_PCDATA_UnsafePointSafe   = -1 // Safe for async preemption
   340  	_PCDATA_UnsafePointUnsafe = -2 // Unsafe for async preemption
   341  
   342  	// _PCDATA_Restart1(2) apply on a sequence of instructions, within
   343  	// which if an async preemption happens, we should back off the PC
   344  	// to the start of the sequence when resume.
   345  	// We need two so we can distinguish the start/end of the sequence
   346  	// in case that two sequences are next to each other.
   347  	_PCDATA_Restart1 = -3
   348  	_PCDATA_Restart2 = -4
   349  
   350  	// Like _PCDATA_RestartAtEntry, but back to function entry if async
   351  	// preempted.
   352  	_PCDATA_RestartAtEntry = -5
   353  )
   354  
   355  // A FuncID identifies particular functions that need to be treated
   356  // specially by the runtime.
   357  // Note that in some situations involving plugins, there may be multiple
   358  // copies of a particular special runtime function.
   359  // Note: this list must match the list in cmd/internal/objabi/funcid.go.
   360  type funcID uint8
   361  
   362  const (
   363  	funcID_normal funcID = iota // not a special function
   364  	funcID_abort
   365  	funcID_asmcgocall
   366  	funcID_asyncPreempt
   367  	funcID_cgocallback
   368  	funcID_debugCallV2
   369  	funcID_gcBgMarkWorker
   370  	funcID_goexit
   371  	funcID_gogo
   372  	funcID_gopanic
   373  	funcID_handleAsyncEvent
   374  	funcID_mcall
   375  	funcID_morestack
   376  	funcID_mstart
   377  	funcID_panicwrap
   378  	funcID_rt0_go
   379  	funcID_runfinq
   380  	funcID_runtime_main
   381  	funcID_sigpanic
   382  	funcID_systemstack
   383  	funcID_systemstack_switch
   384  	funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
   385  )
   386  
   387  // A FuncFlag holds bits about a function.
   388  // This list must match the list in cmd/internal/objabi/funcid.go.
   389  type funcFlag uint8
   390  
   391  const (
   392  	// TOPFRAME indicates a function that appears at the top of its stack.
   393  	// The traceback routine stop at such a function and consider that a
   394  	// successful, complete traversal of the stack.
   395  	// Examples of TOPFRAME functions include goexit, which appears
   396  	// at the top of a user goroutine stack, and mstart, which appears
   397  	// at the top of a system goroutine stack.
   398  	funcFlag_TOPFRAME funcFlag = 1 << iota
   399  
   400  	// SPWRITE indicates a function that writes an arbitrary value to SP
   401  	// (any write other than adding or subtracting a constant amount).
   402  	// The traceback routines cannot encode such changes into the
   403  	// pcsp tables, so the function traceback cannot safely unwind past
   404  	// SPWRITE functions. Stopping at an SPWRITE function is considered
   405  	// to be an incomplete unwinding of the stack. In certain contexts
   406  	// (in particular garbage collector stack scans) that is a fatal error.
   407  	funcFlag_SPWRITE
   408  
   409  	// ASM indicates that a function was implemented in assembly.
   410  	funcFlag_ASM
   411  )
   412  
   413  // pcHeader holds data used by the pclntab lookups.
   414  type pcHeader struct {
   415  	magic          uint32  // 0xFFFFFFF1
   416  	pad1, pad2     uint8   // 0,0
   417  	minLC          uint8   // min instruction size
   418  	ptrSize        uint8   // size of a ptr in bytes
   419  	nfunc          int     // number of functions in the module
   420  	nfiles         uint    // number of entries in the file tab
   421  	textStart      uintptr // base for function entry PC offsets in this module, equal to moduledata.text
   422  	funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
   423  	cuOffset       uintptr // offset to the cutab variable from pcHeader
   424  	filetabOffset  uintptr // offset to the filetab variable from pcHeader
   425  	pctabOffset    uintptr // offset to the pctab variable from pcHeader
   426  	pclnOffset     uintptr // offset to the pclntab variable from pcHeader
   427  }
   428  
   429  // moduledata records information about the layout of the executable
   430  // image. It is written by the linker. Any changes here must be
   431  // matched changes to the code in cmd/link/internal/ld/symtab.go:symtab.
   432  // moduledata is stored in statically allocated non-pointer memory;
   433  // none of the pointers here are visible to the garbage collector.
   434  type moduledata struct {
   435  	pcHeader     *pcHeader
   436  	funcnametab  []byte
   437  	cutab        []uint32
   438  	filetab      []byte
   439  	pctab        []byte
   440  	pclntable    []byte
   441  	ftab         []functab
   442  	findfunctab  uintptr
   443  	minpc, maxpc uintptr
   444  
   445  	text, etext           uintptr
   446  	noptrdata, enoptrdata uintptr
   447  	data, edata           uintptr
   448  	bss, ebss             uintptr
   449  	noptrbss, enoptrbss   uintptr
   450  	covctrs, ecovctrs     uintptr
   451  	end, gcdata, gcbss    uintptr
   452  	types, etypes         uintptr
   453  	rodata                uintptr
   454  	gofunc                uintptr // go.func.*
   455  
   456  	textsectmap []textsect
   457  	typelinks   []int32 // offsets from types
   458  	itablinks   []*itab
   459  
   460  	ptab []ptabEntry
   461  
   462  	pluginpath string
   463  	pkghashes  []modulehash
   464  
   465  	modulename   string
   466  	modulehashes []modulehash
   467  
   468  	hasmain uint8 // 1 if module contains the main function, 0 otherwise
   469  
   470  	gcdatamask, gcbssmask bitvector
   471  
   472  	typemap map[typeOff]*_type // offset to *_rtype in previous module
   473  
   474  	bad bool // module failed to load and should be ignored
   475  
   476  	next *moduledata
   477  }
   478  
   479  // A modulehash is used to compare the ABI of a new module or a
   480  // package in a new module with the loaded program.
   481  //
   482  // For each shared library a module links against, the linker creates an entry in the
   483  // moduledata.modulehashes slice containing the name of the module, the abi hash seen
   484  // at link time and a pointer to the runtime abi hash. These are checked in
   485  // moduledataverify1 below.
   486  //
   487  // For each loaded plugin, the pkghashes slice has a modulehash of the
   488  // newly loaded package that can be used to check the plugin's version of
   489  // a package against any previously loaded version of the package.
   490  // This is done in plugin.lastmoduleinit.
   491  type modulehash struct {
   492  	modulename   string
   493  	linktimehash string
   494  	runtimehash  *string
   495  }
   496  
   497  // pinnedTypemaps are the map[typeOff]*_type from the moduledata objects.
   498  //
   499  // These typemap objects are allocated at run time on the heap, but the
   500  // only direct reference to them is in the moduledata, created by the
   501  // linker and marked SNOPTRDATA so it is ignored by the GC.
   502  //
   503  // To make sure the map isn't collected, we keep a second reference here.
   504  var pinnedTypemaps []map[typeOff]*_type
   505  
   506  var firstmoduledata moduledata  // linker symbol
   507  var lastmoduledatap *moduledata // linker symbol
   508  var modulesSlice *[]*moduledata // see activeModules
   509  
   510  // activeModules returns a slice of active modules.
   511  //
   512  // A module is active once its gcdatamask and gcbssmask have been
   513  // assembled and it is usable by the GC.
   514  //
   515  // This is nosplit/nowritebarrier because it is called by the
   516  // cgo pointer checking code.
   517  //
   518  //go:nosplit
   519  //go:nowritebarrier
   520  func activeModules() []*moduledata {
   521  	p := (*[]*moduledata)(atomic.Loadp(unsafe.Pointer(&modulesSlice)))
   522  	if p == nil {
   523  		return nil
   524  	}
   525  	return *p
   526  }
   527  
   528  // modulesinit creates the active modules slice out of all loaded modules.
   529  //
   530  // When a module is first loaded by the dynamic linker, an .init_array
   531  // function (written by cmd/link) is invoked to call addmoduledata,
   532  // appending to the module to the linked list that starts with
   533  // firstmoduledata.
   534  //
   535  // There are two times this can happen in the lifecycle of a Go
   536  // program. First, if compiled with -linkshared, a number of modules
   537  // built with -buildmode=shared can be loaded at program initialization.
   538  // Second, a Go program can load a module while running that was built
   539  // with -buildmode=plugin.
   540  //
   541  // After loading, this function is called which initializes the
   542  // moduledata so it is usable by the GC and creates a new activeModules
   543  // list.
   544  //
   545  // Only one goroutine may call modulesinit at a time.
   546  func modulesinit() {
   547  	modules := new([]*moduledata)
   548  	for md := &firstmoduledata; md != nil; md = md.next {
   549  		if md.bad {
   550  			continue
   551  		}
   552  		*modules = append(*modules, md)
   553  		if md.gcdatamask == (bitvector{}) {
   554  			scanDataSize := md.edata - md.data
   555  			md.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(md.gcdata)), scanDataSize)
   556  			scanBSSSize := md.ebss - md.bss
   557  			md.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(md.gcbss)), scanBSSSize)
   558  			gcController.addGlobals(int64(scanDataSize + scanBSSSize))
   559  		}
   560  	}
   561  
   562  	// Modules appear in the moduledata linked list in the order they are
   563  	// loaded by the dynamic loader, with one exception: the
   564  	// firstmoduledata itself the module that contains the runtime. This
   565  	// is not always the first module (when using -buildmode=shared, it
   566  	// is typically libstd.so, the second module). The order matters for
   567  	// typelinksinit, so we swap the first module with whatever module
   568  	// contains the main function.
   569  	//
   570  	// See Issue #18729.
   571  	for i, md := range *modules {
   572  		if md.hasmain != 0 {
   573  			(*modules)[0] = md
   574  			(*modules)[i] = &firstmoduledata
   575  			break
   576  		}
   577  	}
   578  
   579  	atomicstorep(unsafe.Pointer(&modulesSlice), unsafe.Pointer(modules))
   580  }
   581  
   582  type functab struct {
   583  	entryoff uint32 // relative to runtime.text
   584  	funcoff  uint32
   585  }
   586  
   587  // Mapping information for secondary text sections
   588  
   589  type textsect struct {
   590  	vaddr    uintptr // prelinked section vaddr
   591  	end      uintptr // vaddr + section length
   592  	baseaddr uintptr // relocated section address
   593  }
   594  
   595  const minfunc = 16                 // minimum function size
   596  const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table
   597  
   598  // findfuncbucket is an array of these structures.
   599  // Each bucket represents 4096 bytes of the text segment.
   600  // Each subbucket represents 256 bytes of the text segment.
   601  // To find a function given a pc, locate the bucket and subbucket for
   602  // that pc. Add together the idx and subbucket value to obtain a
   603  // function index. Then scan the functab array starting at that
   604  // index to find the target function.
   605  // This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
   606  type findfuncbucket struct {
   607  	idx        uint32
   608  	subbuckets [16]byte
   609  }
   610  
   611  func moduledataverify() {
   612  	for datap := &firstmoduledata; datap != nil; datap = datap.next {
   613  		moduledataverify1(datap)
   614  	}
   615  }
   616  
   617  const debugPcln = false
   618  
   619  func moduledataverify1(datap *moduledata) {
   620  	// Check that the pclntab's format is valid.
   621  	hdr := datap.pcHeader
   622  	if hdr.magic != 0xfffffff1 || hdr.pad1 != 0 || hdr.pad2 != 0 ||
   623  		hdr.minLC != sys.PCQuantum || hdr.ptrSize != goarch.PtrSize || hdr.textStart != datap.text {
   624  		println("runtime: pcHeader: magic=", hex(hdr.magic), "pad1=", hdr.pad1, "pad2=", hdr.pad2,
   625  			"minLC=", hdr.minLC, "ptrSize=", hdr.ptrSize, "pcHeader.textStart=", hex(hdr.textStart),
   626  			"text=", hex(datap.text), "pluginpath=", datap.pluginpath)
   627  		throw("invalid function symbol table")
   628  	}
   629  
   630  	// ftab is lookup table for function by program counter.
   631  	nftab := len(datap.ftab) - 1
   632  	for i := 0; i < nftab; i++ {
   633  		// NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
   634  		if datap.ftab[i].entryoff > datap.ftab[i+1].entryoff {
   635  			f1 := funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff])), datap}
   636  			f2 := funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff])), datap}
   637  			f2name := "end"
   638  			if i+1 < nftab {
   639  				f2name = funcname(f2)
   640  			}
   641  			println("function symbol table not sorted by PC offset:", hex(datap.ftab[i].entryoff), funcname(f1), ">", hex(datap.ftab[i+1].entryoff), f2name, ", plugin:", datap.pluginpath)
   642  			for j := 0; j <= i; j++ {
   643  				println("\t", hex(datap.ftab[j].entryoff), funcname(funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff])), datap}))
   644  			}
   645  			if GOOS == "aix" && isarchive {
   646  				println("-Wl,-bnoobjreorder is mandatory on aix/ppc64 with c-archive")
   647  			}
   648  			throw("invalid runtime symbol table")
   649  		}
   650  	}
   651  
   652  	min := datap.textAddr(datap.ftab[0].entryoff)
   653  	max := datap.textAddr(datap.ftab[nftab].entryoff)
   654  	if datap.minpc != min || datap.maxpc != max {
   655  		println("minpc=", hex(datap.minpc), "min=", hex(min), "maxpc=", hex(datap.maxpc), "max=", hex(max))
   656  		throw("minpc or maxpc invalid")
   657  	}
   658  
   659  	for _, modulehash := range datap.modulehashes {
   660  		if modulehash.linktimehash != *modulehash.runtimehash {
   661  			println("abi mismatch detected between", datap.modulename, "and", modulehash.modulename)
   662  			throw("abi mismatch")
   663  		}
   664  	}
   665  }
   666  
   667  // textAddr returns md.text + off, with special handling for multiple text sections.
   668  // off is a (virtual) offset computed at internal linking time,
   669  // before the external linker adjusts the sections' base addresses.
   670  //
   671  // The text, or instruction stream is generated as one large buffer.
   672  // The off (offset) for a function is its offset within this buffer.
   673  // If the total text size gets too large, there can be issues on platforms like ppc64
   674  // if the target of calls are too far for the call instruction.
   675  // To resolve the large text issue, the text is split into multiple text sections
   676  // to allow the linker to generate long calls when necessary.
   677  // When this happens, the vaddr for each text section is set to its offset within the text.
   678  // Each function's offset is compared against the section vaddrs and ends to determine the containing section.
   679  // Then the section relative offset is added to the section's
   680  // relocated baseaddr to compute the function address.
   681  //
   682  // It is nosplit because it is part of the findfunc implementation.
   683  //
   684  //go:nosplit
   685  func (md *moduledata) textAddr(off32 uint32) uintptr {
   686  	off := uintptr(off32)
   687  	res := md.text + off
   688  	if len(md.textsectmap) > 1 {
   689  		for i, sect := range md.textsectmap {
   690  			// For the last section, include the end address (etext), as it is included in the functab.
   691  			if off >= sect.vaddr && off < sect.end || (i == len(md.textsectmap)-1 && off == sect.end) {
   692  				res = sect.baseaddr + off - sect.vaddr
   693  				break
   694  			}
   695  		}
   696  		if res > md.etext && GOARCH != "wasm" { // on wasm, functions do not live in the same address space as the linear memory
   697  			println("runtime: textAddr", hex(res), "out of range", hex(md.text), "-", hex(md.etext))
   698  			throw("runtime: text offset out of range")
   699  		}
   700  	}
   701  	return res
   702  }
   703  
   704  // textOff is the opposite of textAddr. It converts a PC to a (virtual) offset
   705  // to md.text, and returns if the PC is in any Go text section.
   706  //
   707  // It is nosplit because it is part of the findfunc implementation.
   708  //
   709  //go:nosplit
   710  func (md *moduledata) textOff(pc uintptr) (uint32, bool) {
   711  	res := uint32(pc - md.text)
   712  	if len(md.textsectmap) > 1 {
   713  		for i, sect := range md.textsectmap {
   714  			if sect.baseaddr > pc {
   715  				// pc is not in any section.
   716  				return 0, false
   717  			}
   718  			end := sect.baseaddr + (sect.end - sect.vaddr)
   719  			// For the last section, include the end address (etext), as it is included in the functab.
   720  			if i == len(md.textsectmap) {
   721  				end++
   722  			}
   723  			if pc < end {
   724  				res = uint32(pc - sect.baseaddr + sect.vaddr)
   725  				break
   726  			}
   727  		}
   728  	}
   729  	return res, true
   730  }
   731  
   732  // FuncForPC returns a *Func describing the function that contains the
   733  // given program counter address, or else nil.
   734  //
   735  // If pc represents multiple functions because of inlining, it returns
   736  // the *Func describing the innermost function, but with an entry of
   737  // the outermost function.
   738  func FuncForPC(pc uintptr) *Func {
   739  	f := findfunc(pc)
   740  	if !f.valid() {
   741  		return nil
   742  	}
   743  	if inldata := funcdata(f, _FUNCDATA_InlTree); inldata != nil {
   744  		// Note: strict=false so bad PCs (those between functions) don't crash the runtime.
   745  		// We just report the preceding function in that situation. See issue 29735.
   746  		// TODO: Perhaps we should report no function at all in that case.
   747  		// The runtime currently doesn't have function end info, alas.
   748  		if ix := pcdatavalue1(f, _PCDATA_InlTreeIndex, pc, nil, false); ix >= 0 {
   749  			inltree := (*[1 << 20]inlinedCall)(inldata)
   750  			ic := inltree[ix]
   751  			name := funcnameFromNameOff(f, ic.nameOff)
   752  			file, line := funcline(f, pc)
   753  			fi := &funcinl{
   754  				ones:      ^uint32(0),
   755  				entry:     f.entry(), // entry of the real (the outermost) function.
   756  				name:      name,
   757  				file:      file,
   758  				line:      line,
   759  				startLine: ic.startLine,
   760  			}
   761  			return (*Func)(unsafe.Pointer(fi))
   762  		}
   763  	}
   764  	return f._Func()
   765  }
   766  
   767  // Name returns the name of the function.
   768  func (f *Func) Name() string {
   769  	if f == nil {
   770  		return ""
   771  	}
   772  	fn := f.raw()
   773  	if fn.isInlined() { // inlined version
   774  		fi := (*funcinl)(unsafe.Pointer(fn))
   775  		return fi.name
   776  	}
   777  	return funcname(f.funcInfo())
   778  }
   779  
   780  // Entry returns the entry address of the function.
   781  func (f *Func) Entry() uintptr {
   782  	fn := f.raw()
   783  	if fn.isInlined() { // inlined version
   784  		fi := (*funcinl)(unsafe.Pointer(fn))
   785  		return fi.entry
   786  	}
   787  	return fn.funcInfo().entry()
   788  }
   789  
   790  // FileLine returns the file name and line number of the
   791  // source code corresponding to the program counter pc.
   792  // The result will not be accurate if pc is not a program
   793  // counter within f.
   794  func (f *Func) FileLine(pc uintptr) (file string, line int) {
   795  	fn := f.raw()
   796  	if fn.isInlined() { // inlined version
   797  		fi := (*funcinl)(unsafe.Pointer(fn))
   798  		return fi.file, int(fi.line)
   799  	}
   800  	// Pass strict=false here, because anyone can call this function,
   801  	// and they might just be wrong about targetpc belonging to f.
   802  	file, line32 := funcline1(f.funcInfo(), pc, false)
   803  	return file, int(line32)
   804  }
   805  
   806  // startLine returns the starting line number of the function. i.e., the line
   807  // number of the func keyword.
   808  func (f *Func) startLine() int32 {
   809  	fn := f.raw()
   810  	if fn.isInlined() { // inlined version
   811  		fi := (*funcinl)(unsafe.Pointer(fn))
   812  		return fi.startLine
   813  	}
   814  	return fn.funcInfo().startLine
   815  }
   816  
   817  // findmoduledatap looks up the moduledata for a PC.
   818  //
   819  // It is nosplit because it's part of the isgoexception
   820  // implementation.
   821  //
   822  //go:nosplit
   823  func findmoduledatap(pc uintptr) *moduledata {
   824  	for datap := &firstmoduledata; datap != nil; datap = datap.next {
   825  		if datap.minpc <= pc && pc < datap.maxpc {
   826  			return datap
   827  		}
   828  	}
   829  	return nil
   830  }
   831  
   832  type funcInfo struct {
   833  	*_func
   834  	datap *moduledata
   835  }
   836  
   837  func (f funcInfo) valid() bool {
   838  	return f._func != nil
   839  }
   840  
   841  func (f funcInfo) _Func() *Func {
   842  	return (*Func)(unsafe.Pointer(f._func))
   843  }
   844  
   845  // isInlined reports whether f should be re-interpreted as a *funcinl.
   846  func (f *_func) isInlined() bool {
   847  	return f.entryOff == ^uint32(0) // see comment for funcinl.ones
   848  }
   849  
   850  // entry returns the entry PC for f.
   851  func (f funcInfo) entry() uintptr {
   852  	return f.datap.textAddr(f.entryOff)
   853  }
   854  
   855  // findfunc looks up function metadata for a PC.
   856  //
   857  // It is nosplit because it's part of the isgoexception
   858  // implementation.
   859  //
   860  //go:nosplit
   861  func findfunc(pc uintptr) funcInfo {
   862  	datap := findmoduledatap(pc)
   863  	if datap == nil {
   864  		return funcInfo{}
   865  	}
   866  	const nsub = uintptr(len(findfuncbucket{}.subbuckets))
   867  
   868  	pcOff, ok := datap.textOff(pc)
   869  	if !ok {
   870  		return funcInfo{}
   871  	}
   872  
   873  	x := uintptr(pcOff) + datap.text - datap.minpc // TODO: are datap.text and datap.minpc always equal?
   874  	b := x / pcbucketsize
   875  	i := x % pcbucketsize / (pcbucketsize / nsub)
   876  
   877  	ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{})))
   878  	idx := ffb.idx + uint32(ffb.subbuckets[i])
   879  
   880  	// Find the ftab entry.
   881  	for datap.ftab[idx+1].entryoff <= pcOff {
   882  		idx++
   883  	}
   884  
   885  	funcoff := datap.ftab[idx].funcoff
   886  	return funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[funcoff])), datap}
   887  }
   888  
   889  type pcvalueCache struct {
   890  	entries [2][8]pcvalueCacheEnt
   891  }
   892  
   893  type pcvalueCacheEnt struct {
   894  	// targetpc and off together are the key of this cache entry.
   895  	targetpc uintptr
   896  	off      uint32
   897  	// val is the value of this cached pcvalue entry.
   898  	val int32
   899  }
   900  
   901  // pcvalueCacheKey returns the outermost index in a pcvalueCache to use for targetpc.
   902  // It must be very cheap to calculate.
   903  // For now, align to goarch.PtrSize and reduce mod the number of entries.
   904  // In practice, this appears to be fairly randomly and evenly distributed.
   905  func pcvalueCacheKey(targetpc uintptr) uintptr {
   906  	return (targetpc / goarch.PtrSize) % uintptr(len(pcvalueCache{}.entries))
   907  }
   908  
   909  // Returns the PCData value, and the PC where this value starts.
   910  // TODO: the start PC is returned only when cache is nil.
   911  func pcvalue(f funcInfo, off uint32, targetpc uintptr, cache *pcvalueCache, strict bool) (int32, uintptr) {
   912  	if off == 0 {
   913  		return -1, 0
   914  	}
   915  
   916  	// Check the cache. This speeds up walks of deep stacks, which
   917  	// tend to have the same recursive functions over and over.
   918  	//
   919  	// This cache is small enough that full associativity is
   920  	// cheaper than doing the hashing for a less associative
   921  	// cache.
   922  	if cache != nil {
   923  		x := pcvalueCacheKey(targetpc)
   924  		for i := range cache.entries[x] {
   925  			// We check off first because we're more
   926  			// likely to have multiple entries with
   927  			// different offsets for the same targetpc
   928  			// than the other way around, so we'll usually
   929  			// fail in the first clause.
   930  			ent := &cache.entries[x][i]
   931  			if ent.off == off && ent.targetpc == targetpc {
   932  				return ent.val, 0
   933  			}
   934  		}
   935  	}
   936  
   937  	if !f.valid() {
   938  		if strict && panicking.Load() == 0 {
   939  			println("runtime: no module data for", hex(f.entry()))
   940  			throw("no module data")
   941  		}
   942  		return -1, 0
   943  	}
   944  	datap := f.datap
   945  	p := datap.pctab[off:]
   946  	pc := f.entry()
   947  	prevpc := pc
   948  	val := int32(-1)
   949  	for {
   950  		var ok bool
   951  		p, ok = step(p, &pc, &val, pc == f.entry())
   952  		if !ok {
   953  			break
   954  		}
   955  		if targetpc < pc {
   956  			// Replace a random entry in the cache. Random
   957  			// replacement prevents a performance cliff if
   958  			// a recursive stack's cycle is slightly
   959  			// larger than the cache.
   960  			// Put the new element at the beginning,
   961  			// since it is the most likely to be newly used.
   962  			if cache != nil {
   963  				x := pcvalueCacheKey(targetpc)
   964  				e := &cache.entries[x]
   965  				ci := fastrandn(uint32(len(cache.entries[x])))
   966  				e[ci] = e[0]
   967  				e[0] = pcvalueCacheEnt{
   968  					targetpc: targetpc,
   969  					off:      off,
   970  					val:      val,
   971  				}
   972  			}
   973  
   974  			return val, prevpc
   975  		}
   976  		prevpc = pc
   977  	}
   978  
   979  	// If there was a table, it should have covered all program counters.
   980  	// If not, something is wrong.
   981  	if panicking.Load() != 0 || !strict {
   982  		return -1, 0
   983  	}
   984  
   985  	print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n")
   986  
   987  	p = datap.pctab[off:]
   988  	pc = f.entry()
   989  	val = -1
   990  	for {
   991  		var ok bool
   992  		p, ok = step(p, &pc, &val, pc == f.entry())
   993  		if !ok {
   994  			break
   995  		}
   996  		print("\tvalue=", val, " until pc=", hex(pc), "\n")
   997  	}
   998  
   999  	throw("invalid runtime symbol table")
  1000  	return -1, 0
  1001  }
  1002  
  1003  func cfuncname(f funcInfo) *byte {
  1004  	if !f.valid() || f.nameOff == 0 {
  1005  		return nil
  1006  	}
  1007  	return &f.datap.funcnametab[f.nameOff]
  1008  }
  1009  
  1010  func funcname(f funcInfo) string {
  1011  	return gostringnocopy(cfuncname(f))
  1012  }
  1013  
  1014  func funcpkgpath(f funcInfo) string {
  1015  	name := funcname(f)
  1016  	i := len(name) - 1
  1017  	for ; i > 0; i-- {
  1018  		if name[i] == '/' {
  1019  			break
  1020  		}
  1021  	}
  1022  	for ; i < len(name); i++ {
  1023  		if name[i] == '.' {
  1024  			break
  1025  		}
  1026  	}
  1027  	return name[:i]
  1028  }
  1029  
  1030  func cfuncnameFromNameOff(f funcInfo, nameOff int32) *byte {
  1031  	if !f.valid() {
  1032  		return nil
  1033  	}
  1034  	return &f.datap.funcnametab[nameOff]
  1035  }
  1036  
  1037  func funcnameFromNameOff(f funcInfo, nameOff int32) string {
  1038  	return gostringnocopy(cfuncnameFromNameOff(f, nameOff))
  1039  }
  1040  
  1041  func funcfile(f funcInfo, fileno int32) string {
  1042  	datap := f.datap
  1043  	if !f.valid() {
  1044  		return "?"
  1045  	}
  1046  	// Make sure the cu index and file offset are valid
  1047  	if fileoff := datap.cutab[f.cuOffset+uint32(fileno)]; fileoff != ^uint32(0) {
  1048  		return gostringnocopy(&datap.filetab[fileoff])
  1049  	}
  1050  	// pcln section is corrupt.
  1051  	return "?"
  1052  }
  1053  
  1054  func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int32) {
  1055  	datap := f.datap
  1056  	if !f.valid() {
  1057  		return "?", 0
  1058  	}
  1059  	fileno, _ := pcvalue(f, f.pcfile, targetpc, nil, strict)
  1060  	line, _ = pcvalue(f, f.pcln, targetpc, nil, strict)
  1061  	if fileno == -1 || line == -1 || int(fileno) >= len(datap.filetab) {
  1062  		// print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n")
  1063  		return "?", 0
  1064  	}
  1065  	file = funcfile(f, fileno)
  1066  	return
  1067  }
  1068  
  1069  func funcline(f funcInfo, targetpc uintptr) (file string, line int32) {
  1070  	return funcline1(f, targetpc, true)
  1071  }
  1072  
  1073  func funcspdelta(f funcInfo, targetpc uintptr, cache *pcvalueCache) int32 {
  1074  	x, _ := pcvalue(f, f.pcsp, targetpc, cache, true)
  1075  	if debugPcln && x&(goarch.PtrSize-1) != 0 {
  1076  		print("invalid spdelta ", funcname(f), " ", hex(f.entry()), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n")
  1077  		throw("bad spdelta")
  1078  	}
  1079  	return x
  1080  }
  1081  
  1082  // funcMaxSPDelta returns the maximum spdelta at any point in f.
  1083  func funcMaxSPDelta(f funcInfo) int32 {
  1084  	datap := f.datap
  1085  	p := datap.pctab[f.pcsp:]
  1086  	pc := f.entry()
  1087  	val := int32(-1)
  1088  	max := int32(0)
  1089  	for {
  1090  		var ok bool
  1091  		p, ok = step(p, &pc, &val, pc == f.entry())
  1092  		if !ok {
  1093  			return max
  1094  		}
  1095  		if val > max {
  1096  			max = val
  1097  		}
  1098  	}
  1099  }
  1100  
  1101  func pcdatastart(f funcInfo, table uint32) uint32 {
  1102  	return *(*uint32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
  1103  }
  1104  
  1105  func pcdatavalue(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache) int32 {
  1106  	if table >= f.npcdata {
  1107  		return -1
  1108  	}
  1109  	r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, true)
  1110  	return r
  1111  }
  1112  
  1113  func pcdatavalue1(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
  1114  	if table >= f.npcdata {
  1115  		return -1
  1116  	}
  1117  	r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, strict)
  1118  	return r
  1119  }
  1120  
  1121  // Like pcdatavalue, but also return the start PC of this PCData value.
  1122  // It doesn't take a cache.
  1123  func pcdatavalue2(f funcInfo, table uint32, targetpc uintptr) (int32, uintptr) {
  1124  	if table >= f.npcdata {
  1125  		return -1, 0
  1126  	}
  1127  	return pcvalue(f, pcdatastart(f, table), targetpc, nil, true)
  1128  }
  1129  
  1130  // funcdata returns a pointer to the ith funcdata for f.
  1131  // funcdata should be kept in sync with cmd/link:writeFuncs.
  1132  func funcdata(f funcInfo, i uint8) unsafe.Pointer {
  1133  	if i < 0 || i >= f.nfuncdata {
  1134  		return nil
  1135  	}
  1136  	base := f.datap.gofunc // load gofunc address early so that we calculate during cache misses
  1137  	p := uintptr(unsafe.Pointer(&f.nfuncdata)) + unsafe.Sizeof(f.nfuncdata) + uintptr(f.npcdata)*4 + uintptr(i)*4
  1138  	off := *(*uint32)(unsafe.Pointer(p))
  1139  	// Return off == ^uint32(0) ? 0 : f.datap.gofunc + uintptr(off), but without branches.
  1140  	// The compiler calculates mask on most architectures using conditional assignment.
  1141  	var mask uintptr
  1142  	if off == ^uint32(0) {
  1143  		mask = 1
  1144  	}
  1145  	mask--
  1146  	raw := base + uintptr(off)
  1147  	return unsafe.Pointer(raw & mask)
  1148  }
  1149  
  1150  // step advances to the next pc, value pair in the encoded table.
  1151  func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) {
  1152  	// For both uvdelta and pcdelta, the common case (~70%)
  1153  	// is that they are a single byte. If so, avoid calling readvarint.
  1154  	uvdelta := uint32(p[0])
  1155  	if uvdelta == 0 && !first {
  1156  		return nil, false
  1157  	}
  1158  	n := uint32(1)
  1159  	if uvdelta&0x80 != 0 {
  1160  		n, uvdelta = readvarint(p)
  1161  	}
  1162  	*val += int32(-(uvdelta & 1) ^ (uvdelta >> 1))
  1163  	p = p[n:]
  1164  
  1165  	pcdelta := uint32(p[0])
  1166  	n = 1
  1167  	if pcdelta&0x80 != 0 {
  1168  		n, pcdelta = readvarint(p)
  1169  	}
  1170  	p = p[n:]
  1171  	*pc += uintptr(pcdelta * sys.PCQuantum)
  1172  	return p, true
  1173  }
  1174  
  1175  // readvarint reads a varint from p.
  1176  func readvarint(p []byte) (read uint32, val uint32) {
  1177  	var v, shift, n uint32
  1178  	for {
  1179  		b := p[n]
  1180  		n++
  1181  		v |= uint32(b&0x7F) << (shift & 31)
  1182  		if b&0x80 == 0 {
  1183  			break
  1184  		}
  1185  		shift += 7
  1186  	}
  1187  	return n, v
  1188  }
  1189  
  1190  type stackmap struct {
  1191  	n        int32   // number of bitmaps
  1192  	nbit     int32   // number of bits in each bitmap
  1193  	bytedata [1]byte // bitmaps, each starting on a byte boundary
  1194  }
  1195  
  1196  //go:nowritebarrier
  1197  func stackmapdata(stkmap *stackmap, n int32) bitvector {
  1198  	// Check this invariant only when stackDebug is on at all.
  1199  	// The invariant is already checked by many of stackmapdata's callers,
  1200  	// and disabling it by default allows stackmapdata to be inlined.
  1201  	if stackDebug > 0 && (n < 0 || n >= stkmap.n) {
  1202  		throw("stackmapdata: index out of range")
  1203  	}
  1204  	return bitvector{stkmap.nbit, addb(&stkmap.bytedata[0], uintptr(n*((stkmap.nbit+7)>>3)))}
  1205  }
  1206  
  1207  // inlinedCall is the encoding of entries in the FUNCDATA_InlTree table.
  1208  type inlinedCall struct {
  1209  	funcID    funcID // type of the called function
  1210  	_         [3]byte
  1211  	nameOff   int32 // offset into pclntab for name of called function
  1212  	parentPc  int32 // position of an instruction whose source position is the call site (offset from entry)
  1213  	startLine int32 // line number of start of function (func keyword/TEXT directive)
  1214  }
  1215  

View as plain text