Black Lives Matter. Support the Equal Justice Initiative.

Source file src/debug/dwarf/line.go

Documentation: debug/dwarf

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

View as plain text