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