1  
     2  
     3  
     4  
     5  
     8  
     9  package gosym
    10  
    11  import (
    12  	"bytes"
    13  	"encoding/binary"
    14  	"sort"
    15  	"sync"
    16  )
    17  
    18  
    19  type version int
    20  
    21  const (
    22  	verUnknown version = iota
    23  	ver11
    24  	ver12
    25  	ver116
    26  	ver118
    27  	ver120
    28  )
    29  
    30  
    31  
    32  
    33  
    34  
    35  
    36  
    37  
    38  
    39  
    40  
    41  
    42  
    43  type LineTable struct {
    44  	Data []byte
    45  	PC   uint64
    46  	Line int
    47  
    48  	
    49  	mu sync.Mutex
    50  
    51  	
    52  	version version
    53  
    54  	
    55  	binary      binary.ByteOrder
    56  	quantum     uint32
    57  	ptrsize     uint32
    58  	textStart   uint64 
    59  	funcnametab []byte
    60  	cutab       []byte
    61  	funcdata    []byte
    62  	functab     []byte
    63  	nfunctab    uint32
    64  	filetab     []byte
    65  	pctab       []byte 
    66  	nfiletab    uint32
    67  	funcNames   map[uint32]string 
    68  	strings     map[uint32]string 
    69  	
    70  	
    71  	
    72  	fileMap map[string]uint32
    73  }
    74  
    75  
    76  
    77  
    78  
    79  const oldQuantum = 1
    80  
    81  func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) {
    82  	
    83  	
    84  	
    85  	
    86  	
    87  	
    88  	
    89  	
    90  	b, pc, line = t.Data, t.PC, t.Line
    91  	for pc <= targetPC && line != targetLine && len(b) > 0 {
    92  		code := b[0]
    93  		b = b[1:]
    94  		switch {
    95  		case code == 0:
    96  			if len(b) < 4 {
    97  				b = b[0:0]
    98  				break
    99  			}
   100  			val := binary.BigEndian.Uint32(b)
   101  			b = b[4:]
   102  			line += int(val)
   103  		case code <= 64:
   104  			line += int(code)
   105  		case code <= 128:
   106  			line -= int(code - 64)
   107  		default:
   108  			pc += oldQuantum * uint64(code-128)
   109  			continue
   110  		}
   111  		pc += oldQuantum
   112  	}
   113  	return b, pc, line
   114  }
   115  
   116  func (t *LineTable) slice(pc uint64) *LineTable {
   117  	data, pc, line := t.parse(pc, -1)
   118  	return &LineTable{Data: data, PC: pc, Line: line}
   119  }
   120  
   121  
   122  
   123  
   124  func (t *LineTable) PCToLine(pc uint64) int {
   125  	if t.isGo12() {
   126  		return t.go12PCToLine(pc)
   127  	}
   128  	_, _, line := t.parse(pc, -1)
   129  	return line
   130  }
   131  
   132  
   133  
   134  
   135  
   136  func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
   137  	if t.isGo12() {
   138  		return 0
   139  	}
   140  	_, pc, line1 := t.parse(maxpc, line)
   141  	if line1 != line {
   142  		return 0
   143  	}
   144  	
   145  	return pc - oldQuantum
   146  }
   147  
   148  
   149  
   150  
   151  
   152  func NewLineTable(data []byte, text uint64) *LineTable {
   153  	return &LineTable{Data: data, PC: text, Line: 0, funcNames: make(map[uint32]string), strings: make(map[uint32]string)}
   154  }
   155  
   156  
   157  
   158  
   159  
   160  
   161  
   162  
   163  
   164  
   165  
   166  
   167  
   168  func (t *LineTable) isGo12() bool {
   169  	t.parsePclnTab()
   170  	return t.version >= ver12
   171  }
   172  
   173  const (
   174  	go12magic  = 0xfffffffb
   175  	go116magic = 0xfffffffa
   176  	go118magic = 0xfffffff0
   177  	go120magic = 0xfffffff1
   178  )
   179  
   180  
   181  
   182  func (t *LineTable) uintptr(b []byte) uint64 {
   183  	if t.ptrsize == 4 {
   184  		return uint64(t.binary.Uint32(b))
   185  	}
   186  	return t.binary.Uint64(b)
   187  }
   188  
   189  
   190  func (t *LineTable) parsePclnTab() {
   191  	t.mu.Lock()
   192  	defer t.mu.Unlock()
   193  	if t.version != verUnknown {
   194  		return
   195  	}
   196  
   197  	
   198  	
   199  	
   200  	
   201  	
   202  	t.version = ver11
   203  
   204  	if !disableRecover {
   205  		defer func() {
   206  			
   207  			recover()
   208  		}()
   209  	}
   210  
   211  	
   212  	if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 ||
   213  		(t.Data[6] != 1 && t.Data[6] != 2 && t.Data[6] != 4) || 
   214  		(t.Data[7] != 4 && t.Data[7] != 8) { 
   215  		return
   216  	}
   217  
   218  	var possibleVersion version
   219  	leMagic := binary.LittleEndian.Uint32(t.Data)
   220  	beMagic := binary.BigEndian.Uint32(t.Data)
   221  	switch {
   222  	case leMagic == go12magic:
   223  		t.binary, possibleVersion = binary.LittleEndian, ver12
   224  	case beMagic == go12magic:
   225  		t.binary, possibleVersion = binary.BigEndian, ver12
   226  	case leMagic == go116magic:
   227  		t.binary, possibleVersion = binary.LittleEndian, ver116
   228  	case beMagic == go116magic:
   229  		t.binary, possibleVersion = binary.BigEndian, ver116
   230  	case leMagic == go118magic:
   231  		t.binary, possibleVersion = binary.LittleEndian, ver118
   232  	case beMagic == go118magic:
   233  		t.binary, possibleVersion = binary.BigEndian, ver118
   234  	case leMagic == go120magic:
   235  		t.binary, possibleVersion = binary.LittleEndian, ver120
   236  	case beMagic == go120magic:
   237  		t.binary, possibleVersion = binary.BigEndian, ver120
   238  	default:
   239  		return
   240  	}
   241  	t.version = possibleVersion
   242  
   243  	
   244  	t.quantum = uint32(t.Data[6])
   245  	t.ptrsize = uint32(t.Data[7])
   246  
   247  	offset := func(word uint32) uint64 {
   248  		return t.uintptr(t.Data[8+word*t.ptrsize:])
   249  	}
   250  	data := func(word uint32) []byte {
   251  		return t.Data[offset(word):]
   252  	}
   253  
   254  	switch possibleVersion {
   255  	case ver118, ver120:
   256  		t.nfunctab = uint32(offset(0))
   257  		t.nfiletab = uint32(offset(1))
   258  		t.textStart = t.PC 
   259  		t.funcnametab = data(3)
   260  		t.cutab = data(4)
   261  		t.filetab = data(5)
   262  		t.pctab = data(6)
   263  		t.funcdata = data(7)
   264  		t.functab = data(7)
   265  		functabsize := (int(t.nfunctab)*2 + 1) * t.functabFieldSize()
   266  		t.functab = t.functab[:functabsize]
   267  	case ver116:
   268  		t.nfunctab = uint32(offset(0))
   269  		t.nfiletab = uint32(offset(1))
   270  		t.funcnametab = data(2)
   271  		t.cutab = data(3)
   272  		t.filetab = data(4)
   273  		t.pctab = data(5)
   274  		t.funcdata = data(6)
   275  		t.functab = data(6)
   276  		functabsize := (int(t.nfunctab)*2 + 1) * t.functabFieldSize()
   277  		t.functab = t.functab[:functabsize]
   278  	case ver12:
   279  		t.nfunctab = uint32(t.uintptr(t.Data[8:]))
   280  		t.funcdata = t.Data
   281  		t.funcnametab = t.Data
   282  		t.functab = t.Data[8+t.ptrsize:]
   283  		t.pctab = t.Data
   284  		functabsize := (int(t.nfunctab)*2 + 1) * t.functabFieldSize()
   285  		fileoff := t.binary.Uint32(t.functab[functabsize:])
   286  		t.functab = t.functab[:functabsize]
   287  		t.filetab = t.Data[fileoff:]
   288  		t.nfiletab = t.binary.Uint32(t.filetab)
   289  		t.filetab = t.filetab[:t.nfiletab*4]
   290  	default:
   291  		panic("unreachable")
   292  	}
   293  }
   294  
   295  
   296  func (t *LineTable) go12Funcs() []Func {
   297  	
   298  	if !disableRecover {
   299  		defer func() {
   300  			recover()
   301  		}()
   302  	}
   303  
   304  	ft := t.funcTab()
   305  	funcs := make([]Func, ft.Count())
   306  	syms := make([]Sym, len(funcs))
   307  	for i := range funcs {
   308  		f := &funcs[i]
   309  		f.Entry = ft.pc(i)
   310  		f.End = ft.pc(i + 1)
   311  		info := t.funcData(uint32(i))
   312  		f.LineTable = t
   313  		f.FrameSize = int(info.deferreturn())
   314  		syms[i] = Sym{
   315  			Value:     f.Entry,
   316  			Type:      'T',
   317  			Name:      t.funcName(info.nameOff()),
   318  			GoType:    0,
   319  			Func:      f,
   320  			goVersion: t.version,
   321  		}
   322  		f.Sym = &syms[i]
   323  	}
   324  	return funcs
   325  }
   326  
   327  
   328  func (t *LineTable) findFunc(pc uint64) funcData {
   329  	ft := t.funcTab()
   330  	if pc < ft.pc(0) || pc >= ft.pc(ft.Count()) {
   331  		return funcData{}
   332  	}
   333  	idx := sort.Search(int(t.nfunctab), func(i int) bool {
   334  		return ft.pc(i) > pc
   335  	})
   336  	idx--
   337  	return t.funcData(uint32(idx))
   338  }
   339  
   340  
   341  func (t *LineTable) readvarint(pp *[]byte) uint32 {
   342  	var v, shift uint32
   343  	p := *pp
   344  	for shift = 0; ; shift += 7 {
   345  		b := p[0]
   346  		p = p[1:]
   347  		v |= (uint32(b) & 0x7F) << shift
   348  		if b&0x80 == 0 {
   349  			break
   350  		}
   351  	}
   352  	*pp = p
   353  	return v
   354  }
   355  
   356  
   357  func (t *LineTable) funcName(off uint32) string {
   358  	if s, ok := t.funcNames[off]; ok {
   359  		return s
   360  	}
   361  	i := bytes.IndexByte(t.funcnametab[off:], 0)
   362  	s := string(t.funcnametab[off : off+uint32(i)])
   363  	t.funcNames[off] = s
   364  	return s
   365  }
   366  
   367  
   368  func (t *LineTable) stringFrom(arr []byte, off uint32) string {
   369  	if s, ok := t.strings[off]; ok {
   370  		return s
   371  	}
   372  	i := bytes.IndexByte(arr[off:], 0)
   373  	s := string(arr[off : off+uint32(i)])
   374  	t.strings[off] = s
   375  	return s
   376  }
   377  
   378  
   379  func (t *LineTable) string(off uint32) string {
   380  	return t.stringFrom(t.funcdata, off)
   381  }
   382  
   383  
   384  func (t *LineTable) functabFieldSize() int {
   385  	if t.version >= ver118 {
   386  		return 4
   387  	}
   388  	return int(t.ptrsize)
   389  }
   390  
   391  
   392  func (t *LineTable) funcTab() funcTab {
   393  	return funcTab{LineTable: t, sz: t.functabFieldSize()}
   394  }
   395  
   396  
   397  
   398  type funcTab struct {
   399  	*LineTable
   400  	sz int 
   401  }
   402  
   403  
   404  func (f funcTab) Count() int {
   405  	return int(f.nfunctab)
   406  }
   407  
   408  
   409  func (f funcTab) pc(i int) uint64 {
   410  	u := f.uint(f.functab[2*i*f.sz:])
   411  	if f.version >= ver118 {
   412  		u += f.textStart
   413  	}
   414  	return u
   415  }
   416  
   417  
   418  func (f funcTab) funcOff(i int) uint64 {
   419  	return f.uint(f.functab[(2*i+1)*f.sz:])
   420  }
   421  
   422  
   423  func (f funcTab) uint(b []byte) uint64 {
   424  	if f.sz == 4 {
   425  		return uint64(f.binary.Uint32(b))
   426  	}
   427  	return f.binary.Uint64(b)
   428  }
   429  
   430  
   431  type funcData struct {
   432  	t    *LineTable 
   433  	data []byte     
   434  }
   435  
   436  
   437  func (t *LineTable) funcData(i uint32) funcData {
   438  	data := t.funcdata[t.funcTab().funcOff(int(i)):]
   439  	return funcData{t: t, data: data}
   440  }
   441  
   442  
   443  func (f funcData) IsZero() bool {
   444  	return f.t == nil && f.data == nil
   445  }
   446  
   447  
   448  func (f *funcData) entryPC() uint64 {
   449  	
   450  	
   451  	if f.t.version >= ver118 {
   452  		
   453  		
   454  		return uint64(f.t.binary.Uint32(f.data)) + f.t.textStart
   455  	}
   456  	return f.t.uintptr(f.data)
   457  }
   458  
   459  func (f funcData) nameOff() uint32     { return f.field(1) }
   460  func (f funcData) deferreturn() uint32 { return f.field(3) }
   461  func (f funcData) pcfile() uint32      { return f.field(5) }
   462  func (f funcData) pcln() uint32        { return f.field(6) }
   463  func (f funcData) cuOffset() uint32    { return f.field(8) }
   464  
   465  
   466  
   467  
   468  func (f funcData) field(n uint32) uint32 {
   469  	if n == 0 || n > 9 {
   470  		panic("bad funcdata field")
   471  	}
   472  	
   473  	
   474  	sz0 := f.t.ptrsize
   475  	if f.t.version >= ver118 {
   476  		sz0 = 4
   477  	}
   478  	off := sz0 + (n-1)*4 
   479  	data := f.data[off:]
   480  	return f.t.binary.Uint32(data)
   481  }
   482  
   483  
   484  func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
   485  	uvdelta := t.readvarint(p)
   486  	if uvdelta == 0 && !first {
   487  		return false
   488  	}
   489  	if uvdelta&1 != 0 {
   490  		uvdelta = ^(uvdelta >> 1)
   491  	} else {
   492  		uvdelta >>= 1
   493  	}
   494  	vdelta := int32(uvdelta)
   495  	pcdelta := t.readvarint(p) * t.quantum
   496  	*pc += uint64(pcdelta)
   497  	*val += vdelta
   498  	return true
   499  }
   500  
   501  
   502  
   503  
   504  func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
   505  	p := t.pctab[off:]
   506  
   507  	val := int32(-1)
   508  	pc := entry
   509  	for t.step(&p, &pc, &val, pc == entry) {
   510  		if targetpc < pc {
   511  			return val
   512  		}
   513  	}
   514  	return -1
   515  }
   516  
   517  
   518  
   519  
   520  
   521  
   522  
   523  func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32, cutab []byte) uint64 {
   524  	if filetab == 0 || linetab == 0 {
   525  		return 0
   526  	}
   527  
   528  	fp := t.pctab[filetab:]
   529  	fl := t.pctab[linetab:]
   530  	fileVal := int32(-1)
   531  	filePC := entry
   532  	lineVal := int32(-1)
   533  	linePC := entry
   534  	fileStartPC := filePC
   535  	for t.step(&fp, &filePC, &fileVal, filePC == entry) {
   536  		fileIndex := fileVal
   537  		if t.version == ver116 || t.version == ver118 || t.version == ver120 {
   538  			fileIndex = int32(t.binary.Uint32(cutab[fileVal*4:]))
   539  		}
   540  		if fileIndex == filenum && fileStartPC < filePC {
   541  			
   542  			
   543  			
   544  			
   545  			lineStartPC := linePC
   546  			for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) {
   547  				
   548  				if lineVal == line {
   549  					if fileStartPC <= lineStartPC {
   550  						return lineStartPC
   551  					}
   552  					if fileStartPC < linePC {
   553  						return fileStartPC
   554  					}
   555  				}
   556  				lineStartPC = linePC
   557  			}
   558  		}
   559  		fileStartPC = filePC
   560  	}
   561  	return 0
   562  }
   563  
   564  
   565  func (t *LineTable) go12PCToLine(pc uint64) (line int) {
   566  	defer func() {
   567  		if !disableRecover && recover() != nil {
   568  			line = -1
   569  		}
   570  	}()
   571  
   572  	f := t.findFunc(pc)
   573  	if f.IsZero() {
   574  		return -1
   575  	}
   576  	entry := f.entryPC()
   577  	linetab := f.pcln()
   578  	return int(t.pcvalue(linetab, entry, pc))
   579  }
   580  
   581  
   582  func (t *LineTable) go12PCToFile(pc uint64) (file string) {
   583  	defer func() {
   584  		if !disableRecover && recover() != nil {
   585  			file = ""
   586  		}
   587  	}()
   588  
   589  	f := t.findFunc(pc)
   590  	if f.IsZero() {
   591  		return ""
   592  	}
   593  	entry := f.entryPC()
   594  	filetab := f.pcfile()
   595  	fno := t.pcvalue(filetab, entry, pc)
   596  	if t.version == ver12 {
   597  		if fno <= 0 {
   598  			return ""
   599  		}
   600  		return t.string(t.binary.Uint32(t.filetab[4*fno:]))
   601  	}
   602  	
   603  	if fno < 0 { 
   604  		return ""
   605  	}
   606  	cuoff := f.cuOffset()
   607  	if fnoff := t.binary.Uint32(t.cutab[(cuoff+uint32(fno))*4:]); fnoff != ^uint32(0) {
   608  		return t.stringFrom(t.filetab, fnoff)
   609  	}
   610  	return ""
   611  }
   612  
   613  
   614  func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
   615  	defer func() {
   616  		if !disableRecover && recover() != nil {
   617  			pc = 0
   618  		}
   619  	}()
   620  
   621  	t.initFileMap()
   622  	filenum, ok := t.fileMap[file]
   623  	if !ok {
   624  		return 0
   625  	}
   626  
   627  	
   628  	
   629  	
   630  	var cutab []byte
   631  	for i := uint32(0); i < t.nfunctab; i++ {
   632  		f := t.funcData(i)
   633  		entry := f.entryPC()
   634  		filetab := f.pcfile()
   635  		linetab := f.pcln()
   636  		if t.version == ver116 || t.version == ver118 || t.version == ver120 {
   637  			if f.cuOffset() == ^uint32(0) {
   638  				
   639  				continue
   640  			}
   641  			cutab = t.cutab[f.cuOffset()*4:]
   642  		}
   643  		pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line), cutab)
   644  		if pc != 0 {
   645  			return pc
   646  		}
   647  	}
   648  	return 0
   649  }
   650  
   651  
   652  func (t *LineTable) initFileMap() {
   653  	t.mu.Lock()
   654  	defer t.mu.Unlock()
   655  
   656  	if t.fileMap != nil {
   657  		return
   658  	}
   659  	m := make(map[string]uint32)
   660  
   661  	if t.version == ver12 {
   662  		for i := uint32(1); i < t.nfiletab; i++ {
   663  			s := t.string(t.binary.Uint32(t.filetab[4*i:]))
   664  			m[s] = i
   665  		}
   666  	} else {
   667  		var pos uint32
   668  		for i := uint32(0); i < t.nfiletab; i++ {
   669  			s := t.stringFrom(t.filetab, pos)
   670  			m[s] = pos
   671  			pos += uint32(len(s) + 1)
   672  		}
   673  	}
   674  	t.fileMap = m
   675  }
   676  
   677  
   678  
   679  
   680  func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) {
   681  	if !disableRecover {
   682  		defer func() {
   683  			recover()
   684  		}()
   685  	}
   686  
   687  	t.initFileMap()
   688  	for file := range t.fileMap {
   689  		m[file] = obj
   690  	}
   691  }
   692  
   693  
   694  
   695  const disableRecover = false
   696  
View as plain text