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