1 // Copyright 2012 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.
5 // DWARF line number information.
16 // A Line holds all the available information about the source code
17 // corresponding to a specific program counter address.
19 Filename string // source file name
20 OpIndex int // index of operation in VLIW instruction
21 Line int // line number
22 Column int // column number
23 ISA int // instruction set code
24 Discriminator int // block discriminator
25 Stmt bool // instruction starts statement
26 Block bool // instruction starts basic block
27 EndPrologue bool // instruction ends function prologue
28 BeginEpilogue bool // instruction begins function epilogue
31 // LineForPc returns the line number information for a program counter
32 // address, if any. When this returns multiple Line structures in a
33 // context where only one can be used, the last one is the best.
34 func (d *Data) LineForPC(pc uint64) ([]*Line, error) {
35 for i := range d.unit {
38 if err := d.readUnitLine(i, u); err != nil {
42 for _, ar := range u.pc {
43 if pc >= ar.low && pc < ar.high {
44 return d.findLine(u, pc)
51 // readUnitLine reads in the line number information for a compilation
53 func (d *Data) readUnitLine(i int, u *unit) error {
68 case TagCompileUnit, TagSubprogram, TagEntryPoint, TagInlinedSubroutine:
69 low, lowok := e.Val(AttrLowpc).(uint64)
72 switch v := e.Val(AttrHighpc).(type) {
77 high = low + uint64(v)
81 u.pc = append(u.pc, addrRange{low, high})
82 } else if off, ok := e.Val(AttrRanges).(Offset); ok {
83 if err := d.readAddressRanges(off, low, u); err != nil {
87 val := e.Val(AttrStmtList)
89 if off, ok := val.(int64); ok {
90 u.lineoff = Offset(off)
92 } else if off, ok := val.(Offset); ok {
96 return errors.New("unrecognized format for DW_ATTR_stmt_list")
99 if dir, ok := e.Val(AttrCompDir).(string); ok {
105 u.lineoff = Offset(0)
111 // readAddressRanges adds address ranges to a unit.
112 func (d *Data) readAddressRanges(off Offset, base uint64, u *unit) error {
113 b := makeBuf(d, u, "ranges", off, d.ranges[off:])
123 highest = 0xffffffffffffffff
125 return errors.New("unknown address size")
133 if low == 0 && high == 0 {
135 } else if low == highest {
138 u.pc = append(u.pc, addrRange{low + base, high + base})
143 // findLine finds the line information for a PC value, given the unit
144 // containing the information.
145 func (d *Data) findLine(u *unit, pc uint64) ([]*Line, error) {
147 if err := d.parseLine(u); err != nil {
152 for _, ln := range u.lines {
153 if pc < ln.addrs[0].pc || pc > ln.addrs[len(ln.addrs)-1].pc {
156 i := sort.Search(len(ln.addrs),
157 func(i int) bool { return ln.addrs[i].pc > pc })
161 p.Line = ln.addrs[i].line
163 for i++; i < len(ln.addrs) && ln.addrs[i].pc == pc; i++ {
166 p.Line = ln.addrs[i].line
175 // FileLine returns the file name and line number for a program
176 // counter address, or "", 0 if unknown.
177 func (d *Data) FileLine(pc uint64) (string, int, error) {
178 r, err := d.LineForPC(pc)
186 return ln.Filename, ln.Line, nil
189 // A mapLineInfo holds the PC values and line numbers associated with
190 // a single Line structure. This representation is chosen to reduce
191 // memory usage based on typical debug info.
192 type mapLineInfo struct {
193 line Line // line.Line will be zero
194 addrs lineAddrs // sorted by PC
197 // A list of lines. This will be sorted by PC.
198 type lineAddrs []oneLineInfo
200 func (p lineAddrs) Len() int { return len(p) }
201 func (p lineAddrs) Less(i, j int) bool { return p[i].pc < p[j].pc }
202 func (p lineAddrs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
204 // A oneLineInfo is a single PC and line number.
205 type oneLineInfo struct {
210 // A lineHdr holds the relevant information from a line number
212 type lineHdr struct {
213 version uint16 // version of line number encoding
214 minInsnLen uint8 // minimum instruction length
215 maxOpsPerInsn uint8 // maximum number of ops per instruction
216 defStmt bool // initial value of stmt register
217 lineBase int8 // line adjustment base
218 lineRange uint8 // line adjustment step
219 opBase uint8 // base of special opcode values
220 opLen []uint8 // lengths of standard opcodes
221 dirs []string // directories
222 files []string // file names
225 // parseLine parses the line number information for a compilation unit
226 func (d *Data) parseLine(u *unit) error {
227 if u.lineoff+1 == 0 {
228 return errors.New("unknown line offset")
230 b := makeBuf(d, u, "line", u.lineoff, d.line[u.lineoff:])
231 len := uint64(b.uint32())
233 if len == 0xffffffff {
237 end := b.off + Offset(len)
238 hdr := d.parseLineHdr(u, &b, dwarf64)
240 d.parseLineProgram(u, &b, hdr, end)
245 // parseLineHdr parses a line number program header.
246 func (d *Data) parseLineHdr(u *unit, b *buf, dwarf64 bool) (hdr lineHdr) {
247 hdr.version = b.uint16()
248 if hdr.version < 2 || hdr.version > 4 {
249 b.error("unsupported DWARF version " + strconv.Itoa(int(hdr.version)))
255 hlen = Offset(b.uint64())
257 hlen = Offset(b.uint32())
261 hdr.minInsnLen = b.uint8()
263 hdr.maxOpsPerInsn = 1
265 hdr.maxOpsPerInsn = b.uint8()
273 hdr.lineBase = int8(b.uint8())
274 hdr.lineRange = b.uint8()
275 hdr.opBase = b.uint8()
276 hdr.opLen = b.bytes(int(hdr.opBase - 1))
278 for d := b.string(); len(d) > 0; d = b.string() {
279 hdr.dirs = append(hdr.dirs, d)
282 for f := b.string(); len(f) > 0; f = b.string() {
284 if !filepath.IsAbs(f) {
286 if d > uint64(len(hdr.dirs)) {
287 b.error("DWARF directory index out of range")
290 f = filepath.Join(hdr.dirs[d-1], f)
291 } else if u.dir != "" {
292 f = filepath.Join(u.dir, f)
295 b.uint() // file's last mtime
296 b.uint() // file length
297 hdr.files = append(hdr.files, f)
301 b.bytes(int(end - b.off))
307 // parseLineProgram parses a line program, adding information to
308 // d.lineInfo as it goes.
309 func (d *Data) parseLineProgram(u *unit, b *buf, hdr lineHdr, end Offset) {
312 resetLineInfo := Line{
322 BeginEpilogue: false,
324 if len(hdr.files) > 0 {
325 resetLineInfo.Filename = hdr.files[0]
327 lineInfo := resetLineInfo
329 var lines []mapLineInfo
331 minInsnLen := uint64(hdr.minInsnLen)
332 maxOpsPerInsn := uint64(hdr.maxOpsPerInsn)
333 lineBase := int(hdr.lineBase)
334 lineRange := hdr.lineRange
336 for b.off < end && b.err == nil {
338 if op >= hdr.opBase {
339 // This is a special opcode.
341 advance := uint64(op / hdr.lineRange)
342 opIndex := uint64(lineInfo.OpIndex)
343 address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
344 newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
345 line += lineBase + int(op%lineRange)
346 if newOpIndex != lineInfo.OpIndex {
347 lineInfo.OpIndex = newOpIndex
350 lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
351 } else if op == LineExtendedOp {
355 case LineExtEndSequence:
356 u.lines = append(u.lines, lines...)
357 lineInfo = resetLineInfo
360 case LineExtSetAddress:
362 case LineExtDefineFile:
367 if d > 0 && !filepath.IsAbs(f) {
368 if d >= uint64(len(hdr.dirs)) {
369 b.error("DWARF directory index out of range")
372 f = filepath.Join(hdr.dirs[d-1], f)
374 hdr.files = append(hdr.files, f)
375 case LineExtSetDiscriminator:
376 lineInfo.Discriminator = int(b.uint())
386 lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
389 opIndex := uint64(lineInfo.OpIndex)
390 address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
391 newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
392 if newOpIndex != lineInfo.OpIndex {
393 lineInfo.OpIndex = newOpIndex
396 case LineAdvanceLine:
400 if i > uint64(len(hdr.files)) {
401 b.error("DWARF file number out of range")
404 lineInfo.Filename = hdr.files[i-1]
407 lineInfo.Column = int(b.uint())
410 lineInfo.Stmt = !lineInfo.Stmt
412 case LineSetBasicBlock:
413 lineInfo.Block = true
416 op = 255 - hdr.opBase
417 advance := uint64(op / hdr.lineRange)
418 opIndex := uint64(lineInfo.OpIndex)
419 address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
420 newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
421 if newOpIndex != lineInfo.OpIndex {
422 lineInfo.OpIndex = newOpIndex
425 case LineFixedAdvancePC:
426 address += uint64(b.uint16())
427 if lineInfo.OpIndex != 0 {
431 case LineSetPrologueEnd:
432 lineInfo.EndPrologue = true
434 case LineSetEpilogueBegin:
435 lineInfo.BeginEpilogue = true
438 lineInfo.ISA = int(b.uint())
441 if int(op) >= len(hdr.opLen) {
442 b.error("DWARF line opcode has unknown length")
445 for i := hdr.opLen[op-1]; i > 0; i-- {
453 // addLine adds the current address and line to lines using lineInfo.
454 // If newLineInfo is true this is a new lineInfo. This returns the
455 // updated lines, lineInfo, and newLineInfo.
456 func (d *Data) addLine(lines []mapLineInfo, lineInfo Line, address uint64, line int, newLineInfo bool) ([]mapLineInfo, Line, bool) {
459 sort.Sort(lines[len(lines)-1].addrs)
460 p := &lines[len(lines)-1]
461 if len(p.addrs) > 0 && address > p.addrs[len(p.addrs)-1].pc {
462 p.addrs = append(p.addrs, oneLineInfo{address, p.addrs[len(p.addrs)-1].line})
465 lines = append(lines, mapLineInfo{line: lineInfo})
467 p := &lines[len(lines)-1]
468 p.addrs = append(p.addrs, oneLineInfo{address, line})
470 if lineInfo.Block || lineInfo.EndPrologue || lineInfo.BeginEpilogue || lineInfo.Discriminator != 0 {
471 lineInfo.Block = false
472 lineInfo.EndPrologue = false
473 lineInfo.BeginEpilogue = false
474 lineInfo.Discriminator = 0
480 return lines, lineInfo, newLineInfo