...

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

View as plain text