...

Source file src/debug/gosym/symtab.go

Documentation: debug/gosym

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

View as plain text