summaryrefslogtreecommitdiff
path: root/libgo/go/debug/dwarf/line.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/debug/dwarf/line.go')
-rw-r--r--libgo/go/debug/dwarf/line.go935
1 files changed, 522 insertions, 413 deletions
diff --git a/libgo/go/debug/dwarf/line.go b/libgo/go/debug/dwarf/line.go
index c463c3b0d8f..ca64bbd7f3b 100644
--- a/libgo/go/debug/dwarf/line.go
+++ b/libgo/go/debug/dwarf/line.go
@@ -1,481 +1,590 @@
-// Copyright 2012 The Go Authors. All rights reserved.
+// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// DWARF line number information.
-
package dwarf
import (
"errors"
- "path/filepath"
- "sort"
- "strconv"
+ "fmt"
+ "io"
+ "path"
)
-// A Line holds all the available information about the source code
-// corresponding to a specific program counter address.
-type Line struct {
- Filename string // source file name
- OpIndex int // index of operation in VLIW instruction
- Line int // line number
- Column int // column number
- ISA int // instruction set code
- Discriminator int // block discriminator
- Stmt bool // instruction starts statement
- Block bool // instruction starts basic block
- EndPrologue bool // instruction ends function prologue
- BeginEpilogue bool // instruction begins function epilogue
+// A LineReader reads a sequence of LineEntry structures from a DWARF
+// "line" section for a single compilation unit. LineEntries occur in
+// order of increasing PC and each LineEntry gives metadata for the
+// instructions from that LineEntry's PC to just before the next
+// LineEntry's PC. The last entry will have its EndSequence field set.
+type LineReader struct {
+ buf buf
+
+ // Original .debug_line section data. Used by Seek.
+ section []byte
+
+ // Header information
+ version uint16
+ minInstructionLength int
+ maxOpsPerInstruction int
+ defaultIsStmt bool
+ lineBase int
+ lineRange int
+ opcodeBase int
+ opcodeLengths []int
+ directories []string
+ fileEntries []*LineFile
+
+ programOffset Offset // section offset of line number program
+ endOffset Offset // section offset of byte following program
+
+ initialFileEntries int // initial length of fileEntries
+
+ // Current line number program state machine registers
+ state LineEntry // public state
+ fileIndex int // private state
}
-// LineForPc returns the line number information for a program counter
-// address, if any. When this returns multiple Line structures in a
-// context where only one can be used, the last one is the best.
-func (d *Data) LineForPC(pc uint64) ([]*Line, error) {
- for i := range d.unit {
- u := &d.unit[i]
- if u.pc == nil {
- if err := d.readUnitLine(i, u); err != nil {
- return nil, err
- }
- }
- for _, ar := range u.pc {
- if pc >= ar.low && pc < ar.high {
- return d.findLine(u, pc)
- }
- }
+// A LineEntry is a row in a DWARF line table.
+type LineEntry struct {
+ // Address is the program-counter value of a machine
+ // instruction generated by the compiler. This LineEntry
+ // applies to each instruction from Address to just before the
+ // Address of the next LineEntry.
+ Address uint64
+
+ // OpIndex is the index of an operation within a VLIW
+ // instruction. The index of the first operation is 0. For
+ // non-VLIW architectures, it will always be 0. Address and
+ // OpIndex together form an operation pointer that can
+ // reference any individual operation within the instruction
+ // stream.
+ OpIndex int
+
+ // File is the source file corresponding to these
+ // instructions.
+ File *LineFile
+
+ // Line is the source code line number corresponding to these
+ // instructions. Lines are numbered beginning at 1. It may be
+ // 0 if these instructions cannot be attributed to any source
+ // line.
+ Line int
+
+ // Column is the column number within the source line of these
+ // instructions. Columns are numbered beginning at 1. It may
+ // be 0 to indicate the "left edge" of the line.
+ Column int
+
+ // IsStmt indicates that Address is a recommended breakpoint
+ // location, such as the beginning of a line, statement, or a
+ // distinct subpart of a statement.
+ IsStmt bool
+
+ // BasicBlock indicates that Address is the beginning of a
+ // basic block.
+ BasicBlock bool
+
+ // PrologueEnd indicates that Address is one (of possibly
+ // many) PCs where execution should be suspended for a
+ // breakpoint on entry to the containing function.
+ //
+ // Added in DWARF 3.
+ PrologueEnd bool
+
+ // EpilogueBegin indicates that Address is one (of possibly
+ // many) PCs where execution should be suspended for a
+ // breakpoint on exit from this function.
+ //
+ // Added in DWARF 3.
+ EpilogueBegin bool
+
+ // ISA is the instruction set architecture for these
+ // instructions. Possible ISA values should be defined by the
+ // applicable ABI specification.
+ //
+ // Added in DWARF 3.
+ ISA int
+
+ // Discriminator is an arbitrary integer indicating the block
+ // to which these instructions belong. It serves to
+ // distinguish among multiple blocks that may all have with
+ // the same source file, line, and column. Where only one
+ // block exists for a given source position, it should be 0.
+ //
+ // Added in DWARF 3.
+ Discriminator int
+
+ // EndSequence indicates that Address is the first byte after
+ // the end of a sequence of target machine instructions. If it
+ // is set, only this and the Address field are meaningful. A
+ // line number table may contain information for multiple
+ // potentially disjoint instruction sequences. The last entry
+ // in a line table should always have EndSequence set.
+ EndSequence bool
+}
+
+// A LineFile is a source file referenced by a DWARF line table entry.
+type LineFile struct {
+ Name string
+ Mtime uint64 // Implementation defined modification time, or 0 if unknown
+ Length int // File length, or 0 if unknown
+}
+
+// LineReader returns a new reader for the line table of compilation
+// unit cu, which must be an Entry with tag TagCompileUnit.
+//
+// If this compilation unit has no line table, it returns nil, nil.
+func (d *Data) LineReader(cu *Entry) (*LineReader, error) {
+ if d.line == nil {
+ // No line tables available.
+ return nil, nil
+ }
+
+ // Get line table information from cu.
+ off, ok := cu.Val(AttrStmtList).(int64)
+ if !ok {
+ // cu has no line table.
+ return nil, nil
}
- return nil, nil
+ if off > int64(len(d.line)) {
+ return nil, errors.New("AttrStmtList value out of range")
+ }
+ // AttrCompDir is optional if all file names are absolute. Use
+ // the empty string if it's not present.
+ compDir, _ := cu.Val(AttrCompDir).(string)
+
+ // Create the LineReader.
+ u := &d.unit[d.offsetToUnit(cu.Offset)]
+ buf := makeBuf(d, u, "line", Offset(off), d.line[off:])
+ // The compilation directory is implicitly directories[0].
+ r := LineReader{buf: buf, section: d.line, directories: []string{compDir}}
+
+ // Read the header.
+ if err := r.readHeader(); err != nil {
+ return nil, err
+ }
+
+ // Initialize line reader state.
+ r.Reset()
+
+ return &r, nil
}
-// readUnitLine reads in the line number information for a compilation
-// unit.
-func (d *Data) readUnitLine(i int, u *unit) error {
- r := d.unitReader(i)
- setLineOff := false
- for {
- e, err := r.Next()
- if err != nil {
- return err
+// readHeader reads the line number program header from r.buf and sets
+// all of the header fields in r.
+func (r *LineReader) readHeader() error {
+ buf := &r.buf
+
+ // Read basic header fields [DWARF2 6.2.4].
+ hdrOffset := buf.off
+ unitLength, dwarf64 := buf.unitLength()
+ r.endOffset = buf.off + unitLength
+ if r.endOffset > buf.off+Offset(len(buf.data)) {
+ return DecodeError{"line", hdrOffset, fmt.Sprintf("line table end %d exceeds section size %d", r.endOffset, buf.off+Offset(len(buf.data)))}
+ }
+ r.version = buf.uint16()
+ if buf.err == nil && (r.version < 2 || r.version > 4) {
+ // DWARF goes to all this effort to make new opcodes
+ // backward-compatible, and then adds fields right in
+ // the middle of the header in new versions, so we're
+ // picky about only supporting known line table
+ // versions.
+ return DecodeError{"line", hdrOffset, fmt.Sprintf("unknown line table version %d", r.version)}
+ }
+ var headerLength Offset
+ if dwarf64 {
+ headerLength = Offset(buf.uint64())
+ } else {
+ headerLength = Offset(buf.uint32())
+ }
+ r.programOffset = buf.off + headerLength
+ r.minInstructionLength = int(buf.uint8())
+ if r.version >= 4 {
+ // [DWARF4 6.2.4]
+ r.maxOpsPerInstruction = int(buf.uint8())
+ } else {
+ r.maxOpsPerInstruction = 1
+ }
+ r.defaultIsStmt = buf.uint8() != 0
+ r.lineBase = int(int8(buf.uint8()))
+ r.lineRange = int(buf.uint8())
+
+ // Validate header.
+ if buf.err != nil {
+ return buf.err
+ }
+ if r.maxOpsPerInstruction == 0 {
+ return DecodeError{"line", hdrOffset, "invalid maximum operations per instruction: 0"}
+ }
+ if r.lineRange == 0 {
+ return DecodeError{"line", hdrOffset, "invalid line range: 0"}
+ }
+
+ // Read standard opcode length table. This table starts with opcode 1.
+ r.opcodeBase = int(buf.uint8())
+ r.opcodeLengths = make([]int, r.opcodeBase)
+ for i := 1; i < r.opcodeBase; i++ {
+ r.opcodeLengths[i] = int(buf.uint8())
+ }
+
+ // Validate opcode lengths.
+ if buf.err != nil {
+ return buf.err
+ }
+ for i, length := range r.opcodeLengths {
+ if known, ok := knownOpcodeLengths[i]; ok && known != length {
+ return DecodeError{"line", hdrOffset, fmt.Sprintf("opcode %d expected to have length %d, but has length %d", i, known, length)}
}
- if e == nil {
- break
+ }
+
+ // Read include directories table. The caller already set
+ // directories[0] to the compilation directory.
+ for {
+ directory := buf.string()
+ if buf.err != nil {
+ return buf.err
}
- if r.unit != i {
+ if len(directory) == 0 {
break
}
- switch e.Tag {
- case TagCompileUnit, TagSubprogram, TagEntryPoint, TagInlinedSubroutine:
- low, lowok := e.Val(AttrLowpc).(uint64)
- var high uint64
- var highok bool
- switch v := e.Val(AttrHighpc).(type) {
- case uint64:
- high = v
- highok = true
- case int64:
- high = low + uint64(v)
- highok = true
- }
- if lowok && highok {
- u.pc = append(u.pc, addrRange{low, high})
- } else if off, ok := e.Val(AttrRanges).(Offset); ok {
- if err := d.readAddressRanges(off, low, u); err != nil {
- return err
- }
- }
- val := e.Val(AttrStmtList)
- if val != nil {
- if off, ok := val.(int64); ok {
- u.lineoff = Offset(off)
- setLineOff = true
- } else if off, ok := val.(Offset); ok {
- u.lineoff = off
- setLineOff = true
- } else {
- return errors.New("unrecognized format for DW_ATTR_stmt_list")
- }
- }
- if dir, ok := e.Val(AttrCompDir).(string); ok {
- u.dir = dir
- }
+ if !path.IsAbs(directory) {
+ // Relative paths are implicitly relative to
+ // the compilation directory.
+ directory = path.Join(r.directories[0], directory)
}
+ r.directories = append(r.directories, directory)
}
- if !setLineOff {
- u.lineoff = Offset(0)
- u.lineoff--
+
+ // Read file name list. File numbering starts with 1, so leave
+ // the first entry nil.
+ r.fileEntries = make([]*LineFile, 1)
+ for {
+ if done, err := r.readFileEntry(); err != nil {
+ return err
+ } else if done {
+ break
+ }
}
- return nil
+ r.initialFileEntries = len(r.fileEntries)
+
+ return buf.err
}
-// readAddressRanges adds address ranges to a unit.
-func (d *Data) readAddressRanges(off Offset, base uint64, u *unit) error {
- b := makeBuf(d, u, "ranges", off, d.ranges[off:])
- var highest uint64
- switch u.addrsize() {
- case 1:
- highest = 0xff
- case 2:
- highest = 0xffff
- case 4:
- highest = 0xffffffff
- case 8:
- highest = 0xffffffffffffffff
- default:
- return errors.New("unknown address size")
+// readFileEntry reads a file entry from either the header or a
+// DW_LNE_define_file extended opcode and adds it to r.fileEntries. A
+// true return value indicates that there are no more entries to read.
+func (r *LineReader) readFileEntry() (bool, error) {
+ name := r.buf.string()
+ if r.buf.err != nil {
+ return false, r.buf.err
}
- for {
- if b.err != nil {
- return b.err
- }
- low := b.addr()
- high := b.addr()
- if low == 0 && high == 0 {
- return b.err
- } else if low == highest {
- base = high
- } else {
- u.pc = append(u.pc, addrRange{low + base, high + base})
+ if len(name) == 0 {
+ return true, nil
+ }
+ off := r.buf.off
+ dirIndex := int(r.buf.uint())
+ if !path.IsAbs(name) {
+ if dirIndex >= len(r.directories) {
+ return false, DecodeError{"line", off, "directory index too large"}
}
+ name = path.Join(r.directories[dirIndex], name)
}
+ mtime := r.buf.uint()
+ length := int(r.buf.uint())
+
+ r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length})
+ return false, nil
}
-// findLine finds the line information for a PC value, given the unit
-// containing the information.
-func (d *Data) findLine(u *unit, pc uint64) ([]*Line, error) {
- if u.lines == nil {
- if err := d.parseLine(u); err != nil {
- return nil, err
- }
+// updateFile updates r.state.File after r.fileIndex has
+// changed or r.fileEntries has changed.
+func (r *LineReader) updateFile() {
+ if r.fileIndex < len(r.fileEntries) {
+ r.state.File = r.fileEntries[r.fileIndex]
+ } else {
+ r.state.File = nil
}
+}
- for _, ln := range u.lines {
- if pc < ln.addrs[0].pc || pc > ln.addrs[len(ln.addrs)-1].pc {
- continue
+// Next sets *entry to the next row in this line table and moves to
+// the next row. If there are no more entries and the line table is
+// properly terminated, it returns io.EOF.
+//
+// Rows are always in order of increasing entry.Address, but
+// entry.Line may go forward or backward.
+func (r *LineReader) Next(entry *LineEntry) error {
+ if r.buf.err != nil {
+ return r.buf.err
+ }
+
+ // Execute opcodes until we reach an opcode that emits a line
+ // table entry.
+ for {
+ if len(r.buf.data) == 0 {
+ return io.EOF
}
- i := sort.Search(len(ln.addrs),
- func(i int) bool { return ln.addrs[i].pc > pc })
- i--
- p := new(Line)
- *p = ln.line
- p.Line = ln.addrs[i].line
- ret := []*Line{p}
- for i++; i < len(ln.addrs) && ln.addrs[i].pc == pc; i++ {
- p = new(Line)
- *p = ln.line
- p.Line = ln.addrs[i].line
- ret = append(ret, p)
+ emit := r.step(entry)
+ if r.buf.err != nil {
+ return r.buf.err
+ }
+ if emit {
+ return nil
}
- return ret, nil
}
+}
- return nil, nil
+// knownOpcodeLengths gives the opcode lengths (in varint arguments)
+// of known standard opcodes.
+var knownOpcodeLengths = map[int]int{
+ lnsCopy: 0,
+ lnsAdvancePC: 1,
+ lnsAdvanceLine: 1,
+ lnsSetFile: 1,
+ lnsNegateStmt: 0,
+ lnsSetBasicBlock: 0,
+ lnsConstAddPC: 0,
+ lnsSetPrologueEnd: 0,
+ lnsSetEpilogueBegin: 0,
+ lnsSetISA: 1,
+ // lnsFixedAdvancePC takes a uint8 rather than a varint; it's
+ // unclear what length the header is supposed to claim, so
+ // ignore it.
}
-// FileLine returns the file name and line number for a program
-// counter address, or "", 0 if unknown.
-func (d *Data) FileLine(pc uint64) (string, int, error) {
- r, err := d.LineForPC(pc)
- if err != nil {
- return "", 0, err
- }
- if r == nil {
- return "", 0, nil
+// step processes the next opcode and updates r.state. If the opcode
+// emits a row in the line table, this updates *entry and returns
+// true.
+func (r *LineReader) step(entry *LineEntry) bool {
+ opcode := int(r.buf.uint8())
+
+ if opcode >= r.opcodeBase {
+ // Special opcode [DWARF2 6.2.5.1, DWARF4 6.2.5.1]
+ adjustedOpcode := opcode - r.opcodeBase
+ r.advancePC(adjustedOpcode / r.lineRange)
+ lineDelta := r.lineBase + int(adjustedOpcode)%r.lineRange
+ r.state.Line += lineDelta
+ goto emit
}
- ln := r[len(r)-1]
- return ln.Filename, ln.Line, nil
-}
-// A mapLineInfo holds the PC values and line numbers associated with
-// a single Line structure. This representation is chosen to reduce
-// memory usage based on typical debug info.
-type mapLineInfo struct {
- line Line // line.Line will be zero
- addrs lineAddrs // sorted by PC
-}
+ switch opcode {
+ case 0:
+ // Extended opcode [DWARF2 6.2.5.3]
+ length := Offset(r.buf.uint())
+ startOff := r.buf.off
+ opcode := r.buf.uint8()
-// A list of lines. This will be sorted by PC.
-type lineAddrs []oneLineInfo
+ switch opcode {
+ case lneEndSequence:
+ r.state.EndSequence = true
+ *entry = r.state
+ r.resetState()
-func (p lineAddrs) Len() int { return len(p) }
-func (p lineAddrs) Less(i, j int) bool { return p[i].pc < p[j].pc }
-func (p lineAddrs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+ case lneSetAddress:
+ r.state.Address = r.buf.addr()
-// A oneLineInfo is a single PC and line number.
-type oneLineInfo struct {
- pc uint64
- line int
-}
+ case lneDefineFile:
+ if done, err := r.readFileEntry(); err != nil {
+ r.buf.err = err
+ return false
+ } else if done {
+ r.buf.err = DecodeError{"line", startOff, "malformed DW_LNE_define_file operation"}
+ return false
+ }
+ r.updateFile()
-// A lineHdr holds the relevant information from a line number
-// program header.
-type lineHdr struct {
- version uint16 // version of line number encoding
- minInsnLen uint8 // minimum instruction length
- maxOpsPerInsn uint8 // maximum number of ops per instruction
- defStmt bool // initial value of stmt register
- lineBase int8 // line adjustment base
- lineRange uint8 // line adjustment step
- opBase uint8 // base of special opcode values
- opLen []uint8 // lengths of standard opcodes
- dirs []string // directories
- files []string // file names
-}
+ case lneSetDiscriminator:
+ // [DWARF4 6.2.5.3]
+ r.state.Discriminator = int(r.buf.uint())
+ }
-// parseLine parses the line number information for a compilation unit
-func (d *Data) parseLine(u *unit) error {
- if u.lineoff+1 == 0 {
- return errors.New("unknown line offset")
- }
- b := makeBuf(d, u, "line", u.lineoff, d.line[u.lineoff:])
- len := uint64(b.uint32())
- dwarf64 := false
- if len == 0xffffffff {
- len = b.uint64()
- dwarf64 = true
- }
- end := b.off + Offset(len)
- hdr := d.parseLineHdr(u, &b, dwarf64)
- if b.err == nil {
- d.parseLineProgram(u, &b, hdr, end)
- }
- return b.err
-}
+ r.buf.skip(int(startOff + length - r.buf.off))
-// parseLineHdr parses a line number program header.
-func (d *Data) parseLineHdr(u *unit, b *buf, dwarf64 bool) (hdr lineHdr) {
- hdr.version = b.uint16()
- if hdr.version < 2 || hdr.version > 4 {
- b.error("unsupported DWARF version " + strconv.Itoa(int(hdr.version)))
- return
- }
+ if opcode == lneEndSequence {
+ return true
+ }
- var hlen Offset
- if dwarf64 {
- hlen = Offset(b.uint64())
- } else {
- hlen = Offset(b.uint32())
- }
- end := b.off + hlen
+ // Standard opcodes [DWARF2 6.2.5.2]
+ case lnsCopy:
+ goto emit
- hdr.minInsnLen = b.uint8()
- if hdr.version < 4 {
- hdr.maxOpsPerInsn = 1
- } else {
- hdr.maxOpsPerInsn = b.uint8()
- }
+ case lnsAdvancePC:
+ r.advancePC(int(r.buf.uint()))
- if b.uint8() == 0 {
- hdr.defStmt = false
- } else {
- hdr.defStmt = true
- }
- hdr.lineBase = int8(b.uint8())
- hdr.lineRange = b.uint8()
- hdr.opBase = b.uint8()
- hdr.opLen = b.bytes(int(hdr.opBase - 1))
+ case lnsAdvanceLine:
+ r.state.Line += int(r.buf.int())
- for d := b.string(); len(d) > 0; d = b.string() {
- hdr.dirs = append(hdr.dirs, d)
- }
+ case lnsSetFile:
+ r.fileIndex = int(r.buf.uint())
+ r.updateFile()
- for f := b.string(); len(f) > 0; f = b.string() {
- d := b.uint()
- if !filepath.IsAbs(f) {
- if d > 0 {
- if d > uint64(len(hdr.dirs)) {
- b.error("DWARF directory index out of range")
- return
- }
- f = filepath.Join(hdr.dirs[d-1], f)
- } else if u.dir != "" {
- f = filepath.Join(u.dir, f)
- }
+ case lnsSetColumn:
+ r.state.Column = int(r.buf.uint())
+
+ case lnsNegateStmt:
+ r.state.IsStmt = !r.state.IsStmt
+
+ case lnsSetBasicBlock:
+ r.state.BasicBlock = true
+
+ case lnsConstAddPC:
+ r.advancePC((255 - r.opcodeBase) / r.lineRange)
+
+ case lnsFixedAdvancePC:
+ r.state.Address += uint64(r.buf.uint16())
+
+ // DWARF3 standard opcodes [DWARF3 6.2.5.2]
+ case lnsSetPrologueEnd:
+ r.state.PrologueEnd = true
+
+ case lnsSetEpilogueBegin:
+ r.state.EpilogueBegin = true
+
+ case lnsSetISA:
+ r.state.ISA = int(r.buf.uint())
+
+ default:
+ // Unhandled standard opcode. Skip the number of
+ // arguments that the prologue says this opcode has.
+ for i := 0; i < r.opcodeLengths[opcode]; i++ {
+ r.buf.uint()
}
- b.uint() // file's last mtime
- b.uint() // file length
- hdr.files = append(hdr.files, f)
}
+ return false
- if end > b.off {
- b.bytes(int(end - b.off))
- }
+emit:
+ *entry = r.state
+ r.state.BasicBlock = false
+ r.state.PrologueEnd = false
+ r.state.EpilogueBegin = false
+ r.state.Discriminator = 0
+ return true
+}
+
+// advancePC advances "operation pointer" (the combination of Address
+// and OpIndex) in r.state by opAdvance steps.
+func (r *LineReader) advancePC(opAdvance int) {
+ opIndex := r.state.OpIndex + opAdvance
+ r.state.Address += uint64(r.minInstructionLength * (opIndex / r.maxOpsPerInstruction))
+ r.state.OpIndex = opIndex % r.maxOpsPerInstruction
+}
+
+// A LineReaderPos represents a position in a line table.
+type LineReaderPos struct {
+ // off is the current offset in the DWARF line section.
+ off Offset
+ // numFileEntries is the length of fileEntries.
+ numFileEntries int
+ // state and fileIndex are the statement machine state at
+ // offset off.
+ state LineEntry
+ fileIndex int
+}
+
+// Tell returns the current position in the line table.
+func (r *LineReader) Tell() LineReaderPos {
+ return LineReaderPos{r.buf.off, len(r.fileEntries), r.state, r.fileIndex}
+}
+
+// Seek restores the line table reader to a position returned by Tell.
+//
+// The argument pos must have been returned by a call to Tell on this
+// line table.
+func (r *LineReader) Seek(pos LineReaderPos) {
+ r.buf.off = pos.off
+ r.buf.data = r.section[r.buf.off:r.endOffset]
+ r.fileEntries = r.fileEntries[:pos.numFileEntries]
+ r.state = pos.state
+ r.fileIndex = pos.fileIndex
+}
+
+// Reset repositions the line table reader at the beginning of the
+// line table.
+func (r *LineReader) Reset() {
+ // Reset buffer to the line number program offset.
+ r.buf.off = r.programOffset
+ r.buf.data = r.section[r.buf.off:r.endOffset]
- return
+ // Reset file entries list.
+ r.fileEntries = r.fileEntries[:r.initialFileEntries]
+
+ // Reset line number program state.
+ r.resetState()
}
-// parseLineProgram parses a line program, adding information to
-// d.lineInfo as it goes.
-func (d *Data) parseLineProgram(u *unit, b *buf, hdr lineHdr, end Offset) {
- address := uint64(0)
- line := 1
- resetLineInfo := Line{
- Filename: "",
+// resetState resets r.state to its default values
+func (r *LineReader) resetState() {
+ // Reset the state machine registers to the defaults given in
+ // [DWARF4 6.2.2].
+ r.state = LineEntry{
+ Address: 0,
OpIndex: 0,
- Line: 0,
+ File: nil,
+ Line: 1,
Column: 0,
+ IsStmt: r.defaultIsStmt,
+ BasicBlock: false,
+ PrologueEnd: false,
+ EpilogueBegin: false,
ISA: 0,
Discriminator: 0,
- Stmt: hdr.defStmt,
- Block: false,
- EndPrologue: false,
- BeginEpilogue: false,
}
- if len(hdr.files) > 0 {
- resetLineInfo.Filename = hdr.files[0]
+ r.fileIndex = 1
+ r.updateFile()
+}
+
+// ErrUnknownPC is the error returned by LineReader.ScanPC when the
+// seek PC is not covered by any entry in the line table.
+var ErrUnknownPC = errors.New("ErrUnknownPC")
+
+// SeekPC sets *entry to the LineEntry that includes pc and positions
+// the reader on the next entry in the line table. If necessary, this
+// will seek backwards to find pc.
+//
+// If pc is not covered by any entry in this line table, SeekPC
+// returns ErrUnknownPC. In this case, *entry and the final seek
+// position are unspecified.
+//
+// Note that DWARF line tables only permit sequential, forward scans.
+// Hence, in the worst case, this takes time linear in the size of the
+// line table. If the caller wishes to do repeated fast PC lookups, it
+// should build an appropriate index of the line table.
+func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error {
+ if err := r.Next(entry); err != nil {
+ return err
}
- lineInfo := resetLineInfo
-
- var lines []mapLineInfo
-
- minInsnLen := uint64(hdr.minInsnLen)
- maxOpsPerInsn := uint64(hdr.maxOpsPerInsn)
- lineBase := int(hdr.lineBase)
- lineRange := hdr.lineRange
- newLineInfo := true
- for b.off < end && b.err == nil {
- op := b.uint8()
- if op >= hdr.opBase {
- // This is a special opcode.
- op -= hdr.opBase
- advance := uint64(op / hdr.lineRange)
- opIndex := uint64(lineInfo.OpIndex)
- address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
- newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
- line += lineBase + int(op%lineRange)
- if newOpIndex != lineInfo.OpIndex {
- lineInfo.OpIndex = newOpIndex
- newLineInfo = true
- }
- lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
- } else if op == LineExtendedOp {
- c := b.uint()
- op = b.uint8()
- switch op {
- case LineExtEndSequence:
- u.lines = append(u.lines, lines...)
- lineInfo = resetLineInfo
- lines = nil
- newLineInfo = true
- case LineExtSetAddress:
- address = b.addr()
- case LineExtDefineFile:
- f := b.string()
- d := b.uint()
- b.uint() // mtime
- b.uint() // length
- if d > 0 && !filepath.IsAbs(f) {
- if d >= uint64(len(hdr.dirs)) {
- b.error("DWARF directory index out of range")
- return
- }
- f = filepath.Join(hdr.dirs[d-1], f)
- }
- hdr.files = append(hdr.files, f)
- case LineExtSetDiscriminator:
- lineInfo.Discriminator = int(b.uint())
- newLineInfo = true
- default:
- if c > 0 {
- b.bytes(int(c) - 1)
- }
- }
- } else {
- switch op {
- case LineCopy:
- lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
- case LineAdvancePC:
- advance := b.uint()
- opIndex := uint64(lineInfo.OpIndex)
- address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
- newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
- if newOpIndex != lineInfo.OpIndex {
- lineInfo.OpIndex = newOpIndex
- newLineInfo = true
- }
- case LineAdvanceLine:
- line += int(b.int())
- case LineSetFile:
- i := b.uint()
- if i > uint64(len(hdr.files)) {
- b.error("DWARF file number out of range")
- return
- }
- lineInfo.Filename = hdr.files[i-1]
- newLineInfo = true
- case LineSetColumn:
- lineInfo.Column = int(b.uint())
- newLineInfo = true
- case LineNegateStmt:
- lineInfo.Stmt = !lineInfo.Stmt
- newLineInfo = true
- case LineSetBasicBlock:
- lineInfo.Block = true
- newLineInfo = true
- case LineConstAddPC:
- op = 255 - hdr.opBase
- advance := uint64(op / hdr.lineRange)
- opIndex := uint64(lineInfo.OpIndex)
- address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
- newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
- if newOpIndex != lineInfo.OpIndex {
- lineInfo.OpIndex = newOpIndex
- newLineInfo = true
- }
- case LineFixedAdvancePC:
- address += uint64(b.uint16())
- if lineInfo.OpIndex != 0 {
- lineInfo.OpIndex = 0
- newLineInfo = true
- }
- case LineSetPrologueEnd:
- lineInfo.EndPrologue = true
- newLineInfo = true
- case LineSetEpilogueBegin:
- lineInfo.BeginEpilogue = true
- newLineInfo = true
- case LineSetISA:
- lineInfo.ISA = int(b.uint())
- newLineInfo = true
- default:
- if int(op) >= len(hdr.opLen) {
- b.error("DWARF line opcode has unknown length")
- return
- }
- for i := hdr.opLen[op-1]; i > 0; i-- {
- b.int()
- }
- }
+ if entry.Address > pc {
+ // We're too far. Start at the beginning of the table.
+ r.Reset()
+ if err := r.Next(entry); err != nil {
+ return err
+ }
+ if entry.Address > pc {
+ // The whole table starts after pc.
+ r.Reset()
+ return ErrUnknownPC
}
}
-}
-// addLine adds the current address and line to lines using lineInfo.
-// If newLineInfo is true this is a new lineInfo. This returns the
-// updated lines, lineInfo, and newLineInfo.
-func (d *Data) addLine(lines []mapLineInfo, lineInfo Line, address uint64, line int, newLineInfo bool) ([]mapLineInfo, Line, bool) {
- if newLineInfo {
- if len(lines) > 0 {
- sort.Sort(lines[len(lines)-1].addrs)
- p := &lines[len(lines)-1]
- if len(p.addrs) > 0 && address > p.addrs[len(p.addrs)-1].pc {
- p.addrs = append(p.addrs, oneLineInfo{address, p.addrs[len(p.addrs)-1].line})
+ // Scan until we pass pc, then back up one.
+ for {
+ var next LineEntry
+ pos := r.Tell()
+ if err := r.Next(&next); err != nil {
+ if err == io.EOF {
+ return ErrUnknownPC
}
+ return err
}
- lines = append(lines, mapLineInfo{line: lineInfo})
- }
- p := &lines[len(lines)-1]
- p.addrs = append(p.addrs, oneLineInfo{address, line})
-
- if lineInfo.Block || lineInfo.EndPrologue || lineInfo.BeginEpilogue || lineInfo.Discriminator != 0 {
- lineInfo.Block = false
- lineInfo.EndPrologue = false
- lineInfo.BeginEpilogue = false
- lineInfo.Discriminator = 0
- newLineInfo = true
- } else {
- newLineInfo = false
+ if next.Address > pc {
+ if entry.EndSequence {
+ // pc is in a hole in the table.
+ return ErrUnknownPC
+ }
+ // entry is the desired entry. Back up the
+ // cursor to "next" and return success.
+ r.Seek(pos)
+ return nil
+ }
+ *entry = next
}
-
- return lines, lineInfo, newLineInfo
}