...

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

View as plain text