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