Home | History | Annotate | Download | only in 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 )
     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 + 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 }
    591