gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/internal/gosym/symtab.go (about)

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