github.com/jonasi/go@v0.0.0-20150930005915-e78e654c1de0/src/runtime/symtab.go (about)

     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 "unsafe"
     8  
     9  // NOTE: Func does not expose the actual unexported fields, because we return *Func
    10  // values to users, and we want to keep them from being able to overwrite the data
    11  // with (say) *f = Func{}.
    12  // All code operating on a *Func must call raw to get the *_func instead.
    13  
    14  // A Func represents a Go function in the running binary.
    15  type Func struct {
    16  	opaque struct{} // unexported field to disallow conversions
    17  }
    18  
    19  func (f *Func) raw() *_func {
    20  	return (*_func)(unsafe.Pointer(f))
    21  }
    22  
    23  // funcdata.h
    24  const (
    25  	_PCDATA_StackMapIndex       = 0
    26  	_FUNCDATA_ArgsPointerMaps   = 0
    27  	_FUNCDATA_LocalsPointerMaps = 1
    28  	_ArgsSizeUnknown            = -0x80000000
    29  )
    30  
    31  // moduledata records information about the layout of the executable
    32  // image. It is written by the linker. Any changes here must be
    33  // matched changes to the code in cmd/internal/ld/symtab.go:symtab.
    34  // moduledata is stored in read-only memory; none of the pointers here
    35  // are visible to the garbage collector.
    36  type moduledata struct {
    37  	pclntable    []byte
    38  	ftab         []functab
    39  	filetab      []uint32
    40  	findfunctab  uintptr
    41  	minpc, maxpc uintptr
    42  
    43  	text, etext           uintptr
    44  	noptrdata, enoptrdata uintptr
    45  	data, edata           uintptr
    46  	bss, ebss             uintptr
    47  	noptrbss, enoptrbss   uintptr
    48  	end, gcdata, gcbss    uintptr
    49  
    50  	typelinks []*_type
    51  
    52  	modulename   string
    53  	modulehashes []modulehash
    54  
    55  	gcdatamask, gcbssmask bitvector
    56  
    57  	next *moduledata
    58  }
    59  
    60  // For each shared library a module links against, the linker creates an entry in the
    61  // moduledata.modulehashes slice containing the name of the module, the abi hash seen
    62  // at link time and a pointer to the runtime abi hash. These are checked in
    63  // moduledataverify1 below.
    64  type modulehash struct {
    65  	modulename   string
    66  	linktimehash string
    67  	runtimehash  *string
    68  }
    69  
    70  var firstmoduledata moduledata  // linker symbol
    71  var lastmoduledatap *moduledata // linker symbol
    72  
    73  type functab struct {
    74  	entry   uintptr
    75  	funcoff uintptr
    76  }
    77  
    78  const minfunc = 16                 // minimum function size
    79  const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table
    80  
    81  // findfunctab is an array of these structures.
    82  // Each bucket represents 4096 bytes of the text segment.
    83  // Each subbucket represents 256 bytes of the text segment.
    84  // To find a function given a pc, locate the bucket and subbucket for
    85  // that pc.  Add together the idx and subbucket value to obtain a
    86  // function index.  Then scan the functab array starting at that
    87  // index to find the target function.
    88  // This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
    89  type findfuncbucket struct {
    90  	idx        uint32
    91  	subbuckets [16]byte
    92  }
    93  
    94  func moduledataverify() {
    95  	for datap := &firstmoduledata; datap != nil; datap = datap.next {
    96  		moduledataverify1(datap)
    97  	}
    98  }
    99  
   100  const debugPcln = false
   101  
   102  func moduledataverify1(datap *moduledata) {
   103  	// See golang.org/s/go12symtab for header: 0xfffffffb,
   104  	// two zero bytes, a byte giving the PC quantum,
   105  	// and a byte giving the pointer width in bytes.
   106  	pcln := *(**[8]byte)(unsafe.Pointer(&datap.pclntable))
   107  	pcln32 := *(**[2]uint32)(unsafe.Pointer(&datap.pclntable))
   108  	if pcln32[0] != 0xfffffffb || pcln[4] != 0 || pcln[5] != 0 || pcln[6] != _PCQuantum || pcln[7] != ptrSize {
   109  		println("runtime: function symbol table header:", hex(pcln32[0]), hex(pcln[4]), hex(pcln[5]), hex(pcln[6]), hex(pcln[7]))
   110  		throw("invalid function symbol table\n")
   111  	}
   112  
   113  	// ftab is lookup table for function by program counter.
   114  	nftab := len(datap.ftab) - 1
   115  	for i := 0; i < nftab; i++ {
   116  		// NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
   117  		if datap.ftab[i].entry > datap.ftab[i+1].entry {
   118  			f1 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff]))
   119  			f2 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff]))
   120  			f2name := "end"
   121  			if i+1 < nftab {
   122  				f2name = funcname(f2)
   123  			}
   124  			println("function symbol table not sorted by program counter:", hex(datap.ftab[i].entry), funcname(f1), ">", hex(datap.ftab[i+1].entry), f2name)
   125  			for j := 0; j <= i; j++ {
   126  				print("\t", hex(datap.ftab[j].entry), " ", funcname((*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff]))), "\n")
   127  			}
   128  			throw("invalid runtime symbol table")
   129  		}
   130  
   131  		if debugPcln || nftab-i < 5 {
   132  			// Check a PC near but not at the very end.
   133  			// The very end might be just padding that is not covered by the tables.
   134  			// No architecture rounds function entries to more than 16 bytes,
   135  			// but if one came along we'd need to subtract more here.
   136  			// But don't use the next PC if it corresponds to a foreign object chunk
   137  			// (no pcln table, f2.pcln == 0). That chunk might have an alignment
   138  			// more than 16 bytes.
   139  			f := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff]))
   140  			end := f.entry
   141  			if i+1 < nftab {
   142  				f2 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff]))
   143  				if f2.pcln != 0 {
   144  					end = f2.entry - 16
   145  					if end < f.entry {
   146  						end = f.entry
   147  					}
   148  				}
   149  			}
   150  			pcvalue(f, f.pcfile, end, true)
   151  			pcvalue(f, f.pcln, end, true)
   152  			pcvalue(f, f.pcsp, end, true)
   153  		}
   154  	}
   155  
   156  	if datap.minpc != datap.ftab[0].entry ||
   157  		datap.maxpc != datap.ftab[nftab].entry {
   158  		throw("minpc or maxpc invalid")
   159  	}
   160  
   161  	for _, modulehash := range datap.modulehashes {
   162  		if modulehash.linktimehash != *modulehash.runtimehash {
   163  			println("abi mismatch detected between", datap.modulename, "and", modulehash.modulename)
   164  			throw("abi mismatch")
   165  		}
   166  	}
   167  }
   168  
   169  // FuncForPC returns a *Func describing the function that contains the
   170  // given program counter address, or else nil.
   171  func FuncForPC(pc uintptr) *Func {
   172  	return (*Func)(unsafe.Pointer(findfunc(pc)))
   173  }
   174  
   175  // Name returns the name of the function.
   176  func (f *Func) Name() string {
   177  	return funcname(f.raw())
   178  }
   179  
   180  // Entry returns the entry address of the function.
   181  func (f *Func) Entry() uintptr {
   182  	return f.raw().entry
   183  }
   184  
   185  // FileLine returns the file name and line number of the
   186  // source code corresponding to the program counter pc.
   187  // The result will not be accurate if pc is not a program
   188  // counter within f.
   189  func (f *Func) FileLine(pc uintptr) (file string, line int) {
   190  	// Pass strict=false here, because anyone can call this function,
   191  	// and they might just be wrong about targetpc belonging to f.
   192  	file, line32 := funcline1(f.raw(), pc, false)
   193  	return file, int(line32)
   194  }
   195  
   196  func findmoduledatap(pc uintptr) *moduledata {
   197  	for datap := &firstmoduledata; datap != nil; datap = datap.next {
   198  		if datap.minpc <= pc && pc <= datap.maxpc {
   199  			return datap
   200  		}
   201  	}
   202  	return nil
   203  }
   204  
   205  func findfunc(pc uintptr) *_func {
   206  	datap := findmoduledatap(pc)
   207  	if datap == nil {
   208  		return nil
   209  	}
   210  	const nsub = uintptr(len(findfuncbucket{}.subbuckets))
   211  
   212  	x := pc - datap.minpc
   213  	b := x / pcbucketsize
   214  	i := x % pcbucketsize / (pcbucketsize / nsub)
   215  
   216  	ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{})))
   217  	idx := ffb.idx + uint32(ffb.subbuckets[i])
   218  	if pc < datap.ftab[idx].entry {
   219  		throw("findfunc: bad findfunctab entry")
   220  	}
   221  
   222  	// linear search to find func with pc >= entry.
   223  	for datap.ftab[idx+1].entry <= pc {
   224  		idx++
   225  	}
   226  	return (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff]))
   227  }
   228  
   229  func pcvalue(f *_func, off int32, targetpc uintptr, strict bool) int32 {
   230  	if off == 0 {
   231  		return -1
   232  	}
   233  	datap := findmoduledatap(f.entry) // inefficient
   234  	if datap == nil {
   235  		if strict && panicking == 0 {
   236  			print("runtime: no module data for ", hex(f.entry), "\n")
   237  			throw("no module data")
   238  		}
   239  		return -1
   240  	}
   241  	p := datap.pclntable[off:]
   242  	pc := f.entry
   243  	val := int32(-1)
   244  	for {
   245  		var ok bool
   246  		p, ok = step(p, &pc, &val, pc == f.entry)
   247  		if !ok {
   248  			break
   249  		}
   250  		if targetpc < pc {
   251  			return val
   252  		}
   253  	}
   254  
   255  	// If there was a table, it should have covered all program counters.
   256  	// If not, something is wrong.
   257  	if panicking != 0 || !strict {
   258  		return -1
   259  	}
   260  
   261  	print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n")
   262  
   263  	p = datap.pclntable[off:]
   264  	pc = f.entry
   265  	val = -1
   266  	for {
   267  		var ok bool
   268  		p, ok = step(p, &pc, &val, pc == f.entry)
   269  		if !ok {
   270  			break
   271  		}
   272  		print("\tvalue=", val, " until pc=", hex(pc), "\n")
   273  	}
   274  
   275  	throw("invalid runtime symbol table")
   276  	return -1
   277  }
   278  
   279  func cfuncname(f *_func) *byte {
   280  	if f == nil || f.nameoff == 0 {
   281  		return nil
   282  	}
   283  	datap := findmoduledatap(f.entry) // inefficient
   284  	if datap == nil {
   285  		return nil
   286  	}
   287  	return (*byte)(unsafe.Pointer(&datap.pclntable[f.nameoff]))
   288  }
   289  
   290  func funcname(f *_func) string {
   291  	return gostringnocopy(cfuncname(f))
   292  }
   293  
   294  func funcline1(f *_func, targetpc uintptr, strict bool) (file string, line int32) {
   295  	datap := findmoduledatap(f.entry) // inefficient
   296  	if datap == nil {
   297  		return "?", 0
   298  	}
   299  	fileno := int(pcvalue(f, f.pcfile, targetpc, strict))
   300  	line = pcvalue(f, f.pcln, targetpc, strict)
   301  	if fileno == -1 || line == -1 || fileno >= len(datap.filetab) {
   302  		// print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n")
   303  		return "?", 0
   304  	}
   305  	file = gostringnocopy(&datap.pclntable[datap.filetab[fileno]])
   306  	return
   307  }
   308  
   309  func funcline(f *_func, targetpc uintptr) (file string, line int32) {
   310  	return funcline1(f, targetpc, true)
   311  }
   312  
   313  func funcspdelta(f *_func, targetpc uintptr) int32 {
   314  	x := pcvalue(f, f.pcsp, targetpc, true)
   315  	if x&(ptrSize-1) != 0 {
   316  		print("invalid spdelta ", funcname(f), " ", hex(f.entry), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n")
   317  	}
   318  	return x
   319  }
   320  
   321  func pcdatavalue(f *_func, table int32, targetpc uintptr) int32 {
   322  	if table < 0 || table >= f.npcdata {
   323  		return -1
   324  	}
   325  	off := *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
   326  	return pcvalue(f, off, targetpc, true)
   327  }
   328  
   329  func funcdata(f *_func, i int32) unsafe.Pointer {
   330  	if i < 0 || i >= f.nfuncdata {
   331  		return nil
   332  	}
   333  	p := add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(f.npcdata)*4)
   334  	if ptrSize == 8 && uintptr(p)&4 != 0 {
   335  		if uintptr(unsafe.Pointer(f))&4 != 0 {
   336  			println("runtime: misaligned func", f)
   337  		}
   338  		p = add(p, 4)
   339  	}
   340  	return *(*unsafe.Pointer)(add(p, uintptr(i)*ptrSize))
   341  }
   342  
   343  // step advances to the next pc, value pair in the encoded table.
   344  func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) {
   345  	p, uvdelta := readvarint(p)
   346  	if uvdelta == 0 && !first {
   347  		return nil, false
   348  	}
   349  	if uvdelta&1 != 0 {
   350  		uvdelta = ^(uvdelta >> 1)
   351  	} else {
   352  		uvdelta >>= 1
   353  	}
   354  	vdelta := int32(uvdelta)
   355  	p, pcdelta := readvarint(p)
   356  	*pc += uintptr(pcdelta * _PCQuantum)
   357  	*val += vdelta
   358  	return p, true
   359  }
   360  
   361  // readvarint reads a varint from p.
   362  func readvarint(p []byte) (newp []byte, val uint32) {
   363  	var v, shift uint32
   364  	for {
   365  		b := p[0]
   366  		p = p[1:]
   367  		v |= (uint32(b) & 0x7F) << shift
   368  		if b&0x80 == 0 {
   369  			break
   370  		}
   371  		shift += 7
   372  	}
   373  	return p, v
   374  }
   375  
   376  type stackmap struct {
   377  	n        int32   // number of bitmaps
   378  	nbit     int32   // number of bits in each bitmap
   379  	bytedata [1]byte // bitmaps, each starting on a 32-bit boundary
   380  }
   381  
   382  //go:nowritebarrier
   383  func stackmapdata(stkmap *stackmap, n int32) bitvector {
   384  	if n < 0 || n >= stkmap.n {
   385  		throw("stackmapdata: index out of range")
   386  	}
   387  	return bitvector{stkmap.nbit, (*byte)(add(unsafe.Pointer(&stkmap.bytedata), uintptr(n*((stkmap.nbit+31)/32*4))))}
   388  }