line.mx raw

   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