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
5package dwarf
6
7import (
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.
20type 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.
54type 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.
133type 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.
143func (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.
186func (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.
322type lnctForm struct {
323	lnct int
324	form format
325}
326
327// readLNCTFormat reads an LNCT format description.
328func (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.
339func (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.
429func (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.
467func (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.
481func (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.
504var 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.
523func (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
631emit:
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.
642func (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.
649type 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.
661func (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.
669func (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.
679func (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
692func (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.
724func (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.
730var 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.
744func (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.
791func 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.
798func 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
832func 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}
853