Source file src/debug/gosym/symtab.go

     1  // Copyright 2009 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 gosym implements access to the Go symbol
     6  // and line number tables embedded in Go binaries generated
     7  // by the gc compilers.
     8  package gosym
     9  
    10  import (
    11  	"bytes"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"strconv"
    15  	"strings"
    16  )
    17  
    18  /*
    19   * Symbols
    20   */
    21  
    22  // A Sym represents a single symbol table entry.
    23  type Sym struct {
    24  	Value  uint64
    25  	Type   byte
    26  	Name   string
    27  	GoType uint64
    28  	// If this symbol is a function symbol, the corresponding Func
    29  	Func *Func
    30  
    31  	goVersion version
    32  }
    33  
    34  // Static reports whether this symbol is static (not visible outside its file).
    35  func (s *Sym) Static() bool { return s.Type >= 'a' }
    36  
    37  // nameWithoutInst returns s.Name if s.Name has no brackets (does not reference an
    38  // instantiated type, function, or method). If s.Name contains brackets, then it
    39  // returns s.Name with all the contents between (and including) the outermost left
    40  // and right bracket removed. This is useful to ignore any extra slashes or dots
    41  // inside the brackets from the string searches below, where needed.
    42  func (s *Sym) nameWithoutInst() string {
    43  	start := strings.Index(s.Name, "[")
    44  	if start < 0 {
    45  		return s.Name
    46  	}
    47  	end := strings.LastIndex(s.Name, "]")
    48  	if end < 0 {
    49  		// Malformed name, should contain closing bracket too.
    50  		return s.Name
    51  	}
    52  	return s.Name[0:start] + s.Name[end+1:]
    53  }
    54  
    55  // PackageName returns the package part of the symbol name,
    56  // or the empty string if there is none.
    57  func (s *Sym) PackageName() string {
    58  	name := s.nameWithoutInst()
    59  
    60  	// Since go1.20, a prefix of "type:" and "go:" is a compiler-generated symbol,
    61  	// they do not belong to any package.
    62  	//
    63  	// See cmd/compile/internal/base/link.go:ReservedImports variable.
    64  	if s.goVersion >= ver120 && (strings.HasPrefix(name, "go:") || strings.HasPrefix(name, "type:")) {
    65  		return ""
    66  	}
    67  
    68  	// For go1.18 and below, the prefix are "type." and "go." instead.
    69  	if s.goVersion <= ver118 && (strings.HasPrefix(name, "go.") || strings.HasPrefix(name, "type.")) {
    70  		return ""
    71  	}
    72  
    73  	pathend := strings.LastIndex(name, "/")
    74  	if pathend < 0 {
    75  		pathend = 0
    76  	}
    77  
    78  	if i := strings.Index(name[pathend:], "."); i != -1 {
    79  		return name[:pathend+i]
    80  	}
    81  	return ""
    82  }
    83  
    84  // ReceiverName returns the receiver type name of this symbol,
    85  // or the empty string if there is none.  A receiver name is only detected in
    86  // the case that s.Name is fully-specified with a package name.
    87  func (s *Sym) ReceiverName() string {
    88  	name := s.nameWithoutInst()
    89  	// If we find a slash in name, it should precede any bracketed expression
    90  	// that was removed, so pathend will apply correctly to name and s.Name.
    91  	pathend := strings.LastIndex(name, "/")
    92  	if pathend < 0 {
    93  		pathend = 0
    94  	}
    95  	// Find the first dot after pathend (or from the beginning, if there was
    96  	// no slash in name).
    97  	l := strings.Index(name[pathend:], ".")
    98  	// Find the last dot after pathend (or the beginning).
    99  	r := strings.LastIndex(name[pathend:], ".")
   100  	if l == -1 || r == -1 || l == r {
   101  		// There is no receiver if we didn't find two distinct dots after pathend.
   102  		return ""
   103  	}
   104  	// Given there is a trailing '.' that is in name, find it now in s.Name.
   105  	// pathend+l should apply to s.Name, because it should be the dot in the
   106  	// package name.
   107  	r = strings.LastIndex(s.Name[pathend:], ".")
   108  	return s.Name[pathend+l+1 : pathend+r]
   109  }
   110  
   111  // BaseName returns the symbol name without the package or receiver name.
   112  func (s *Sym) BaseName() string {
   113  	name := s.nameWithoutInst()
   114  	if i := strings.LastIndex(name, "."); i != -1 {
   115  		if s.Name != name {
   116  			brack := strings.Index(s.Name, "[")
   117  			if i > brack {
   118  				// BaseName is a method name after the brackets, so
   119  				// recalculate for s.Name. Otherwise, i applies
   120  				// correctly to s.Name, since it is before the
   121  				// brackets.
   122  				i = strings.LastIndex(s.Name, ".")
   123  			}
   124  		}
   125  		return s.Name[i+1:]
   126  	}
   127  	return s.Name
   128  }
   129  
   130  // A Func collects information about a single function.
   131  type Func struct {
   132  	Entry uint64
   133  	*Sym
   134  	End       uint64
   135  	Params    []*Sym // nil for Go 1.3 and later binaries
   136  	Locals    []*Sym // nil for Go 1.3 and later binaries
   137  	FrameSize int
   138  	LineTable *LineTable
   139  	Obj       *Obj
   140  }
   141  
   142  // An Obj represents a collection of functions in a symbol table.
   143  //
   144  // The exact method of division of a binary into separate Objs is an internal detail
   145  // of the symbol table format.
   146  //
   147  // In early versions of Go each source file became a different Obj.
   148  //
   149  // In Go 1 and Go 1.1, each package produced one Obj for all Go sources
   150  // and one Obj per C source file.
   151  //
   152  // In Go 1.2, there is a single Obj for the entire program.
   153  type Obj struct {
   154  	// Funcs is a list of functions in the Obj.
   155  	Funcs []Func
   156  
   157  	// In Go 1.1 and earlier, Paths is a list of symbols corresponding
   158  	// to the source file names that produced the Obj.
   159  	// In Go 1.2, Paths is nil.
   160  	// Use the keys of Table.Files to obtain a list of source files.
   161  	Paths []Sym // meta
   162  }
   163  
   164  /*
   165   * Symbol tables
   166   */
   167  
   168  // Table represents a Go symbol table. It stores all of the
   169  // symbols decoded from the program and provides methods to translate
   170  // between symbols, names, and addresses.
   171  type Table struct {
   172  	Syms  []Sym // nil for Go 1.3 and later binaries
   173  	Funcs []Func
   174  	Files map[string]*Obj // for Go 1.2 and later all files map to one Obj
   175  	Objs  []Obj           // for Go 1.2 and later only one Obj in slice
   176  
   177  	go12line *LineTable // Go 1.2 line number table
   178  }
   179  
   180  type sym struct {
   181  	value  uint64
   182  	gotype uint64
   183  	typ    byte
   184  	name   []byte
   185  }
   186  
   187  var (
   188  	littleEndianSymtab    = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
   189  	bigEndianSymtab       = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
   190  	oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
   191  )
   192  
   193  func walksymtab(data []byte, fn func(sym) error) error {
   194  	if len(data) == 0 { // missing symtab is okay
   195  		return nil
   196  	}
   197  	var order binary.ByteOrder = binary.BigEndian
   198  	newTable := false
   199  	switch {
   200  	case bytes.HasPrefix(data, oldLittleEndianSymtab):
   201  		// Same as Go 1.0, but little endian.
   202  		// Format was used during interim development between Go 1.0 and Go 1.1.
   203  		// Should not be widespread, but easy to support.
   204  		data = data[6:]
   205  		order = binary.LittleEndian
   206  	case bytes.HasPrefix(data, bigEndianSymtab):
   207  		newTable = true
   208  	case bytes.HasPrefix(data, littleEndianSymtab):
   209  		newTable = true
   210  		order = binary.LittleEndian
   211  	}
   212  	var ptrsz int
   213  	if newTable {
   214  		if len(data) < 8 {
   215  			return &DecodingError{len(data), "unexpected EOF", nil}
   216  		}
   217  		ptrsz = int(data[7])
   218  		if ptrsz != 4 && ptrsz != 8 {
   219  			return &DecodingError{7, "invalid pointer size", ptrsz}
   220  		}
   221  		data = data[8:]
   222  	}
   223  	var s sym
   224  	p := data
   225  	for len(p) >= 4 {
   226  		var typ byte
   227  		if newTable {
   228  			// Symbol type, value, Go type.
   229  			typ = p[0] & 0x3F
   230  			wideValue := p[0]&0x40 != 0
   231  			goType := p[0]&0x80 != 0
   232  			if typ < 26 {
   233  				typ += 'A'
   234  			} else {
   235  				typ += 'a' - 26
   236  			}
   237  			s.typ = typ
   238  			p = p[1:]
   239  			if wideValue {
   240  				if len(p) < ptrsz {
   241  					return &DecodingError{len(data), "unexpected EOF", nil}
   242  				}
   243  				// fixed-width value
   244  				if ptrsz == 8 {
   245  					s.value = order.Uint64(p[0:8])
   246  					p = p[8:]
   247  				} else {
   248  					s.value = uint64(order.Uint32(p[0:4]))
   249  					p = p[4:]
   250  				}
   251  			} else {
   252  				// varint value
   253  				s.value = 0
   254  				shift := uint(0)
   255  				for len(p) > 0 && p[0]&0x80 != 0 {
   256  					s.value |= uint64(p[0]&0x7F) << shift
   257  					shift += 7
   258  					p = p[1:]
   259  				}
   260  				if len(p) == 0 {
   261  					return &DecodingError{len(data), "unexpected EOF", nil}
   262  				}
   263  				s.value |= uint64(p[0]) << shift
   264  				p = p[1:]
   265  			}
   266  			if goType {
   267  				if len(p) < ptrsz {
   268  					return &DecodingError{len(data), "unexpected EOF", nil}
   269  				}
   270  				// fixed-width go type
   271  				if ptrsz == 8 {
   272  					s.gotype = order.Uint64(p[0:8])
   273  					p = p[8:]
   274  				} else {
   275  					s.gotype = uint64(order.Uint32(p[0:4]))
   276  					p = p[4:]
   277  				}
   278  			}
   279  		} else {
   280  			// Value, symbol type.
   281  			s.value = uint64(order.Uint32(p[0:4]))
   282  			if len(p) < 5 {
   283  				return &DecodingError{len(data), "unexpected EOF", nil}
   284  			}
   285  			typ = p[4]
   286  			if typ&0x80 == 0 {
   287  				return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
   288  			}
   289  			typ &^= 0x80
   290  			s.typ = typ
   291  			p = p[5:]
   292  		}
   293  
   294  		// Name.
   295  		var i int
   296  		var nnul int
   297  		for i = 0; i < len(p); i++ {
   298  			if p[i] == 0 {
   299  				nnul = 1
   300  				break
   301  			}
   302  		}
   303  		switch typ {
   304  		case 'z', 'Z':
   305  			p = p[i+nnul:]
   306  			for i = 0; i+2 <= len(p); i += 2 {
   307  				if p[i] == 0 && p[i+1] == 0 {
   308  					nnul = 2
   309  					break
   310  				}
   311  			}
   312  		}
   313  		if len(p) < i+nnul {
   314  			return &DecodingError{len(data), "unexpected EOF", nil}
   315  		}
   316  		s.name = p[0:i]
   317  		i += nnul
   318  		p = p[i:]
   319  
   320  		if !newTable {
   321  			if len(p) < 4 {
   322  				return &DecodingError{len(data), "unexpected EOF", nil}
   323  			}
   324  			// Go type.
   325  			s.gotype = uint64(order.Uint32(p[:4]))
   326  			p = p[4:]
   327  		}
   328  		fn(s)
   329  	}
   330  	return nil
   331  }
   332  
   333  // NewTable decodes the Go symbol table (the ".gosymtab" section in ELF),
   334  // returning an in-memory representation.
   335  // Starting with Go 1.3, the Go symbol table no longer includes symbol data;
   336  // callers should pass nil for the symtab parameter.
   337  func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
   338  	var n int
   339  	err := walksymtab(symtab, func(s sym) error {
   340  		n++
   341  		return nil
   342  	})
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  
   347  	var t Table
   348  	if pcln.isGo12() {
   349  		t.go12line = pcln
   350  	}
   351  	fname := make(map[uint16]string)
   352  	t.Syms = make([]Sym, 0, n)
   353  	nf := 0
   354  	nz := 0
   355  	lasttyp := uint8(0)
   356  	err = walksymtab(symtab, func(s sym) error {
   357  		n := len(t.Syms)
   358  		t.Syms = t.Syms[0 : n+1]
   359  		ts := &t.Syms[n]
   360  		ts.Type = s.typ
   361  		ts.Value = s.value
   362  		ts.GoType = s.gotype
   363  		ts.goVersion = pcln.version
   364  		switch s.typ {
   365  		default:
   366  			// rewrite name to use . instead of ยท (c2 b7)
   367  			w := 0
   368  			b := s.name
   369  			for i := 0; i < len(b); i++ {
   370  				if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {
   371  					i++
   372  					b[i] = '.'
   373  				}
   374  				b[w] = b[i]
   375  				w++
   376  			}
   377  			ts.Name = string(s.name[0:w])
   378  		case 'z', 'Z':
   379  			if lasttyp != 'z' && lasttyp != 'Z' {
   380  				nz++
   381  			}
   382  			for i := 0; i < len(s.name); i += 2 {
   383  				eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
   384  				elt, ok := fname[eltIdx]
   385  				if !ok {
   386  					return &DecodingError{-1, "bad filename code", eltIdx}
   387  				}
   388  				if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
   389  					ts.Name += "/"
   390  				}
   391  				ts.Name += elt
   392  			}
   393  		}
   394  		switch s.typ {
   395  		case 'T', 't', 'L', 'l':
   396  			nf++
   397  		case 'f':
   398  			fname[uint16(s.value)] = ts.Name
   399  		}
   400  		lasttyp = s.typ
   401  		return nil
   402  	})
   403  	if err != nil {
   404  		return nil, err
   405  	}
   406  
   407  	t.Funcs = make([]Func, 0, nf)
   408  	t.Files = make(map[string]*Obj)
   409  
   410  	var obj *Obj
   411  	if t.go12line != nil {
   412  		// Put all functions into one Obj.
   413  		t.Objs = make([]Obj, 1)
   414  		obj = &t.Objs[0]
   415  		t.go12line.go12MapFiles(t.Files, obj)
   416  	} else {
   417  		t.Objs = make([]Obj, 0, nz)
   418  	}
   419  
   420  	// Count text symbols and attach frame sizes, parameters, and
   421  	// locals to them. Also, find object file boundaries.
   422  	lastf := 0
   423  	for i := 0; i < len(t.Syms); i++ {
   424  		sym := &t.Syms[i]
   425  		switch sym.Type {
   426  		case 'Z', 'z': // path symbol
   427  			if t.go12line != nil {
   428  				// Go 1.2 binaries have the file information elsewhere. Ignore.
   429  				break
   430  			}
   431  			// Finish the current object
   432  			if obj != nil {
   433  				obj.Funcs = t.Funcs[lastf:]
   434  			}
   435  			lastf = len(t.Funcs)
   436  
   437  			// Start new object
   438  			n := len(t.Objs)
   439  			t.Objs = t.Objs[0 : n+1]
   440  			obj = &t.Objs[n]
   441  
   442  			// Count & copy path symbols
   443  			var end int
   444  			for end = i + 1; end < len(t.Syms); end++ {
   445  				if c := t.Syms[end].Type; c != 'Z' && c != 'z' {
   446  					break
   447  				}
   448  			}
   449  			obj.Paths = t.Syms[i:end]
   450  			i = end - 1 // loop will i++
   451  
   452  			// Record file names
   453  			depth := 0
   454  			for j := range obj.Paths {
   455  				s := &obj.Paths[j]
   456  				if s.Name == "" {
   457  					depth--
   458  				} else {
   459  					if depth == 0 {
   460  						t.Files[s.Name] = obj
   461  					}
   462  					depth++
   463  				}
   464  			}
   465  
   466  		case 'T', 't', 'L', 'l': // text symbol
   467  			if n := len(t.Funcs); n > 0 {
   468  				t.Funcs[n-1].End = sym.Value
   469  			}
   470  			if sym.Name == "runtime.etext" || sym.Name == "etext" {
   471  				continue
   472  			}
   473  
   474  			// Count parameter and local (auto) syms
   475  			var np, na int
   476  			var end int
   477  		countloop:
   478  			for end = i + 1; end < len(t.Syms); end++ {
   479  				switch t.Syms[end].Type {
   480  				case 'T', 't', 'L', 'l', 'Z', 'z':
   481  					break countloop
   482  				case 'p':
   483  					np++
   484  				case 'a':
   485  					na++
   486  				}
   487  			}
   488  
   489  			// Fill in the function symbol
   490  			n := len(t.Funcs)
   491  			t.Funcs = t.Funcs[0 : n+1]
   492  			fn := &t.Funcs[n]
   493  			sym.Func = fn
   494  			fn.Params = make([]*Sym, 0, np)
   495  			fn.Locals = make([]*Sym, 0, na)
   496  			fn.Sym = sym
   497  			fn.Entry = sym.Value
   498  			fn.Obj = obj
   499  			if t.go12line != nil {
   500  				// All functions share the same line table.
   501  				// It knows how to narrow down to a specific
   502  				// function quickly.
   503  				fn.LineTable = t.go12line
   504  			} else if pcln != nil {
   505  				fn.LineTable = pcln.slice(fn.Entry)
   506  				pcln = fn.LineTable
   507  			}
   508  			for j := i; j < end; j++ {
   509  				s := &t.Syms[j]
   510  				switch s.Type {
   511  				case 'm':
   512  					fn.FrameSize = int(s.Value)
   513  				case 'p':
   514  					n := len(fn.Params)
   515  					fn.Params = fn.Params[0 : n+1]
   516  					fn.Params[n] = s
   517  				case 'a':
   518  					n := len(fn.Locals)
   519  					fn.Locals = fn.Locals[0 : n+1]
   520  					fn.Locals[n] = s
   521  				}
   522  			}
   523  			i = end - 1 // loop will i++
   524  		}
   525  	}
   526  
   527  	if t.go12line != nil && nf == 0 {
   528  		t.Funcs = t.go12line.go12Funcs()
   529  	}
   530  	if obj != nil {
   531  		obj.Funcs = t.Funcs[lastf:]
   532  	}
   533  	return &t, nil
   534  }
   535  
   536  // PCToFunc returns the function containing the program counter pc,
   537  // or nil if there is no such function.
   538  func (t *Table) PCToFunc(pc uint64) *Func {
   539  	funcs := t.Funcs
   540  	for len(funcs) > 0 {
   541  		m := len(funcs) / 2
   542  		fn := &funcs[m]
   543  		switch {
   544  		case pc < fn.Entry:
   545  			funcs = funcs[0:m]
   546  		case fn.Entry <= pc && pc < fn.End:
   547  			return fn
   548  		default:
   549  			funcs = funcs[m+1:]
   550  		}
   551  	}
   552  	return nil
   553  }
   554  
   555  // PCToLine looks up line number information for a program counter.
   556  // If there is no information, it returns fn == nil.
   557  func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
   558  	if fn = t.PCToFunc(pc); fn == nil {
   559  		return
   560  	}
   561  	if t.go12line != nil {
   562  		file = t.go12line.go12PCToFile(pc)
   563  		line = t.go12line.go12PCToLine(pc)
   564  	} else {
   565  		file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
   566  	}
   567  	return
   568  }
   569  
   570  // LineToPC looks up the first program counter on the given line in
   571  // the named file. It returns [UnknownFileError] or [UnknownLineError] if
   572  // there is an error looking up this line.
   573  func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) {
   574  	obj, ok := t.Files[file]
   575  	if !ok {
   576  		return 0, nil, UnknownFileError(file)
   577  	}
   578  
   579  	if t.go12line != nil {
   580  		pc := t.go12line.go12LineToPC(file, line)
   581  		if pc == 0 {
   582  			return 0, nil, &UnknownLineError{file, line}
   583  		}
   584  		return pc, t.PCToFunc(pc), nil
   585  	}
   586  
   587  	abs, err := obj.alineFromLine(file, line)
   588  	if err != nil {
   589  		return
   590  	}
   591  	for i := range obj.Funcs {
   592  		f := &obj.Funcs[i]
   593  		pc := f.LineTable.LineToPC(abs, f.End)
   594  		if pc != 0 {
   595  			return pc, f, nil
   596  		}
   597  	}
   598  	return 0, nil, &UnknownLineError{file, line}
   599  }
   600  
   601  // LookupSym returns the text, data, or bss symbol with the given name,
   602  // or nil if no such symbol is found.
   603  func (t *Table) LookupSym(name string) *Sym {
   604  	// TODO(austin) Maybe make a map
   605  	for i := range t.Syms {
   606  		s := &t.Syms[i]
   607  		switch s.Type {
   608  		case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
   609  			if s.Name == name {
   610  				return s
   611  			}
   612  		}
   613  	}
   614  	return nil
   615  }
   616  
   617  // LookupFunc returns the text, data, or bss symbol with the given name,
   618  // or nil if no such symbol is found.
   619  func (t *Table) LookupFunc(name string) *Func {
   620  	for i := range t.Funcs {
   621  		f := &t.Funcs[i]
   622  		if f.Sym.Name == name {
   623  			return f
   624  		}
   625  	}
   626  	return nil
   627  }
   628  
   629  // SymByAddr returns the text, data, or bss symbol starting at the given address.
   630  func (t *Table) SymByAddr(addr uint64) *Sym {
   631  	for i := range t.Syms {
   632  		s := &t.Syms[i]
   633  		switch s.Type {
   634  		case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
   635  			if s.Value == addr {
   636  				return s
   637  			}
   638  		}
   639  	}
   640  	return nil
   641  }
   642  
   643  /*
   644   * Object files
   645   */
   646  
   647  // This is legacy code for Go 1.1 and earlier, which used the
   648  // Plan 9 format for pc-line tables. This code was never quite
   649  // correct. It's probably very close, and it's usually correct, but
   650  // we never quite found all the corner cases.
   651  //
   652  // Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab.
   653  
   654  func (o *Obj) lineFromAline(aline int) (string, int) {
   655  	type stackEnt struct {
   656  		path   string
   657  		start  int
   658  		offset int
   659  		prev   *stackEnt
   660  	}
   661  
   662  	noPath := &stackEnt{"", 0, 0, nil}
   663  	tos := noPath
   664  
   665  pathloop:
   666  	for _, s := range o.Paths {
   667  		val := int(s.Value)
   668  		switch {
   669  		case val > aline:
   670  			break pathloop
   671  
   672  		case val == 1:
   673  			// Start a new stack
   674  			tos = &stackEnt{s.Name, val, 0, noPath}
   675  
   676  		case s.Name == "":
   677  			// Pop
   678  			if tos == noPath {
   679  				return "<malformed symbol table>", 0
   680  			}
   681  			tos.prev.offset += val - tos.start
   682  			tos = tos.prev
   683  
   684  		default:
   685  			// Push
   686  			tos = &stackEnt{s.Name, val, 0, tos}
   687  		}
   688  	}
   689  
   690  	if tos == noPath {
   691  		return "", 0
   692  	}
   693  	return tos.path, aline - tos.start - tos.offset + 1
   694  }
   695  
   696  func (o *Obj) alineFromLine(path string, line int) (int, error) {
   697  	if line < 1 {
   698  		return 0, &UnknownLineError{path, line}
   699  	}
   700  
   701  	for i, s := range o.Paths {
   702  		// Find this path
   703  		if s.Name != path {
   704  			continue
   705  		}
   706  
   707  		// Find this line at this stack level
   708  		depth := 0
   709  		var incstart int
   710  		line += int(s.Value)
   711  	pathloop:
   712  		for _, s := range o.Paths[i:] {
   713  			val := int(s.Value)
   714  			switch {
   715  			case depth == 1 && val >= line:
   716  				return line - 1, nil
   717  
   718  			case s.Name == "":
   719  				depth--
   720  				if depth == 0 {
   721  					break pathloop
   722  				} else if depth == 1 {
   723  					line += val - incstart
   724  				}
   725  
   726  			default:
   727  				if depth == 1 {
   728  					incstart = val
   729  				}
   730  				depth++
   731  			}
   732  		}
   733  		return 0, &UnknownLineError{path, line}
   734  	}
   735  	return 0, UnknownFileError(path)
   736  }
   737  
   738  /*
   739   * Errors
   740   */
   741  
   742  // UnknownFileError represents a failure to find the specific file in
   743  // the symbol table.
   744  type UnknownFileError string
   745  
   746  func (e UnknownFileError) Error() string { return "unknown file: " + string(e) }
   747  
   748  // UnknownLineError represents a failure to map a line to a program
   749  // counter, either because the line is beyond the bounds of the file
   750  // or because there is no code on the given line.
   751  type UnknownLineError struct {
   752  	File string
   753  	Line int
   754  }
   755  
   756  func (e *UnknownLineError) Error() string {
   757  	return "no code at " + e.File + ":" + strconv.Itoa(e.Line)
   758  }
   759  
   760  // DecodingError represents an error during the decoding of
   761  // the symbol table.
   762  type DecodingError struct {
   763  	off int
   764  	msg string
   765  	val any
   766  }
   767  
   768  func (e *DecodingError) Error() string {
   769  	msg := e.msg
   770  	if e.val != nil {
   771  		msg += fmt.Sprintf(" '%v'", e.val)
   772  	}
   773  	msg += fmt.Sprintf(" at byte %#x", e.off)
   774  	return msg
   775  }
   776  

View as plain text