func pathJoin(dirname, filename string) string {
	if len(dirname) == 0 {
		return filename
	}
	drive, dirname := splitDrive(dirname)
	if drive == "" {
		return path.Join(dirname, filename)
	}
	drive2, filename := splitDrive(filename)
	if drive2 != "" {
		if !strings.EqualFold(drive, drive2) {
			return drive2 + filename
		}
	}
	if !(strings.HasSuffix(dirname, "/") || strings.HasSuffix(dirname, `\`)) && dirname != "" {
		sep := `\`
		if strings.HasPrefix(dirname, "/") {
			sep = `/`
		}
		dirname += sep
	}
	return drive + dirname + filename
}
pathJoin joins dirname and filename. filename must be relative.
DWARF paths can be UNIX-style or DOS-style, so this handles both.
package dwarf
import (
	"errors"
	"fmt"
	"io"
	"path"
	"strings"
)
type LineReader struct {
	buf buf
	section []byte
	str     []byte  
	lineStr []byte  
	version              uint16
	addrsize             int
	segmentSelectorSize  int
	minInstructionLength int
	maxOpsPerInstruction int
	defaultIsStmt        bool
	lineBase             int
	lineRange            int
	opcodeBase           int
	opcodeLengths        []int
	directories          []string
	fileEntries          []*LineFile
	programOffset Offset  
	endOffset     Offset  
	initialFileEntries int  
	state     LineEntry  
	fileIndex int        
}
type LineEntry struct {
	Address uint64
	OpIndex int
	File *LineFile
	Line int
	Column int
	IsStmt bool
	BasicBlock bool
	PrologueEnd bool
	EpilogueBegin bool
	ISA int
	Discriminator int
	EndSequence bool
}
type LineFile struct {
	Name   string
	Mtime  uint64  
	Length int     
}
func (d *Data) LineReader(cu *Entry) (*LineReader, error) {
	if d.line == nil {
		return nil, nil
	}
	off, ok := cu.Val(AttrStmtList).(int64)
	if !ok {
		return nil, nil
	}
	if off > int64(len(d.line)) {
		return nil, errors.New("AttrStmtList value out of range")
	}
	compDir, _ := cu.Val(AttrCompDir).(string)
	u := &d.unit[d.offsetToUnit(cu.Offset)]
	buf := makeBuf(d, u, "line", Offset(off), d.line[off:])
	r := LineReader{
		buf:     buf,
		section: d.line,
		str:     d.str,
		lineStr: d.lineStr,
	}
	if err := r.readHeader(compDir); err != nil {
		return nil, err
	}
	r.Reset()
	return &r, nil
}
func (r *LineReader) readHeader(compDir string) error {
	buf := &r.buf
	hdrOffset := buf.off
	unitLength, dwarf64 := buf.unitLength()
	r.endOffset = buf.off + unitLength
	if r.endOffset > buf.off+Offset(len(buf.data)) {
		return DecodeError{"line", hdrOffset, fmt.Sprintf("line table end %d exceeds section size %d", r.endOffset, buf.off+Offset(len(buf.data)))}
	}
	r.version = buf.uint16()
	if buf.err == nil && (r.version < 2 || r.version > 5) {
		return DecodeError{"line", hdrOffset, fmt.Sprintf("unknown line table version %d", r.version)}
	}
	if r.version >= 5 {
		r.addrsize = int(buf.uint8())
		r.segmentSelectorSize = int(buf.uint8())
	} else {
		r.addrsize = buf.format.addrsize()
		r.segmentSelectorSize = 0
	}
	var headerLength Offset
	if dwarf64 {
		headerLength = Offset(buf.uint64())
	} else {
		headerLength = Offset(buf.uint32())
	}
	r.programOffset = buf.off + headerLength
	r.minInstructionLength = int(buf.uint8())
	if r.version >= 4 {
		r.maxOpsPerInstruction = int(buf.uint8())
	} else {
		r.maxOpsPerInstruction = 1
	}
	r.defaultIsStmt = buf.uint8() != 0
	r.lineBase = int(int8(buf.uint8()))
	r.lineRange = int(buf.uint8())
	if buf.err != nil {
		return buf.err
	}
	if r.maxOpsPerInstruction == 0 {
		return DecodeError{"line", hdrOffset, "invalid maximum operations per instruction: 0"}
	}
	if r.lineRange == 0 {
		return DecodeError{"line", hdrOffset, "invalid line range: 0"}
	}
	r.opcodeBase = int(buf.uint8())
	r.opcodeLengths = make([]int, r.opcodeBase)
	for i := 1; i < r.opcodeBase; i++ {
		r.opcodeLengths[i] = int(buf.uint8())
	}
	if buf.err != nil {
		return buf.err
	}
	for i, length := range r.opcodeLengths {
		if known, ok := knownOpcodeLengths[i]; ok && known != length {
			return DecodeError{"line", hdrOffset, fmt.Sprintf("opcode %d expected to have length %d, but has length %d", i, known, length)}
		}
	}
	if r.version < 5 {
		r.directories = []string{compDir}
		for {
			directory := buf.string()
			if buf.err != nil {
				return buf.err
			}
			if len(directory) == 0 {
				break
			}
			if !pathIsAbs(directory) {
				directory = pathJoin(compDir, directory)
			}
			r.directories = append(r.directories, directory)
		}
		r.fileEntries = make([]*LineFile, 1)
		for {
			if done, err := r.readFileEntry(); err != nil {
				return err
			} else if done {
				break
			}
		}
	} else {
		dirFormat := r.readLNCTFormat()
		c := buf.uint()
		r.directories = make([]string, c)
		for i := range r.directories {
			dir, _, _, err := r.readLNCT(dirFormat, dwarf64)
			if err != nil {
				return err
			}
			r.directories[i] = dir
		}
		fileFormat := r.readLNCTFormat()
		c = buf.uint()
		r.fileEntries = make([]*LineFile, c)
		for i := range r.fileEntries {
			name, mtime, size, err := r.readLNCT(fileFormat, dwarf64)
			if err != nil {
				return err
			}
			r.fileEntries[i] = &LineFile{name, mtime, int(size)}
		}
	}
	r.initialFileEntries = len(r.fileEntries)
	return buf.err
}
type lnctForm struct {
	lnct int
	form format
}
func (r *LineReader) readLNCTFormat() []lnctForm {
	c := r.buf.uint8()
	ret := make([]lnctForm, c)
	for i := range ret {
		ret[i].lnct = int(r.buf.uint())
		ret[i].form = format(r.buf.uint())
	}
	return ret
}
func (r *LineReader) readLNCT(s []lnctForm, dwarf64 bool) (path string, mtime uint64, size uint64, err error) {
	var dir string
	for _, lf := range s {
		var str string
		var val uint64
		switch lf.form {
		case formString:
			str = r.buf.string()
		case formStrp, formLineStrp:
			var off uint64
			if dwarf64 {
				off = r.buf.uint64()
			} else {
				off = uint64(r.buf.uint32())
			}
			if uint64(int(off)) != off {
				return "", 0, 0, DecodeError{"line", r.buf.off, "strp/line_strp offset out of range"}
			}
			var b1 buf
			if lf.form == formStrp {
				b1 = makeBuf(r.buf.dwarf, r.buf.format, "str", 0, r.str)
			} else {
				b1 = makeBuf(r.buf.dwarf, r.buf.format, "line_str", 0, r.lineStr)
			}
			b1.skip(int(off))
			str = b1.string()
			if b1.err != nil {
				return "", 0, 0, DecodeError{"line", r.buf.off, b1.err.Error()}
			}
		case formStrpSup:
			if dwarf64 {
				r.buf.uint64()
			} else {
				r.buf.uint32()
			}
		case formStrx:
			r.buf.uint()
		case formStrx1:
			r.buf.uint8()
		case formStrx2:
			r.buf.uint16()
		case formStrx3:
			r.buf.uint24()
		case formStrx4:
			r.buf.uint32()
		case formData1:
			val = uint64(r.buf.uint8())
		case formData2:
			val = uint64(r.buf.uint16())
		case formData4:
			val = uint64(r.buf.uint32())
		case formData8:
			val = r.buf.uint64()
		case formData16:
			r.buf.bytes(16)
		case formDwarfBlock:
			r.buf.bytes(int(r.buf.uint()))
		case formUdata:
			val = r.buf.uint()
		}
		switch lf.lnct {
		case lnctPath:
			path = str
		case lnctDirectoryIndex:
			if val >= uint64(len(r.directories)) {
				return "", 0, 0, DecodeError{"line", r.buf.off, "directory index out of range"}
			}
			dir = r.directories[val]
		case lnctTimestamp:
			mtime = val
		case lnctSize:
			size = val
		case lnctMD5:
		}
	}
	if dir != "" && path != "" {
		path = pathJoin(dir, path)
	}
	return path, mtime, size, nil
}
func (r *LineReader) readFileEntry() (bool, error) {
	name := r.buf.string()
	if r.buf.err != nil {
		return false, r.buf.err
	}
	if len(name) == 0 {
		return true, nil
	}
	off := r.buf.off
	dirIndex := int(r.buf.uint())
	if !pathIsAbs(name) {
		if dirIndex >= len(r.directories) {
			return false, DecodeError{"line", off, "directory index too large"}
		}
		name = pathJoin(r.directories[dirIndex], name)
	}
	mtime := r.buf.uint()
	length := int(r.buf.uint())
	if len(r.fileEntries) < cap(r.fileEntries) {
		fe := r.fileEntries[:len(r.fileEntries)+1]
		if fe[len(fe)-1] != nil {
			r.fileEntries = fe
			return false, nil
		}
	}
	r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length})
	return false, nil
}
func (r *LineReader) updateFile() {
	if r.fileIndex < len(r.fileEntries) {
		r.state.File = r.fileEntries[r.fileIndex]
	} else {
		r.state.File = nil
	}
}
func (r *LineReader) Next(entry *LineEntry) error {
	if r.buf.err != nil {
		return r.buf.err
	}
	for {
		if len(r.buf.data) == 0 {
			return io.EOF
		}
		emit := r.step(entry)
		if r.buf.err != nil {
			return r.buf.err
		}
		if emit {
			return nil
		}
	}
}
var knownOpcodeLengths = map[int]int{
	lnsCopy:             0,
	lnsAdvancePC:        1,
	lnsAdvanceLine:      1,
	lnsSetFile:          1,
	lnsNegateStmt:       0,
	lnsSetBasicBlock:    0,
	lnsConstAddPC:       0,
	lnsSetPrologueEnd:   0,
	lnsSetEpilogueBegin: 0,
	lnsSetISA:           1,
}
func (r *LineReader) step(entry *LineEntry) bool {
	opcode := int(r.buf.uint8())
	if opcode >= r.opcodeBase {
		adjustedOpcode := opcode - r.opcodeBase
		r.advancePC(adjustedOpcode / r.lineRange)
		lineDelta := r.lineBase + adjustedOpcode%r.lineRange
		r.state.Line += lineDelta
		goto emit
	}
	switch opcode {
	case 0:
		length := Offset(r.buf.uint())
		startOff := r.buf.off
		opcode := r.buf.uint8()
		switch opcode {
		case lneEndSequence:
			r.state.EndSequence = true
			*entry = r.state
			r.resetState()
		case lneSetAddress:
			switch r.addrsize {
			case 1:
				r.state.Address = uint64(r.buf.uint8())
			case 2:
				r.state.Address = uint64(r.buf.uint16())
			case 4:
				r.state.Address = uint64(r.buf.uint32())
			case 8:
				r.state.Address = r.buf.uint64()
			default:
				r.buf.error("unknown address size")
			}
		case lneDefineFile:
			if done, err := r.readFileEntry(); err != nil {
				r.buf.err = err
				return false
			} else if done {
				r.buf.err = DecodeError{"line", startOff, "malformed DW_LNE_define_file operation"}
				return false
			}
			r.updateFile()
		case lneSetDiscriminator:
			r.state.Discriminator = int(r.buf.uint())
		}
		r.buf.skip(int(startOff + length - r.buf.off))
		if opcode == lneEndSequence {
			return true
		}
	case lnsCopy:
		goto emit
	case lnsAdvancePC:
		r.advancePC(int(r.buf.uint()))
	case lnsAdvanceLine:
		r.state.Line += int(r.buf.int())
	case lnsSetFile:
		r.fileIndex = int(r.buf.uint())
		r.updateFile()
	case lnsSetColumn:
		r.state.Column = int(r.buf.uint())
	case lnsNegateStmt:
		r.state.IsStmt = !r.state.IsStmt
	case lnsSetBasicBlock:
		r.state.BasicBlock = true
	case lnsConstAddPC:
		r.advancePC((255 - r.opcodeBase) / r.lineRange)
	case lnsFixedAdvancePC:
		r.state.Address += uint64(r.buf.uint16())
	case lnsSetPrologueEnd:
		r.state.PrologueEnd = true
	case lnsSetEpilogueBegin:
		r.state.EpilogueBegin = true
	case lnsSetISA:
		r.state.ISA = int(r.buf.uint())
	default:
		for i := 0; i < r.opcodeLengths[opcode]; i++ {
			r.buf.uint()
		}
	}
	return false
emit:
	*entry = r.state
	r.state.BasicBlock = false
	r.state.PrologueEnd = false
	r.state.EpilogueBegin = false
	r.state.Discriminator = 0
	return true
}
func (r *LineReader) advancePC(opAdvance int) {
	opIndex := r.state.OpIndex + opAdvance
	r.state.Address += uint64(r.minInstructionLength * (opIndex / r.maxOpsPerInstruction))
	r.state.OpIndex = opIndex % r.maxOpsPerInstruction
}
type LineReaderPos struct {
	off Offset
	numFileEntries int
	state     LineEntry
	fileIndex int
}
func (r *LineReader) Tell() LineReaderPos {
	return LineReaderPos{r.buf.off, len(r.fileEntries), r.state, r.fileIndex}
}
func (r *LineReader) Seek(pos LineReaderPos) {
	r.buf.off = pos.off
	r.buf.data = r.section[r.buf.off:r.endOffset]
	r.fileEntries = r.fileEntries[:pos.numFileEntries]
	r.state = pos.state
	r.fileIndex = pos.fileIndex
}
func (r *LineReader) Reset() {
	r.buf.off = r.programOffset
	r.buf.data = r.section[r.buf.off:r.endOffset]
	r.fileEntries = r.fileEntries[:r.initialFileEntries]
	r.resetState()
}
func (r *LineReader) resetState() {
	r.state = LineEntry{
		Address:       0,
		OpIndex:       0,
		File:          nil,
		Line:          1,
		Column:        0,
		IsStmt:        r.defaultIsStmt,
		BasicBlock:    false,
		PrologueEnd:   false,
		EpilogueBegin: false,
		ISA:           0,
		Discriminator: 0,
	}
	r.fileIndex = 1
	r.updateFile()
}
func (r *LineReader) Files() []*LineFile {
	return r.fileEntries
}
var ErrUnknownPC = errors.New("ErrUnknownPC")
func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error {
	if err := r.Next(entry); err != nil {
		return err
	}
	if entry.Address > pc {
		r.Reset()
		if err := r.Next(entry); err != nil {
			return err
		}
		if entry.Address > pc {
			r.Reset()
			return ErrUnknownPC
		}
	}
	for {
		var next LineEntry
		pos := r.Tell()
		if err := r.Next(&next); err != nil {
			if err == io.EOF {
				return ErrUnknownPC
			}
			return err
		}
		if next.Address > pc {
			if entry.EndSequence {
				return ErrUnknownPC
			}
			r.Seek(pos)
			return nil
		}
		*entry = next
	}
}
func pathIsAbs(path string) bool {
	_, path = splitDrive(path)
	return len(path) > 0 && (path[0] == '/' || path[0] == '\\')
}