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 "bytes"
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 [][]byte
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 = []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 = [][]byte{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 = []*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 = [][]byte{: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 = []*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 := []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 !bytes.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 !(bytes.HasSuffix(dirname, "/") || bytes.HasSuffix(dirname, `\`)) && dirname != "" {
821 sep := `\`
822 if bytes.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 := bytes.ReplaceAll(path, "/", `\`)
841 // Get the host part, which must be non-empty.
842 slash1 := bytes.IndexByte(npath[2:], '\\') + 2
843 if slash1 > 2 {
844 // Get the mount-point part, which must be non-empty.
845 slash2 := bytes.IndexByte(npath[slash1+1:], '\\') + slash1 + 1
846 if slash2 > slash1 {
847 return path[:slash2], path[slash2:]
848 }
849 }
850 }
851 return "", path
852 }
853