github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/go/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 // 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 // nil for Go 1.2 and later binaries 125 Objs []Obj // nil for Go 1.2 and later binaries 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 }