Source file src/debug/dwarf/line.go

     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  	"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.
    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          []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.
    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 = 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.
   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 := 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.
   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 !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
   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 := 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  

View as plain text