github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/debug/dwarf/line.go (about) 1 // Copyright 2015 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 dwarf 6 7 import ( 8 "errors" 9 "fmt" 10 "io" 11 "path" 12 "strings" 13 ) 14 15 // A LineReader reads a sequence of LineEntry structures from a DWARF 16 // "line" section for a single compilation unit. LineEntries occur in 17 // order of increasing PC and each LineEntry gives metadata for the 18 // instructions from that LineEntry's PC to just before the next 19 // LineEntry's PC. The last entry will have its EndSequence field set. 20 type LineReader struct { 21 buf buf 22 23 // Original .debug_line section data. Used by Seek. 24 section []byte 25 26 str []byte // .debug_str 27 lineStr []byte // .debug_line_str 28 29 // Header information 30 version uint16 31 addrsize int 32 segmentSelectorSize int 33 minInstructionLength int 34 maxOpsPerInstruction int 35 defaultIsStmt bool 36 lineBase int 37 lineRange int 38 opcodeBase int 39 opcodeLengths []int 40 directories []string 41 fileEntries []*LineFile 42 43 programOffset Offset // section offset of line number program 44 endOffset Offset // section offset of byte following program 45 46 initialFileEntries int // initial length of fileEntries 47 48 // Current line number program state machine registers 49 state LineEntry // public state 50 fileIndex int // private state 51 } 52 53 // A LineEntry is a row in a DWARF line table. 54 type LineEntry struct { 55 // Address is the program-counter value of a machine 56 // instruction generated by the compiler. This LineEntry 57 // applies to each instruction from Address to just before the 58 // Address of the next LineEntry. 59 Address uint64 60 61 // OpIndex is the index of an operation within a VLIW 62 // instruction. The index of the first operation is 0. For 63 // non-VLIW architectures, it will always be 0. Address and 64 // OpIndex together form an operation pointer that can 65 // reference any individual operation within the instruction 66 // stream. 67 OpIndex int 68 69 // File is the source file corresponding to these 70 // instructions. 71 File *LineFile 72 73 // Line is the source code line number corresponding to these 74 // instructions. Lines are numbered beginning at 1. It may be 75 // 0 if these instructions cannot be attributed to any source 76 // line. 77 Line int 78 79 // Column is the column number within the source line of these 80 // instructions. Columns are numbered beginning at 1. It may 81 // be 0 to indicate the "left edge" of the line. 82 Column int 83 84 // IsStmt indicates that Address is a recommended breakpoint 85 // location, such as the beginning of a line, statement, or a 86 // distinct subpart of a statement. 87 IsStmt bool 88 89 // BasicBlock indicates that Address is the beginning of a 90 // basic block. 91 BasicBlock bool 92 93 // PrologueEnd indicates that Address is one (of possibly 94 // many) PCs where execution should be suspended for a 95 // breakpoint on entry to the containing function. 96 // 97 // Added in DWARF 3. 98 PrologueEnd bool 99 100 // EpilogueBegin indicates that Address is one (of possibly 101 // many) PCs where execution should be suspended for a 102 // breakpoint on exit from this function. 103 // 104 // Added in DWARF 3. 105 EpilogueBegin bool 106 107 // ISA is the instruction set architecture for these 108 // instructions. Possible ISA values should be defined by the 109 // applicable ABI specification. 110 // 111 // Added in DWARF 3. 112 ISA int 113 114 // Discriminator is an arbitrary integer indicating the block 115 // to which these instructions belong. It serves to 116 // distinguish among multiple blocks that may all have with 117 // the same source file, line, and column. Where only one 118 // block exists for a given source position, it should be 0. 119 // 120 // Added in DWARF 3. 121 Discriminator int 122 123 // EndSequence indicates that Address is the first byte after 124 // the end of a sequence of target machine instructions. If it 125 // is set, only this and the Address field are meaningful. A 126 // line number table may contain information for multiple 127 // potentially disjoint instruction sequences. The last entry 128 // in a line table should always have EndSequence set. 129 EndSequence bool 130 } 131 132 // A LineFile is a source file referenced by a DWARF line table entry. 133 type LineFile struct { 134 Name string 135 Mtime uint64 // Implementation defined modification time, or 0 if unknown 136 Length int // File length, or 0 if unknown 137 } 138 139 // LineReader returns a new reader for the line table of compilation 140 // unit cu, which must be an Entry with tag TagCompileUnit. 141 // 142 // If this compilation unit has no line table, it returns nil, nil. 143 func (d *Data) LineReader(cu *Entry) (*LineReader, error) { 144 if d.line == nil { 145 // No line tables available. 146 return nil, nil 147 } 148 149 // Get line table information from cu. 150 off, ok := cu.Val(AttrStmtList).(int64) 151 if !ok { 152 // cu has no line table. 153 return nil, nil 154 } 155 if off > int64(len(d.line)) { 156 return nil, errors.New("AttrStmtList value out of range") 157 } 158 // AttrCompDir is optional if all file names are absolute. Use 159 // the empty string if it's not present. 160 compDir, _ := cu.Val(AttrCompDir).(string) 161 162 // Create the LineReader. 163 u := &d.unit[d.offsetToUnit(cu.Offset)] 164 buf := makeBuf(d, u, "line", Offset(off), d.line[off:]) 165 // The compilation directory is implicitly directories[0]. 166 r := LineReader{ 167 buf: buf, 168 section: d.line, 169 str: d.str, 170 lineStr: d.lineStr, 171 } 172 173 // Read the header. 174 if err := r.readHeader(compDir); err != nil { 175 return nil, err 176 } 177 178 // Initialize line reader state. 179 r.Reset() 180 181 return &r, nil 182 } 183 184 // readHeader reads the line number program header from r.buf and sets 185 // all of the header fields in r. 186 func (r *LineReader) readHeader(compDir string) error { 187 buf := &r.buf 188 189 // Read basic header fields [DWARF2 6.2.4]. 190 hdrOffset := buf.off 191 unitLength, dwarf64 := buf.unitLength() 192 r.endOffset = buf.off + unitLength 193 if r.endOffset > buf.off+Offset(len(buf.data)) { 194 return DecodeError{"line", hdrOffset, fmt.Sprintf("line table end %d exceeds section size %d", r.endOffset, buf.off+Offset(len(buf.data)))} 195 } 196 r.version = buf.uint16() 197 if buf.err == nil && (r.version < 2 || r.version > 5) { 198 // DWARF goes to all this effort to make new opcodes 199 // backward-compatible, and then adds fields right in 200 // the middle of the header in new versions, so we're 201 // picky about only supporting known line table 202 // versions. 203 return DecodeError{"line", hdrOffset, fmt.Sprintf("unknown line table version %d", r.version)} 204 } 205 if r.version >= 5 { 206 r.addrsize = int(buf.uint8()) 207 r.segmentSelectorSize = int(buf.uint8()) 208 } else { 209 r.addrsize = buf.format.addrsize() 210 r.segmentSelectorSize = 0 211 } 212 var headerLength Offset 213 if dwarf64 { 214 headerLength = Offset(buf.uint64()) 215 } else { 216 headerLength = Offset(buf.uint32()) 217 } 218 r.programOffset = buf.off + headerLength 219 r.minInstructionLength = int(buf.uint8()) 220 if r.version >= 4 { 221 // [DWARF4 6.2.4] 222 r.maxOpsPerInstruction = int(buf.uint8()) 223 } else { 224 r.maxOpsPerInstruction = 1 225 } 226 r.defaultIsStmt = buf.uint8() != 0 227 r.lineBase = int(int8(buf.uint8())) 228 r.lineRange = int(buf.uint8()) 229 230 // Validate header. 231 if buf.err != nil { 232 return buf.err 233 } 234 if r.maxOpsPerInstruction == 0 { 235 return DecodeError{"line", hdrOffset, "invalid maximum operations per instruction: 0"} 236 } 237 if r.lineRange == 0 { 238 return DecodeError{"line", hdrOffset, "invalid line range: 0"} 239 } 240 241 // Read standard opcode length table. This table starts with opcode 1. 242 r.opcodeBase = int(buf.uint8()) 243 r.opcodeLengths = make([]int, r.opcodeBase) 244 for i := 1; i < r.opcodeBase; i++ { 245 r.opcodeLengths[i] = int(buf.uint8()) 246 } 247 248 // Validate opcode lengths. 249 if buf.err != nil { 250 return buf.err 251 } 252 for i, length := range r.opcodeLengths { 253 if known, ok := knownOpcodeLengths[i]; ok && known != length { 254 return DecodeError{"line", hdrOffset, fmt.Sprintf("opcode %d expected to have length %d, but has length %d", i, known, length)} 255 } 256 } 257 258 if r.version < 5 { 259 // Read include directories table. 260 r.directories = []string{compDir} 261 for { 262 directory := buf.string() 263 if buf.err != nil { 264 return buf.err 265 } 266 if len(directory) == 0 { 267 break 268 } 269 if !pathIsAbs(directory) { 270 // Relative paths are implicitly relative to 271 // the compilation directory. 272 directory = pathJoin(compDir, directory) 273 } 274 r.directories = append(r.directories, directory) 275 } 276 277 // Read file name list. File numbering starts with 1, 278 // so leave the first entry nil. 279 r.fileEntries = make([]*LineFile, 1) 280 for { 281 if done, err := r.readFileEntry(); err != nil { 282 return err 283 } else if done { 284 break 285 } 286 } 287 } else { 288 dirFormat := r.readLNCTFormat() 289 c := buf.uint() 290 r.directories = make([]string, c) 291 for i := range r.directories { 292 dir, _, _, err := r.readLNCT(dirFormat, dwarf64) 293 if err != nil { 294 return err 295 } 296 r.directories[i] = dir 297 } 298 fileFormat := r.readLNCTFormat() 299 c = buf.uint() 300 r.fileEntries = make([]*LineFile, c) 301 for i := range r.fileEntries { 302 name, mtime, size, err := r.readLNCT(fileFormat, dwarf64) 303 if err != nil { 304 return err 305 } 306 r.fileEntries[i] = &LineFile{name, mtime, int(size)} 307 } 308 } 309 310 r.initialFileEntries = len(r.fileEntries) 311 312 return buf.err 313 } 314 315 // lnctForm is a pair of an LNCT code and a form. This represents an 316 // entry in the directory name or file name description in the DWARF 5 317 // line number program header. 318 type lnctForm struct { 319 lnct int 320 form format 321 } 322 323 // readLNCTFormat reads an LNCT format description. 324 func (r *LineReader) readLNCTFormat() []lnctForm { 325 c := r.buf.uint8() 326 ret := make([]lnctForm, c) 327 for i := range ret { 328 ret[i].lnct = int(r.buf.uint()) 329 ret[i].form = format(r.buf.uint()) 330 } 331 return ret 332 } 333 334 // readLNCT reads a sequence of LNCT entries and returns path information. 335 func (r *LineReader) readLNCT(s []lnctForm, dwarf64 bool) (path string, mtime uint64, size uint64, err error) { 336 var dir string 337 for _, lf := range s { 338 var str string 339 var val uint64 340 switch lf.form { 341 case formString: 342 str = r.buf.string() 343 case formStrp, formLineStrp: 344 var off uint64 345 if dwarf64 { 346 off = r.buf.uint64() 347 } else { 348 off = uint64(r.buf.uint32()) 349 } 350 if uint64(int(off)) != off { 351 return "", 0, 0, DecodeError{"line", r.buf.off, "strp/line_strp offset out of range"} 352 } 353 var b1 buf 354 if lf.form == formStrp { 355 b1 = makeBuf(r.buf.dwarf, r.buf.format, "str", 0, r.str) 356 } else { 357 b1 = makeBuf(r.buf.dwarf, r.buf.format, "line_str", 0, r.lineStr) 358 } 359 b1.skip(int(off)) 360 str = b1.string() 361 if b1.err != nil { 362 return "", 0, 0, DecodeError{"line", r.buf.off, b1.err.Error()} 363 } 364 case formStrpSup: 365 // Supplemental sections not yet supported. 366 if dwarf64 { 367 r.buf.uint64() 368 } else { 369 r.buf.uint32() 370 } 371 case formStrx: 372 // .debug_line.dwo sections not yet supported. 373 r.buf.uint() 374 case formStrx1: 375 r.buf.uint8() 376 case formStrx2: 377 r.buf.uint16() 378 case formStrx3: 379 r.buf.uint24() 380 case formStrx4: 381 r.buf.uint32() 382 case formData1: 383 val = uint64(r.buf.uint8()) 384 case formData2: 385 val = uint64(r.buf.uint16()) 386 case formData4: 387 val = uint64(r.buf.uint32()) 388 case formData8: 389 val = r.buf.uint64() 390 case formData16: 391 r.buf.bytes(16) 392 case formDwarfBlock: 393 r.buf.bytes(int(r.buf.uint())) 394 case formUdata: 395 val = r.buf.uint() 396 } 397 398 switch lf.lnct { 399 case lnctPath: 400 path = str 401 case lnctDirectoryIndex: 402 if val >= uint64(len(r.directories)) { 403 return "", 0, 0, DecodeError{"line", r.buf.off, "directory index out of range"} 404 } 405 dir = r.directories[val] 406 case lnctTimestamp: 407 mtime = val 408 case lnctSize: 409 size = val 410 case lnctMD5: 411 // Ignored. 412 } 413 } 414 415 if dir != "" && path != "" { 416 path = pathJoin(dir, path) 417 } 418 419 return path, mtime, size, nil 420 } 421 422 // readFileEntry reads a file entry from either the header or a 423 // DW_LNE_define_file extended opcode and adds it to r.fileEntries. A 424 // true return value indicates that there are no more entries to read. 425 func (r *LineReader) readFileEntry() (bool, error) { 426 name := r.buf.string() 427 if r.buf.err != nil { 428 return false, r.buf.err 429 } 430 if len(name) == 0 { 431 return true, nil 432 } 433 off := r.buf.off 434 dirIndex := int(r.buf.uint()) 435 if !pathIsAbs(name) { 436 if dirIndex >= len(r.directories) { 437 return false, DecodeError{"line", off, "directory index too large"} 438 } 439 name = pathJoin(r.directories[dirIndex], name) 440 } 441 mtime := r.buf.uint() 442 length := int(r.buf.uint()) 443 444 // If this is a dynamically added path and the cursor was 445 // backed up, we may have already added this entry. Avoid 446 // updating existing line table entries in this case. This 447 // avoids an allocation and potential racy access to the slice 448 // backing store if the user called Files. 449 if len(r.fileEntries) < cap(r.fileEntries) { 450 fe := r.fileEntries[:len(r.fileEntries)+1] 451 if fe[len(fe)-1] != nil { 452 // We already processed this addition. 453 r.fileEntries = fe 454 return false, nil 455 } 456 } 457 r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length}) 458 return false, nil 459 } 460 461 // updateFile updates r.state.File after r.fileIndex has 462 // changed or r.fileEntries has changed. 463 func (r *LineReader) updateFile() { 464 if r.fileIndex < len(r.fileEntries) { 465 r.state.File = r.fileEntries[r.fileIndex] 466 } else { 467 r.state.File = nil 468 } 469 } 470 471 // Next sets *entry to the next row in this line table and moves to 472 // the next row. If there are no more entries and the line table is 473 // properly terminated, it returns io.EOF. 474 // 475 // Rows are always in order of increasing entry.Address, but 476 // entry.Line may go forward or backward. 477 func (r *LineReader) Next(entry *LineEntry) error { 478 if r.buf.err != nil { 479 return r.buf.err 480 } 481 482 // Execute opcodes until we reach an opcode that emits a line 483 // table entry. 484 for { 485 if len(r.buf.data) == 0 { 486 return io.EOF 487 } 488 emit := r.step(entry) 489 if r.buf.err != nil { 490 return r.buf.err 491 } 492 if emit { 493 return nil 494 } 495 } 496 } 497 498 // knownOpcodeLengths gives the opcode lengths (in varint arguments) 499 // of known standard opcodes. 500 var knownOpcodeLengths = map[int]int{ 501 lnsCopy: 0, 502 lnsAdvancePC: 1, 503 lnsAdvanceLine: 1, 504 lnsSetFile: 1, 505 lnsNegateStmt: 0, 506 lnsSetBasicBlock: 0, 507 lnsConstAddPC: 0, 508 lnsSetPrologueEnd: 0, 509 lnsSetEpilogueBegin: 0, 510 lnsSetISA: 1, 511 // lnsFixedAdvancePC takes a uint8 rather than a varint; it's 512 // unclear what length the header is supposed to claim, so 513 // ignore it. 514 } 515 516 // step processes the next opcode and updates r.state. If the opcode 517 // emits a row in the line table, this updates *entry and returns 518 // true. 519 func (r *LineReader) step(entry *LineEntry) bool { 520 opcode := int(r.buf.uint8()) 521 522 if opcode >= r.opcodeBase { 523 // Special opcode [DWARF2 6.2.5.1, DWARF4 6.2.5.1] 524 adjustedOpcode := opcode - r.opcodeBase 525 r.advancePC(adjustedOpcode / r.lineRange) 526 lineDelta := r.lineBase + adjustedOpcode%r.lineRange 527 r.state.Line += lineDelta 528 goto emit 529 } 530 531 switch opcode { 532 case 0: 533 // Extended opcode [DWARF2 6.2.5.3] 534 length := Offset(r.buf.uint()) 535 startOff := r.buf.off 536 opcode := r.buf.uint8() 537 538 switch opcode { 539 case lneEndSequence: 540 r.state.EndSequence = true 541 *entry = r.state 542 r.resetState() 543 544 case lneSetAddress: 545 switch r.addrsize { 546 case 1: 547 r.state.Address = uint64(r.buf.uint8()) 548 case 2: 549 r.state.Address = uint64(r.buf.uint16()) 550 case 4: 551 r.state.Address = uint64(r.buf.uint32()) 552 case 8: 553 r.state.Address = r.buf.uint64() 554 default: 555 r.buf.error("unknown address size") 556 } 557 558 case lneDefineFile: 559 if done, err := r.readFileEntry(); err != nil { 560 r.buf.err = err 561 return false 562 } else if done { 563 r.buf.err = DecodeError{"line", startOff, "malformed DW_LNE_define_file operation"} 564 return false 565 } 566 r.updateFile() 567 568 case lneSetDiscriminator: 569 // [DWARF4 6.2.5.3] 570 r.state.Discriminator = int(r.buf.uint()) 571 } 572 573 r.buf.skip(int(startOff + length - r.buf.off)) 574 575 if opcode == lneEndSequence { 576 return true 577 } 578 579 // Standard opcodes [DWARF2 6.2.5.2] 580 case lnsCopy: 581 goto emit 582 583 case lnsAdvancePC: 584 r.advancePC(int(r.buf.uint())) 585 586 case lnsAdvanceLine: 587 r.state.Line += int(r.buf.int()) 588 589 case lnsSetFile: 590 r.fileIndex = int(r.buf.uint()) 591 r.updateFile() 592 593 case lnsSetColumn: 594 r.state.Column = int(r.buf.uint()) 595 596 case lnsNegateStmt: 597 r.state.IsStmt = !r.state.IsStmt 598 599 case lnsSetBasicBlock: 600 r.state.BasicBlock = true 601 602 case lnsConstAddPC: 603 r.advancePC((255 - r.opcodeBase) / r.lineRange) 604 605 case lnsFixedAdvancePC: 606 r.state.Address += uint64(r.buf.uint16()) 607 608 // DWARF3 standard opcodes [DWARF3 6.2.5.2] 609 case lnsSetPrologueEnd: 610 r.state.PrologueEnd = true 611 612 case lnsSetEpilogueBegin: 613 r.state.EpilogueBegin = true 614 615 case lnsSetISA: 616 r.state.ISA = int(r.buf.uint()) 617 618 default: 619 // Unhandled standard opcode. Skip the number of 620 // arguments that the prologue says this opcode has. 621 for i := 0; i < r.opcodeLengths[opcode]; i++ { 622 r.buf.uint() 623 } 624 } 625 return false 626 627 emit: 628 *entry = r.state 629 r.state.BasicBlock = false 630 r.state.PrologueEnd = false 631 r.state.EpilogueBegin = false 632 r.state.Discriminator = 0 633 return true 634 } 635 636 // advancePC advances "operation pointer" (the combination of Address 637 // and OpIndex) in r.state by opAdvance steps. 638 func (r *LineReader) advancePC(opAdvance int) { 639 opIndex := r.state.OpIndex + opAdvance 640 r.state.Address += uint64(r.minInstructionLength * (opIndex / r.maxOpsPerInstruction)) 641 r.state.OpIndex = opIndex % r.maxOpsPerInstruction 642 } 643 644 // A LineReaderPos represents a position in a line table. 645 type LineReaderPos struct { 646 // off is the current offset in the DWARF line section. 647 off Offset 648 // numFileEntries is the length of fileEntries. 649 numFileEntries int 650 // state and fileIndex are the statement machine state at 651 // offset off. 652 state LineEntry 653 fileIndex int 654 } 655 656 // Tell returns the current position in the line table. 657 func (r *LineReader) Tell() LineReaderPos { 658 return LineReaderPos{r.buf.off, len(r.fileEntries), r.state, r.fileIndex} 659 } 660 661 // Seek restores the line table reader to a position returned by Tell. 662 // 663 // The argument pos must have been returned by a call to Tell on this 664 // line table. 665 func (r *LineReader) Seek(pos LineReaderPos) { 666 r.buf.off = pos.off 667 r.buf.data = r.section[r.buf.off:r.endOffset] 668 r.fileEntries = r.fileEntries[:pos.numFileEntries] 669 r.state = pos.state 670 r.fileIndex = pos.fileIndex 671 } 672 673 // Reset repositions the line table reader at the beginning of the 674 // line table. 675 func (r *LineReader) Reset() { 676 // Reset buffer to the line number program offset. 677 r.buf.off = r.programOffset 678 r.buf.data = r.section[r.buf.off:r.endOffset] 679 680 // Reset file entries list. 681 r.fileEntries = r.fileEntries[:r.initialFileEntries] 682 683 // Reset line number program state. 684 r.resetState() 685 } 686 687 // resetState resets r.state to its default values 688 func (r *LineReader) resetState() { 689 // Reset the state machine registers to the defaults given in 690 // [DWARF4 6.2.2]. 691 r.state = LineEntry{ 692 Address: 0, 693 OpIndex: 0, 694 File: nil, 695 Line: 1, 696 Column: 0, 697 IsStmt: r.defaultIsStmt, 698 BasicBlock: false, 699 PrologueEnd: false, 700 EpilogueBegin: false, 701 ISA: 0, 702 Discriminator: 0, 703 } 704 r.fileIndex = 1 705 r.updateFile() 706 } 707 708 // Files returns the file name table of this compilation unit as of 709 // the current position in the line table. The file name table may be 710 // referenced from attributes in this compilation unit such as 711 // AttrDeclFile. 712 // 713 // Entry 0 is always nil, since file index 0 represents "no file". 714 // 715 // The file name table of a compilation unit is not fixed. Files 716 // returns the file table as of the current position in the line 717 // table. This may contain more entries than the file table at an 718 // earlier position in the line table, though existing entries never 719 // change. 720 func (r *LineReader) Files() []*LineFile { 721 return r.fileEntries 722 } 723 724 // ErrUnknownPC is the error returned by LineReader.ScanPC when the 725 // seek PC is not covered by any entry in the line table. 726 var ErrUnknownPC = errors.New("ErrUnknownPC") 727 728 // SeekPC sets *entry to the LineEntry that includes pc and positions 729 // the reader on the next entry in the line table. If necessary, this 730 // will seek backwards to find pc. 731 // 732 // If pc is not covered by any entry in this line table, SeekPC 733 // returns ErrUnknownPC. In this case, *entry and the final seek 734 // position are unspecified. 735 // 736 // Note that DWARF line tables only permit sequential, forward scans. 737 // Hence, in the worst case, this takes time linear in the size of the 738 // line table. If the caller wishes to do repeated fast PC lookups, it 739 // should build an appropriate index of the line table. 740 func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error { 741 if err := r.Next(entry); err != nil { 742 return err 743 } 744 if entry.Address > pc { 745 // We're too far. Start at the beginning of the table. 746 r.Reset() 747 if err := r.Next(entry); err != nil { 748 return err 749 } 750 if entry.Address > pc { 751 // The whole table starts after pc. 752 r.Reset() 753 return ErrUnknownPC 754 } 755 } 756 757 // Scan until we pass pc, then back up one. 758 for { 759 var next LineEntry 760 pos := r.Tell() 761 if err := r.Next(&next); err != nil { 762 if err == io.EOF { 763 return ErrUnknownPC 764 } 765 return err 766 } 767 if next.Address > pc { 768 if entry.EndSequence { 769 // pc is in a hole in the table. 770 return ErrUnknownPC 771 } 772 // entry is the desired entry. Back up the 773 // cursor to "next" and return success. 774 r.Seek(pos) 775 return nil 776 } 777 *entry = next 778 } 779 } 780 781 // pathIsAbs reports whether path is an absolute path (or "full path 782 // name" in DWARF parlance). This is in "whatever form makes sense for 783 // the host system", so this accepts both UNIX-style and DOS-style 784 // absolute paths. We avoid the filepath package because we want this 785 // to behave the same regardless of our host system and because we 786 // don't know what system the paths came from. 787 func pathIsAbs(path string) bool { 788 _, path = splitDrive(path) 789 return len(path) > 0 && (path[0] == '/' || path[0] == '\\') 790 } 791 792 // pathJoin joins dirname and filename. filename must be relative. 793 // DWARF paths can be UNIX-style or DOS-style, so this handles both. 794 func pathJoin(dirname, filename string) string { 795 if len(dirname) == 0 { 796 return filename 797 } 798 // dirname should be absolute, which means we can determine 799 // whether it's a DOS path reasonably reliably by looking for 800 // a drive letter or UNC path. 801 drive, dirname := splitDrive(dirname) 802 if drive == "" { 803 // UNIX-style path. 804 return path.Join(dirname, filename) 805 } 806 // DOS-style path. 807 drive2, filename := splitDrive(filename) 808 if drive2 != "" { 809 if !strings.EqualFold(drive, drive2) { 810 // Different drives. There's not much we can 811 // do here, so just ignore the directory. 812 return drive2 + filename 813 } 814 // Drives are the same. Ignore drive on filename. 815 } 816 if !(strings.HasSuffix(dirname, "/") || strings.HasSuffix(dirname, `\`)) && dirname != "" { 817 sep := `\` 818 if strings.HasPrefix(dirname, "/") { 819 sep = `/` 820 } 821 dirname += sep 822 } 823 return drive + dirname + filename 824 } 825 826 // splitDrive splits the DOS drive letter or UNC share point from 827 // path, if any. path == drive + rest 828 func splitDrive(path string) (drive, rest string) { 829 if len(path) >= 2 && path[1] == ':' { 830 if c := path[0]; 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' { 831 return path[:2], path[2:] 832 } 833 } 834 if len(path) > 3 && (path[0] == '\\' || path[0] == '/') && (path[1] == '\\' || path[1] == '/') { 835 // Normalize the path so we can search for just \ below. 836 npath := strings.Replace(path, "/", `\`, -1) 837 // Get the host part, which must be non-empty. 838 slash1 := strings.IndexByte(npath[2:], '\\') + 2 839 if slash1 > 2 { 840 // Get the mount-point part, which must be non-empty. 841 slash2 := strings.IndexByte(npath[slash1+1:], '\\') + slash1 + 1 842 if slash2 > slash1 { 843 return path[:slash2], path[slash2:] 844 } 845 } 846 } 847 return "", path 848 }