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 its 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 > 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	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.
318type lnctForm struct {
319	lnct int
320	form format
321}
322
323// readLNCTFormat reads an LNCT format description.
324func (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.
335func (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.
425func (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.
463func (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.
477func (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.
500var 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.
519func (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
627emit:
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.
638func (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.
645type 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.
657func (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.
665func (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.
675func (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
688func (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.
720func (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.
726var 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.
740func (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.
787func 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.
794func 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
828func 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}
849