src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

symtab.go (18365B)


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