...

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