diff options
Diffstat (limited to 'libgo/go/debug/dwarf/line.go')
-rw-r--r-- | libgo/go/debug/dwarf/line.go | 935 |
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 } |