github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/debug/dwarf/line.go (about)

     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  )
    13  
    14  // A LineReader reads a sequence of LineEntry structures from a DWARF
    15  // "line" section for a single compilation unit. LineEntries occur in
    16  // order of increasing PC and each LineEntry gives metadata for the
    17  // instructions from that LineEntry's PC to just before the next
    18  // LineEntry's PC. The last entry will have its EndSequence field set.
    19  type LineReader struct {
    20  	buf buf
    21  
    22  	// Original .debug_line section data. Used by Seek.
    23  	section []byte
    24  
    25  	// Header information
    26  	version              uint16
    27  	minInstructionLength int
    28  	maxOpsPerInstruction int
    29  	defaultIsStmt        bool
    30  	lineBase             int
    31  	lineRange            int
    32  	opcodeBase           int
    33  	opcodeLengths        []int
    34  	directories          []string
    35  	fileEntries          []*LineFile
    36  
    37  	programOffset Offset // section offset of line number program
    38  	endOffset     Offset // section offset of byte following program
    39  
    40  	initialFileEntries int // initial length of fileEntries
    41  
    42  	// Current line number program state machine registers
    43  	state     LineEntry // public state
    44  	fileIndex int       // private state
    45  }
    46  
    47  // A LineEntry is a row in a DWARF line table.
    48  type LineEntry struct {
    49  	// Address is the program-counter value of a machine
    50  	// instruction generated by the compiler. This LineEntry
    51  	// applies to each instruction from Address to just before the
    52  	// Address of the next LineEntry.
    53  	Address uint64
    54  
    55  	// OpIndex is the index of an operation within a VLIW
    56  	// instruction. The index of the first operation is 0. For
    57  	// non-VLIW architectures, it will always be 0. Address and
    58  	// OpIndex together form an operation pointer that can
    59  	// reference any individual operation within the instruction
    60  	// stream.
    61  	OpIndex int
    62  
    63  	// File is the source file corresponding to these
    64  	// instructions.
    65  	File *LineFile
    66  
    67  	// Line is the source code line number corresponding to these
    68  	// instructions. Lines are numbered beginning at 1. It may be
    69  	// 0 if these instructions cannot be attributed to any source
    70  	// line.
    71  	Line int
    72  
    73  	// Column is the column number within the source line of these
    74  	// instructions. Columns are numbered beginning at 1. It may
    75  	// be 0 to indicate the "left edge" of the line.
    76  	Column int
    77  
    78  	// IsStmt indicates that Address is a recommended breakpoint
    79  	// location, such as the beginning of a line, statement, or a
    80  	// distinct subpart of a statement.
    81  	IsStmt bool
    82  
    83  	// BasicBlock indicates that Address is the beginning of a
    84  	// basic block.
    85  	BasicBlock bool
    86  
    87  	// PrologueEnd indicates that Address is one (of possibly
    88  	// many) PCs where execution should be suspended for a
    89  	// breakpoint on entry to the containing function.
    90  	//
    91  	// Added in DWARF 3.
    92  	PrologueEnd bool
    93  
    94  	// EpilogueBegin indicates that Address is one (of possibly
    95  	// many) PCs where execution should be suspended for a
    96  	// breakpoint on exit from this function.
    97  	//
    98  	// Added in DWARF 3.
    99  	EpilogueBegin bool
   100  
   101  	// ISA is the instruction set architecture for these
   102  	// instructions. Possible ISA values should be defined by the
   103  	// applicable ABI specification.
   104  	//
   105  	// Added in DWARF 3.
   106  	ISA int
   107  
   108  	// Discriminator is an arbitrary integer indicating the block
   109  	// to which these instructions belong. It serves to
   110  	// distinguish among multiple blocks that may all have with
   111  	// the same source file, line, and column. Where only one
   112  	// block exists for a given source position, it should be 0.
   113  	//
   114  	// Added in DWARF 3.
   115  	Discriminator int
   116  
   117  	// EndSequence indicates that Address is the first byte after
   118  	// the end of a sequence of target machine instructions. If it
   119  	// is set, only this and the Address field are meaningful. A
   120  	// line number table may contain information for multiple
   121  	// potentially disjoint instruction sequences. The last entry
   122  	// in a line table should always have EndSequence set.
   123  	EndSequence bool
   124  }
   125  
   126  // A LineFile is a source file referenced by a DWARF line table entry.
   127  type LineFile struct {
   128  	Name   string
   129  	Mtime  uint64 // Implementation defined modification time, or 0 if unknown
   130  	Length int    // File length, or 0 if unknown
   131  }
   132  
   133  // LineReader returns a new reader for the line table of compilation
   134  // unit cu, which must be an Entry with tag TagCompileUnit.
   135  //
   136  // If this compilation unit has no line table, it returns nil, nil.
   137  func (d *Data) LineReader(cu *Entry) (*LineReader, error) {
   138  	if d.line == nil {
   139  		// No line tables available.
   140  		return nil, nil
   141  	}
   142  
   143  	// Get line table information from cu.
   144  	off, ok := cu.Val(AttrStmtList).(int64)
   145  	if !ok {
   146  		// cu has no line table.
   147  		return nil, nil
   148  	}
   149  	if off > int64(len(d.line)) {
   150  		return nil, errors.New("AttrStmtList value out of range")
   151  	}
   152  	// AttrCompDir is optional if all file names are absolute. Use
   153  	// the empty string if it's not present.
   154  	compDir, _ := cu.Val(AttrCompDir).(string)
   155  
   156  	// Create the LineReader.
   157  	u := &d.unit[d.offsetToUnit(cu.Offset)]
   158  	buf := makeBuf(d, u, "line", Offset(off), d.line[off:])
   159  	// The compilation directory is implicitly directories[0].
   160  	r := LineReader{buf: buf, section: d.line, directories: []string{compDir}}
   161  
   162  	// Read the header.
   163  	if err := r.readHeader(); err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	// Initialize line reader state.
   168  	r.Reset()
   169  
   170  	return &r, nil
   171  }
   172  
   173  // readHeader reads the line number program header from r.buf and sets
   174  // all of the header fields in r.
   175  func (r *LineReader) readHeader() error {
   176  	buf := &r.buf
   177  
   178  	// Read basic header fields [DWARF2 6.2.4].
   179  	hdrOffset := buf.off
   180  	unitLength, dwarf64 := buf.unitLength()
   181  	r.endOffset = buf.off + unitLength
   182  	if r.endOffset > buf.off+Offset(len(buf.data)) {
   183  		return DecodeError{"line", hdrOffset, fmt.Sprintf("line table end %d exceeds section size %d", r.endOffset, buf.off+Offset(len(buf.data)))}
   184  	}
   185  	r.version = buf.uint16()
   186  	if buf.err == nil && (r.version < 2 || r.version > 4) {
   187  		// DWARF goes to all this effort to make new opcodes
   188  		// backward-compatible, and then adds fields right in
   189  		// the middle of the header in new versions, so we're
   190  		// picky about only supporting known line table
   191  		// versions.
   192  		return DecodeError{"line", hdrOffset, fmt.Sprintf("unknown line table version %d", r.version)}
   193  	}
   194  	var headerLength Offset
   195  	if dwarf64 {
   196  		headerLength = Offset(buf.uint64())
   197  	} else {
   198  		headerLength = Offset(buf.uint32())
   199  	}
   200  	r.programOffset = buf.off + headerLength
   201  	r.minInstructionLength = int(buf.uint8())
   202  	if r.version >= 4 {
   203  		// [DWARF4 6.2.4]
   204  		r.maxOpsPerInstruction = int(buf.uint8())
   205  	} else {
   206  		r.maxOpsPerInstruction = 1
   207  	}
   208  	r.defaultIsStmt = buf.uint8() != 0
   209  	r.lineBase = int(int8(buf.uint8()))
   210  	r.lineRange = int(buf.uint8())
   211  
   212  	// Validate header.
   213  	if buf.err != nil {
   214  		return buf.err
   215  	}
   216  	if r.maxOpsPerInstruction == 0 {
   217  		return DecodeError{"line", hdrOffset, "invalid maximum operations per instruction: 0"}
   218  	}
   219  	if r.lineRange == 0 {
   220  		return DecodeError{"line", hdrOffset, "invalid line range: 0"}
   221  	}
   222  
   223  	// Read standard opcode length table. This table starts with opcode 1.
   224  	r.opcodeBase = int(buf.uint8())
   225  	r.opcodeLengths = make([]int, r.opcodeBase)
   226  	for i := 1; i < r.opcodeBase; i++ {
   227  		r.opcodeLengths[i] = int(buf.uint8())
   228  	}
   229  
   230  	// Validate opcode lengths.
   231  	if buf.err != nil {
   232  		return buf.err
   233  	}
   234  	for i, length := range r.opcodeLengths {
   235  		if known, ok := knownOpcodeLengths[i]; ok && known != length {
   236  			return DecodeError{"line", hdrOffset, fmt.Sprintf("opcode %d expected to have length %d, but has length %d", i, known, length)}
   237  		}
   238  	}
   239  
   240  	// Read include directories table. The caller already set
   241  	// directories[0] to the compilation directory.
   242  	for {
   243  		directory := buf.string()
   244  		if buf.err != nil {
   245  			return buf.err
   246  		}
   247  		if len(directory) == 0 {
   248  			break
   249  		}
   250  		if !path.IsAbs(directory) {
   251  			// Relative paths are implicitly relative to
   252  			// the compilation directory.
   253  			directory = path.Join(r.directories[0], directory)
   254  		}
   255  		r.directories = append(r.directories, directory)
   256  	}
   257  
   258  	// Read file name list. File numbering starts with 1, so leave
   259  	// the first entry nil.
   260  	r.fileEntries = make([]*LineFile, 1)
   261  	for {
   262  		if done, err := r.readFileEntry(); err != nil {
   263  			return err
   264  		} else if done {
   265  			break
   266  		}
   267  	}
   268  	r.initialFileEntries = len(r.fileEntries)
   269  
   270  	return buf.err
   271  }
   272  
   273  // readFileEntry reads a file entry from either the header or a
   274  // DW_LNE_define_file extended opcode and adds it to r.fileEntries. A
   275  // true return value indicates that there are no more entries to read.
   276  func (r *LineReader) readFileEntry() (bool, error) {
   277  	name := r.buf.string()
   278  	if r.buf.err != nil {
   279  		return false, r.buf.err
   280  	}
   281  	if len(name) == 0 {
   282  		return true, nil
   283  	}
   284  	off := r.buf.off
   285  	dirIndex := int(r.buf.uint())
   286  	if !path.IsAbs(name) {
   287  		if dirIndex >= len(r.directories) {
   288  			return false, DecodeError{"line", off, "directory index too large"}
   289  		}
   290  		name = path.Join(r.directories[dirIndex], name)
   291  	}
   292  	mtime := r.buf.uint()
   293  	length := int(r.buf.uint())
   294  
   295  	r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length})
   296  	return false, nil
   297  }
   298  
   299  // updateFile updates r.state.File after r.fileIndex has
   300  // changed or r.fileEntries has changed.
   301  func (r *LineReader) updateFile() {
   302  	if r.fileIndex < len(r.fileEntries) {
   303  		r.state.File = r.fileEntries[r.fileIndex]
   304  	} else {
   305  		r.state.File = nil
   306  	}
   307  }
   308  
   309  // Next sets *entry to the next row in this line table and moves to
   310  // the next row. If there are no more entries and the line table is
   311  // properly terminated, it returns io.EOF.
   312  //
   313  // Rows are always in order of increasing entry.Address, but
   314  // entry.Line may go forward or backward.
   315  func (r *LineReader) Next(entry *LineEntry) error {
   316  	if r.buf.err != nil {
   317  		return r.buf.err
   318  	}
   319  
   320  	// Execute opcodes until we reach an opcode that emits a line
   321  	// table entry.
   322  	for {
   323  		if len(r.buf.data) == 0 {
   324  			return io.EOF
   325  		}
   326  		emit := r.step(entry)
   327  		if r.buf.err != nil {
   328  			return r.buf.err
   329  		}
   330  		if emit {
   331  			return nil
   332  		}
   333  	}
   334  }
   335  
   336  // knownOpcodeLengths gives the opcode lengths (in varint arguments)
   337  // of known standard opcodes.
   338  var knownOpcodeLengths = map[int]int{
   339  	lnsCopy:             0,
   340  	lnsAdvancePC:        1,
   341  	lnsAdvanceLine:      1,
   342  	lnsSetFile:          1,
   343  	lnsNegateStmt:       0,
   344  	lnsSetBasicBlock:    0,
   345  	lnsConstAddPC:       0,
   346  	lnsSetPrologueEnd:   0,
   347  	lnsSetEpilogueBegin: 0,
   348  	lnsSetISA:           1,
   349  	// lnsFixedAdvancePC takes a uint8 rather than a varint; it's
   350  	// unclear what length the header is supposed to claim, so
   351  	// ignore it.
   352  }
   353  
   354  // step processes the next opcode and updates r.state. If the opcode
   355  // emits a row in the line table, this updates *entry and returns
   356  // true.
   357  func (r *LineReader) step(entry *LineEntry) bool {
   358  	opcode := int(r.buf.uint8())
   359  
   360  	if opcode >= r.opcodeBase {
   361  		// Special opcode [DWARF2 6.2.5.1, DWARF4 6.2.5.1]
   362  		adjustedOpcode := opcode - r.opcodeBase
   363  		r.advancePC(adjustedOpcode / r.lineRange)
   364  		lineDelta := r.lineBase + int(adjustedOpcode)%r.lineRange
   365  		r.state.Line += lineDelta
   366  		goto emit
   367  	}
   368  
   369  	switch opcode {
   370  	case 0:
   371  		// Extended opcode [DWARF2 6.2.5.3]
   372  		length := Offset(r.buf.uint())
   373  		startOff := r.buf.off
   374  		opcode := r.buf.uint8()
   375  
   376  		switch opcode {
   377  		case lneEndSequence:
   378  			r.state.EndSequence = true
   379  			*entry = r.state
   380  			r.resetState()
   381  
   382  		case lneSetAddress:
   383  			r.state.Address = r.buf.addr()
   384  
   385  		case lneDefineFile:
   386  			if done, err := r.readFileEntry(); err != nil {
   387  				r.buf.err = err
   388  				return false
   389  			} else if done {
   390  				r.buf.err = DecodeError{"line", startOff, "malformed DW_LNE_define_file operation"}
   391  				return false
   392  			}
   393  			r.updateFile()
   394  
   395  		case lneSetDiscriminator:
   396  			// [DWARF4 6.2.5.3]
   397  			r.state.Discriminator = int(r.buf.uint())
   398  		}
   399  
   400  		r.buf.skip(int(startOff + length - r.buf.off))
   401  
   402  		if opcode == lneEndSequence {
   403  			return true
   404  		}
   405  
   406  	// Standard opcodes [DWARF2 6.2.5.2]
   407  	case lnsCopy:
   408  		goto emit
   409  
   410  	case lnsAdvancePC:
   411  		r.advancePC(int(r.buf.uint()))
   412  
   413  	case lnsAdvanceLine:
   414  		r.state.Line += int(r.buf.int())
   415  
   416  	case lnsSetFile:
   417  		r.fileIndex = int(r.buf.uint())
   418  		r.updateFile()
   419  
   420  	case lnsSetColumn:
   421  		r.state.Column = int(r.buf.uint())
   422  
   423  	case lnsNegateStmt:
   424  		r.state.IsStmt = !r.state.IsStmt
   425  
   426  	case lnsSetBasicBlock:
   427  		r.state.BasicBlock = true
   428  
   429  	case lnsConstAddPC:
   430  		r.advancePC((255 - r.opcodeBase) / r.lineRange)
   431  
   432  	case lnsFixedAdvancePC:
   433  		r.state.Address += uint64(r.buf.uint16())
   434  
   435  	// DWARF3 standard opcodes [DWARF3 6.2.5.2]
   436  	case lnsSetPrologueEnd:
   437  		r.state.PrologueEnd = true
   438  
   439  	case lnsSetEpilogueBegin:
   440  		r.state.EpilogueBegin = true
   441  
   442  	case lnsSetISA:
   443  		r.state.ISA = int(r.buf.uint())
   444  
   445  	default:
   446  		// Unhandled standard opcode. Skip the number of
   447  		// arguments that the prologue says this opcode has.
   448  		for i := 0; i < r.opcodeLengths[opcode]; i++ {
   449  			r.buf.uint()
   450  		}
   451  	}
   452  	return false
   453  
   454  emit:
   455  	*entry = r.state
   456  	r.state.BasicBlock = false
   457  	r.state.PrologueEnd = false
   458  	r.state.EpilogueBegin = false
   459  	r.state.Discriminator = 0
   460  	return true
   461  }
   462  
   463  // advancePC advances "operation pointer" (the combination of Address
   464  // and OpIndex) in r.state by opAdvance steps.
   465  func (r *LineReader) advancePC(opAdvance int) {
   466  	opIndex := r.state.OpIndex + opAdvance
   467  	r.state.Address += uint64(r.minInstructionLength * (opIndex / r.maxOpsPerInstruction))
   468  	r.state.OpIndex = opIndex % r.maxOpsPerInstruction
   469  }
   470  
   471  // A LineReaderPos represents a position in a line table.
   472  type LineReaderPos struct {
   473  	// off is the current offset in the DWARF line section.
   474  	off Offset
   475  	// numFileEntries is the length of fileEntries.
   476  	numFileEntries int
   477  	// state and fileIndex are the statement machine state at
   478  	// offset off.
   479  	state     LineEntry
   480  	fileIndex int
   481  }
   482  
   483  // Tell returns the current position in the line table.
   484  func (r *LineReader) Tell() LineReaderPos {
   485  	return LineReaderPos{r.buf.off, len(r.fileEntries), r.state, r.fileIndex}
   486  }
   487  
   488  // Seek restores the line table reader to a position returned by Tell.
   489  //
   490  // The argument pos must have been returned by a call to Tell on this
   491  // line table.
   492  func (r *LineReader) Seek(pos LineReaderPos) {
   493  	r.buf.off = pos.off
   494  	r.buf.data = r.section[r.buf.off:r.endOffset]
   495  	r.fileEntries = r.fileEntries[:pos.numFileEntries]
   496  	r.state = pos.state
   497  	r.fileIndex = pos.fileIndex
   498  }
   499  
   500  // Reset repositions the line table reader at the beginning of the
   501  // line table.
   502  func (r *LineReader) Reset() {
   503  	// Reset buffer to the line number program offset.
   504  	r.buf.off = r.programOffset
   505  	r.buf.data = r.section[r.buf.off:r.endOffset]
   506  
   507  	// Reset file entries list.
   508  	r.fileEntries = r.fileEntries[:r.initialFileEntries]
   509  
   510  	// Reset line number program state.
   511  	r.resetState()
   512  }
   513  
   514  // resetState resets r.state to its default values
   515  func (r *LineReader) resetState() {
   516  	// Reset the state machine registers to the defaults given in
   517  	// [DWARF4 6.2.2].
   518  	r.state = LineEntry{
   519  		Address:       0,
   520  		OpIndex:       0,
   521  		File:          nil,
   522  		Line:          1,
   523  		Column:        0,
   524  		IsStmt:        r.defaultIsStmt,
   525  		BasicBlock:    false,
   526  		PrologueEnd:   false,
   527  		EpilogueBegin: false,
   528  		ISA:           0,
   529  		Discriminator: 0,
   530  	}
   531  	r.fileIndex = 1
   532  	r.updateFile()
   533  }
   534  
   535  // ErrUnknownPC is the error returned by LineReader.ScanPC when the
   536  // seek PC is not covered by any entry in the line table.
   537  var ErrUnknownPC = errors.New("ErrUnknownPC")
   538  
   539  // SeekPC sets *entry to the LineEntry that includes pc and positions
   540  // the reader on the next entry in the line table. If necessary, this
   541  // will seek backwards to find pc.
   542  //
   543  // If pc is not covered by any entry in this line table, SeekPC
   544  // returns ErrUnknownPC. In this case, *entry and the final seek
   545  // position are unspecified.
   546  //
   547  // Note that DWARF line tables only permit sequential, forward scans.
   548  // Hence, in the worst case, this takes time linear in the size of the
   549  // line table. If the caller wishes to do repeated fast PC lookups, it
   550  // should build an appropriate index of the line table.
   551  func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error {
   552  	if err := r.Next(entry); err != nil {
   553  		return err
   554  	}
   555  	if entry.Address > pc {
   556  		// We're too far. Start at the beginning of the table.
   557  		r.Reset()
   558  		if err := r.Next(entry); err != nil {
   559  			return err
   560  		}
   561  		if entry.Address > pc {
   562  			// The whole table starts after pc.
   563  			r.Reset()
   564  			return ErrUnknownPC
   565  		}
   566  	}
   567  
   568  	// Scan until we pass pc, then back up one.
   569  	for {
   570  		var next LineEntry
   571  		pos := r.Tell()
   572  		if err := r.Next(&next); err != nil {
   573  			if err == io.EOF {
   574  				return ErrUnknownPC
   575  			}
   576  			return err
   577  		}
   578  		if next.Address > pc {
   579  			if entry.EndSequence {
   580  				// pc is in a hole in the table.
   581  				return ErrUnknownPC
   582  			}
   583  			// entry is the desired entry. Back up the
   584  			// cursor to "next" and return success.
   585  			r.Seek(pos)
   586  			return nil
   587  		}
   588  		*entry = next
   589  	}
   590  }