github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/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 the [LineEntry.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 < 0 || 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 programOffset := buf.off + headerLength 219 if programOffset > r.endOffset { 220 return DecodeError{"line", hdrOffset, fmt.Sprintf("malformed line table: program offset %d exceeds end offset %d", programOffset, r.endOffset)} 221 } 222 r.programOffset = programOffset 223 r.minInstructionLength = int(buf.uint8()) 224 if r.version >= 4 { 225 // [DWARF4 6.2.4] 226 r.maxOpsPerInstruction = int(buf.uint8()) 227 } else { 228 r.maxOpsPerInstruction = 1 229 } 230 r.defaultIsStmt = buf.uint8() != 0 231 r.lineBase = int(int8(buf.uint8())) 232 r.lineRange = int(buf.uint8()) 233 234 // Validate header. 235 if buf.err != nil { 236 return buf.err 237 } 238 if r.maxOpsPerInstruction == 0 { 239 return DecodeError{"line", hdrOffset, "invalid maximum operations per instruction: 0"} 240 } 241 if r.lineRange == 0 { 242 return DecodeError{"line", hdrOffset, "invalid line range: 0"} 243 } 244 245 // Read standard opcode length table. This table starts with opcode 1. 246 r.opcodeBase = int(buf.uint8()) 247 r.opcodeLengths = make([]int, r.opcodeBase) 248 for i := 1; i < r.opcodeBase; i++ { 249 r.opcodeLengths[i] = int(buf.uint8()) 250 } 251 252 // Validate opcode lengths. 253 if buf.err != nil { 254 return buf.err 255 } 256 for i, length := range r.opcodeLengths { 257 if known, ok := knownOpcodeLengths[i]; ok && known != length { 258 return DecodeError{"line", hdrOffset, fmt.Sprintf("opcode %d expected to have length %d, but has length %d", i, known, length)} 259 } 260 } 261 262 if r.version < 5 { 263 // Read include directories table. 264 r.directories = []string{compDir} 265 for { 266 directory := buf.string() 267 if buf.err != nil { 268 return buf.err 269 } 270 if len(directory) == 0 { 271 break 272 } 273 if !pathIsAbs(directory) { 274 // Relative paths are implicitly relative to 275 // the compilation directory. 276 directory = pathJoin(compDir, directory) 277 } 278 r.directories = append(r.directories, directory) 279 } 280 281 // Read file name list. File numbering starts with 1, 282 // so leave the first entry nil. 283 r.fileEntries = make([]*LineFile, 1) 284 for { 285 if done, err := r.readFileEntry(); err != nil { 286 return err 287 } else if done { 288 break 289 } 290 } 291 } else { 292 dirFormat := r.readLNCTFormat() 293 c := buf.uint() 294 r.directories = make([]string, c) 295 for i := range r.directories { 296 dir, _, _, err := r.readLNCT(dirFormat, dwarf64) 297 if err != nil { 298 return err 299 } 300 r.directories[i] = dir 301 } 302 fileFormat := r.readLNCTFormat() 303 c = buf.uint() 304 r.fileEntries = make([]*LineFile, c) 305 for i := range r.fileEntries { 306 name, mtime, size, err := r.readLNCT(fileFormat, dwarf64) 307 if err != nil { 308 return err 309 } 310 r.fileEntries[i] = &LineFile{name, mtime, int(size)} 311 } 312 } 313 314 r.initialFileEntries = len(r.fileEntries) 315 316 return buf.err 317 } 318 319 // lnctForm is a pair of an LNCT code and a form. This represents an 320 // entry in the directory name or file name description in the DWARF 5 321 // line number program header. 322 type lnctForm struct { 323 lnct int 324 form format 325 } 326 327 // readLNCTFormat reads an LNCT format description. 328 func (r *LineReader) readLNCTFormat() []lnctForm { 329 c := r.buf.uint8() 330 ret := make([]lnctForm, c) 331 for i := range ret { 332 ret[i].lnct = int(r.buf.uint()) 333 ret[i].form = format(r.buf.uint()) 334 } 335 return ret 336 } 337 338 // readLNCT reads a sequence of LNCT entries and returns path information. 339 func (r *LineReader) readLNCT(s []lnctForm, dwarf64 bool) (path string, mtime uint64, size uint64, err error) { 340 var dir string 341 for _, lf := range s { 342 var str string 343 var val uint64 344 switch lf.form { 345 case formString: 346 str = r.buf.string() 347 case formStrp, formLineStrp: 348 var off uint64 349 if dwarf64 { 350 off = r.buf.uint64() 351 } else { 352 off = uint64(r.buf.uint32()) 353 } 354 if uint64(int(off)) != off { 355 return "", 0, 0, DecodeError{"line", r.buf.off, "strp/line_strp offset out of range"} 356 } 357 var b1 buf 358 if lf.form == formStrp { 359 b1 = makeBuf(r.buf.dwarf, r.buf.format, "str", 0, r.str) 360 } else { 361 b1 = makeBuf(r.buf.dwarf, r.buf.format, "line_str", 0, r.lineStr) 362 } 363 b1.skip(int(off)) 364 str = b1.string() 365 if b1.err != nil { 366 return "", 0, 0, DecodeError{"line", r.buf.off, b1.err.Error()} 367 } 368 case formStrpSup: 369 // Supplemental sections not yet supported. 370 if dwarf64 { 371 r.buf.uint64() 372 } else { 373 r.buf.uint32() 374 } 375 case formStrx: 376 // .debug_line.dwo sections not yet supported. 377 r.buf.uint() 378 case formStrx1: 379 r.buf.uint8() 380 case formStrx2: 381 r.buf.uint16() 382 case formStrx3: 383 r.buf.uint24() 384 case formStrx4: 385 r.buf.uint32() 386 case formData1: 387 val = uint64(r.buf.uint8()) 388 case formData2: 389 val = uint64(r.buf.uint16()) 390 case formData4: 391 val = uint64(r.buf.uint32()) 392 case formData8: 393 val = r.buf.uint64() 394 case formData16: 395 r.buf.bytes(16) 396 case formDwarfBlock: 397 r.buf.bytes(int(r.buf.uint())) 398 case formUdata: 399 val = r.buf.uint() 400 } 401 402 switch lf.lnct { 403 case lnctPath: 404 path = str 405 case lnctDirectoryIndex: 406 if val >= uint64(len(r.directories)) { 407 return "", 0, 0, DecodeError{"line", r.buf.off, "directory index out of range"} 408 } 409 dir = r.directories[val] 410 case lnctTimestamp: 411 mtime = val 412 case lnctSize: 413 size = val 414 case lnctMD5: 415 // Ignored. 416 } 417 } 418 419 if dir != "" && path != "" { 420 path = pathJoin(dir, path) 421 } 422 423 return path, mtime, size, nil 424 } 425 426 // readFileEntry reads a file entry from either the header or a 427 // DW_LNE_define_file extended opcode and adds it to r.fileEntries. A 428 // true return value indicates that there are no more entries to read. 429 func (r *LineReader) readFileEntry() (bool, error) { 430 name := r.buf.string() 431 if r.buf.err != nil { 432 return false, r.buf.err 433 } 434 if len(name) == 0 { 435 return true, nil 436 } 437 off := r.buf.off 438 dirIndex := int(r.buf.uint()) 439 if !pathIsAbs(name) { 440 if dirIndex >= len(r.directories) { 441 return false, DecodeError{"line", off, "directory index too large"} 442 } 443 name = pathJoin(r.directories[dirIndex], name) 444 } 445 mtime := r.buf.uint() 446 length := int(r.buf.uint()) 447 448 // If this is a dynamically added path and the cursor was 449 // backed up, we may have already added this entry. Avoid 450 // updating existing line table entries in this case. This 451 // avoids an allocation and potential racy access to the slice 452 // backing store if the user called Files. 453 if len(r.fileEntries) < cap(r.fileEntries) { 454 fe := r.fileEntries[:len(r.fileEntries)+1] 455 if fe[len(fe)-1] != nil { 456 // We already processed this addition. 457 r.fileEntries = fe 458 return false, nil 459 } 460 } 461 r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length}) 462 return false, nil 463 } 464 465 // updateFile updates r.state.File after r.fileIndex has 466 // changed or r.fileEntries has changed. 467 func (r *LineReader) updateFile() { 468 if r.fileIndex < len(r.fileEntries) { 469 r.state.File = r.fileEntries[r.fileIndex] 470 } else { 471 r.state.File = nil 472 } 473 } 474 475 // Next sets *entry to the next row in this line table and moves to 476 // the next row. If there are no more entries and the line table is 477 // properly terminated, it returns [io.EOF]. 478 // 479 // Rows are always in order of increasing entry.Address, but 480 // entry.Line may go forward or backward. 481 func (r *LineReader) Next(entry *LineEntry) error { 482 if r.buf.err != nil { 483 return r.buf.err 484 } 485 486 // Execute opcodes until we reach an opcode that emits a line 487 // table entry. 488 for { 489 if len(r.buf.data) == 0 { 490 return io.EOF 491 } 492 emit := r.step(entry) 493 if r.buf.err != nil { 494 return r.buf.err 495 } 496 if emit { 497 return nil 498 } 499 } 500 } 501 502 // knownOpcodeLengths gives the opcode lengths (in varint arguments) 503 // of known standard opcodes. 504 var knownOpcodeLengths = map[int]int{ 505 lnsCopy: 0, 506 lnsAdvancePC: 1, 507 lnsAdvanceLine: 1, 508 lnsSetFile: 1, 509 lnsNegateStmt: 0, 510 lnsSetBasicBlock: 0, 511 lnsConstAddPC: 0, 512 lnsSetPrologueEnd: 0, 513 lnsSetEpilogueBegin: 0, 514 lnsSetISA: 1, 515 // lnsFixedAdvancePC takes a uint8 rather than a varint; it's 516 // unclear what length the header is supposed to claim, so 517 // ignore it. 518 } 519 520 // step processes the next opcode and updates r.state. If the opcode 521 // emits a row in the line table, this updates *entry and returns 522 // true. 523 func (r *LineReader) step(entry *LineEntry) bool { 524 opcode := int(r.buf.uint8()) 525 526 if opcode >= r.opcodeBase { 527 // Special opcode [DWARF2 6.2.5.1, DWARF4 6.2.5.1] 528 adjustedOpcode := opcode - r.opcodeBase 529 r.advancePC(adjustedOpcode / r.lineRange) 530 lineDelta := r.lineBase + adjustedOpcode%r.lineRange 531 r.state.Line += lineDelta 532 goto emit 533 } 534 535 switch opcode { 536 case 0: 537 // Extended opcode [DWARF2 6.2.5.3] 538 length := Offset(r.buf.uint()) 539 startOff := r.buf.off 540 opcode := r.buf.uint8() 541 542 switch opcode { 543 case lneEndSequence: 544 r.state.EndSequence = true 545 *entry = r.state 546 r.resetState() 547 548 case lneSetAddress: 549 switch r.addrsize { 550 case 1: 551 r.state.Address = uint64(r.buf.uint8()) 552 case 2: 553 r.state.Address = uint64(r.buf.uint16()) 554 case 4: 555 r.state.Address = uint64(r.buf.uint32()) 556 case 8: 557 r.state.Address = r.buf.uint64() 558 default: 559 r.buf.error("unknown address size") 560 } 561 562 case lneDefineFile: 563 if done, err := r.readFileEntry(); err != nil { 564 r.buf.err = err 565 return false 566 } else if done { 567 r.buf.err = DecodeError{"line", startOff, "malformed DW_LNE_define_file operation"} 568 return false 569 } 570 r.updateFile() 571 572 case lneSetDiscriminator: 573 // [DWARF4 6.2.5.3] 574 r.state.Discriminator = int(r.buf.uint()) 575 } 576 577 r.buf.skip(int(startOff + length - r.buf.off)) 578 579 if opcode == lneEndSequence { 580 return true 581 } 582 583 // Standard opcodes [DWARF2 6.2.5.2] 584 case lnsCopy: 585 goto emit 586 587 case lnsAdvancePC: 588 r.advancePC(int(r.buf.uint())) 589 590 case lnsAdvanceLine: 591 r.state.Line += int(r.buf.int()) 592 593 case lnsSetFile: 594 r.fileIndex = int(r.buf.uint()) 595 r.updateFile() 596 597 case lnsSetColumn: 598 r.state.Column = int(r.buf.uint()) 599 600 case lnsNegateStmt: 601 r.state.IsStmt = !r.state.IsStmt 602 603 case lnsSetBasicBlock: 604 r.state.BasicBlock = true 605 606 case lnsConstAddPC: 607 r.advancePC((255 - r.opcodeBase) / r.lineRange) 608 609 case lnsFixedAdvancePC: 610 r.state.Address += uint64(r.buf.uint16()) 611 612 // DWARF3 standard opcodes [DWARF3 6.2.5.2] 613 case lnsSetPrologueEnd: 614 r.state.PrologueEnd = true 615 616 case lnsSetEpilogueBegin: 617 r.state.EpilogueBegin = true 618 619 case lnsSetISA: 620 r.state.ISA = int(r.buf.uint()) 621 622 default: 623 // Unhandled standard opcode. Skip the number of 624 // arguments that the prologue says this opcode has. 625 for i := 0; i < r.opcodeLengths[opcode]; i++ { 626 r.buf.uint() 627 } 628 } 629 return false 630 631 emit: 632 *entry = r.state 633 r.state.BasicBlock = false 634 r.state.PrologueEnd = false 635 r.state.EpilogueBegin = false 636 r.state.Discriminator = 0 637 return true 638 } 639 640 // advancePC advances "operation pointer" (the combination of Address 641 // and OpIndex) in r.state by opAdvance steps. 642 func (r *LineReader) advancePC(opAdvance int) { 643 opIndex := r.state.OpIndex + opAdvance 644 r.state.Address += uint64(r.minInstructionLength * (opIndex / r.maxOpsPerInstruction)) 645 r.state.OpIndex = opIndex % r.maxOpsPerInstruction 646 } 647 648 // A LineReaderPos represents a position in a line table. 649 type LineReaderPos struct { 650 // off is the current offset in the DWARF line section. 651 off Offset 652 // numFileEntries is the length of fileEntries. 653 numFileEntries int 654 // state and fileIndex are the statement machine state at 655 // offset off. 656 state LineEntry 657 fileIndex int 658 } 659 660 // Tell returns the current position in the line table. 661 func (r *LineReader) Tell() LineReaderPos { 662 return LineReaderPos{r.buf.off, len(r.fileEntries), r.state, r.fileIndex} 663 } 664 665 // Seek restores the line table reader to a position returned by [LineReader.Tell]. 666 // 667 // The argument pos must have been returned by a call to [LineReader.Tell] on this 668 // line table. 669 func (r *LineReader) Seek(pos LineReaderPos) { 670 r.buf.off = pos.off 671 r.buf.data = r.section[r.buf.off:r.endOffset] 672 r.fileEntries = r.fileEntries[:pos.numFileEntries] 673 r.state = pos.state 674 r.fileIndex = pos.fileIndex 675 } 676 677 // Reset repositions the line table reader at the beginning of the 678 // line table. 679 func (r *LineReader) Reset() { 680 // Reset buffer to the line number program offset. 681 r.buf.off = r.programOffset 682 r.buf.data = r.section[r.buf.off:r.endOffset] 683 684 // Reset file entries list. 685 r.fileEntries = r.fileEntries[:r.initialFileEntries] 686 687 // Reset line number program state. 688 r.resetState() 689 } 690 691 // resetState resets r.state to its default values 692 func (r *LineReader) resetState() { 693 // Reset the state machine registers to the defaults given in 694 // [DWARF4 6.2.2]. 695 r.state = LineEntry{ 696 Address: 0, 697 OpIndex: 0, 698 File: nil, 699 Line: 1, 700 Column: 0, 701 IsStmt: r.defaultIsStmt, 702 BasicBlock: false, 703 PrologueEnd: false, 704 EpilogueBegin: false, 705 ISA: 0, 706 Discriminator: 0, 707 } 708 r.fileIndex = 1 709 r.updateFile() 710 } 711 712 // Files returns the file name table of this compilation unit as of 713 // the current position in the line table. The file name table may be 714 // referenced from attributes in this compilation unit such as 715 // [AttrDeclFile]. 716 // 717 // Entry 0 is always nil, since file index 0 represents "no file". 718 // 719 // The file name table of a compilation unit is not fixed. Files 720 // returns the file table as of the current position in the line 721 // table. This may contain more entries than the file table at an 722 // earlier position in the line table, though existing entries never 723 // change. 724 func (r *LineReader) Files() []*LineFile { 725 return r.fileEntries 726 } 727 728 // ErrUnknownPC is the error returned by LineReader.ScanPC when the 729 // seek PC is not covered by any entry in the line table. 730 var ErrUnknownPC = errors.New("ErrUnknownPC") 731 732 // SeekPC sets *entry to the [LineEntry] that includes pc and positions 733 // the reader on the next entry in the line table. If necessary, this 734 // will seek backwards to find pc. 735 // 736 // If pc is not covered by any entry in this line table, SeekPC 737 // returns [ErrUnknownPC]. In this case, *entry and the final seek 738 // position are unspecified. 739 // 740 // Note that DWARF line tables only permit sequential, forward scans. 741 // Hence, in the worst case, this takes time linear in the size of the 742 // line table. If the caller wishes to do repeated fast PC lookups, it 743 // should build an appropriate index of the line table. 744 func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error { 745 if err := r.Next(entry); err != nil { 746 return err 747 } 748 if entry.Address > pc { 749 // We're too far. Start at the beginning of the table. 750 r.Reset() 751 if err := r.Next(entry); err != nil { 752 return err 753 } 754 if entry.Address > pc { 755 // The whole table starts after pc. 756 r.Reset() 757 return ErrUnknownPC 758 } 759 } 760 761 // Scan until we pass pc, then back up one. 762 for { 763 var next LineEntry 764 pos := r.Tell() 765 if err := r.Next(&next); err != nil { 766 if err == io.EOF { 767 return ErrUnknownPC 768 } 769 return err 770 } 771 if next.Address > pc { 772 if entry.EndSequence { 773 // pc is in a hole in the table. 774 return ErrUnknownPC 775 } 776 // entry is the desired entry. Back up the 777 // cursor to "next" and return success. 778 r.Seek(pos) 779 return nil 780 } 781 *entry = next 782 } 783 } 784 785 // pathIsAbs reports whether path is an absolute path (or "full path 786 // name" in DWARF parlance). This is in "whatever form makes sense for 787 // the host system", so this accepts both UNIX-style and DOS-style 788 // absolute paths. We avoid the filepath package because we want this 789 // to behave the same regardless of our host system and because we 790 // don't know what system the paths came from. 791 func pathIsAbs(path string) bool { 792 _, path = splitDrive(path) 793 return len(path) > 0 && (path[0] == '/' || path[0] == '\\') 794 } 795 796 // pathJoin joins dirname and filename. filename must be relative. 797 // DWARF paths can be UNIX-style or DOS-style, so this handles both. 798 func pathJoin(dirname, filename string) string { 799 if len(dirname) == 0 { 800 return filename 801 } 802 // dirname should be absolute, which means we can determine 803 // whether it's a DOS path reasonably reliably by looking for 804 // a drive letter or UNC path. 805 drive, dirname := splitDrive(dirname) 806 if drive == "" { 807 // UNIX-style path. 808 return path.Join(dirname, filename) 809 } 810 // DOS-style path. 811 drive2, filename := splitDrive(filename) 812 if drive2 != "" { 813 if !strings.EqualFold(drive, drive2) { 814 // Different drives. There's not much we can 815 // do here, so just ignore the directory. 816 return drive2 + filename 817 } 818 // Drives are the same. Ignore drive on filename. 819 } 820 if !(strings.HasSuffix(dirname, "/") || strings.HasSuffix(dirname, `\`)) && dirname != "" { 821 sep := `\` 822 if strings.HasPrefix(dirname, "/") { 823 sep = `/` 824 } 825 dirname += sep 826 } 827 return drive + dirname + filename 828 } 829 830 // splitDrive splits the DOS drive letter or UNC share point from 831 // path, if any. path == drive + rest 832 func splitDrive(path string) (drive, rest string) { 833 if len(path) >= 2 && path[1] == ':' { 834 if c := path[0]; 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' { 835 return path[:2], path[2:] 836 } 837 } 838 if len(path) > 3 && (path[0] == '\\' || path[0] == '/') && (path[1] == '\\' || path[1] == '/') { 839 // Normalize the path so we can search for just \ below. 840 npath := strings.Replace(path, "/", `\`, -1) 841 // Get the host part, which must be non-empty. 842 slash1 := strings.IndexByte(npath[2:], '\\') + 2 843 if slash1 > 2 { 844 // Get the mount-point part, which must be non-empty. 845 slash2 := strings.IndexByte(npath[slash1+1:], '\\') + slash1 + 1 846 if slash2 > slash1 { 847 return path[:slash2], path[slash2:] 848 } 849 } 850 } 851 return "", path 852 }