github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/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 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 beginnng). 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 }