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 }