OSDN Git Service

debug/elf, debug/dwarf: DWARF line number fixes.
[pf3gnuchains/gcc-fork.git] / libgo / go / debug / dwarf / line.go
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.
4
5 // DWARF line number information.
6
7 package dwarf
8
9 import (
10         "errors"
11         "path/filepath"
12         "sort"
13         "strconv"
14 )
15
16 // A Line holds all the available information about the source code
17 // corresponding to a specific program counter address.
18 type Line struct {
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
29 }
30
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 {
36                 u := &d.unit[i]
37                 if u.pc == nil {
38                         if err := d.readUnitLine(i, u); err != nil {
39                                 return nil, err
40                         }
41                 }
42                 for _, ar := range u.pc {
43                         if pc >= ar.low && pc < ar.high {
44                                 return d.findLine(u, pc)
45                         }
46                 }
47         }
48         return nil, nil
49 }
50
51 // readUnitLine reads in the line number information for a compilation
52 // unit.
53 func (d *Data) readUnitLine(i int, u *unit) error {
54         r := d.unitReader(i)
55         setLineOff := false
56         for {
57                 e, err := r.Next()
58                 if err != nil {
59                         return err
60                 }
61                 if e == nil {
62                         break
63                 }
64                 if r.unit != i {
65                         break
66                 }
67                 switch e.Tag {
68                 case TagCompileUnit, TagSubprogram, TagEntryPoint, TagInlinedSubroutine:
69                         low, lowok := e.Val(AttrLowpc).(uint64)
70                         var high uint64
71                         var highok bool
72                         switch v := e.Val(AttrHighpc).(type) {
73                         case uint64:
74                                 high = v
75                                 highok = true
76                         case int64:
77                                 high = low + uint64(v)
78                                 highok = true
79                         }
80                         if lowok && highok {
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 {
84                                         return err
85                                 }
86                         }
87                         val := e.Val(AttrStmtList)
88                         if val != nil {
89                                 if off, ok := val.(int64); ok {
90                                         u.lineoff = Offset(off)
91                                         setLineOff = true
92                                 } else if off, ok := val.(Offset); ok {
93                                         u.lineoff = off
94                                         setLineOff = true
95                                 } else {
96                                         return errors.New("unrecognized format for DW_ATTR_stmt_list")
97                                 }
98                         }
99                         if dir, ok := e.Val(AttrCompDir).(string); ok {
100                                 u.dir = dir
101                         }
102                 }
103         }
104         if !setLineOff {
105                 u.lineoff = Offset(0)
106                 u.lineoff--
107         }
108         return nil
109 }
110
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:])
114         var highest uint64
115         switch u.addrsize {
116         case 1:
117                 highest = 0xff
118         case 2:
119                 highest = 0xffff
120         case 4:
121                 highest = 0xffffffff
122         case 8:
123                 highest = 0xffffffffffffffff
124         default:
125                 return errors.New("unknown address size")
126         }
127         for {
128                 if b.err != nil {
129                         return b.err
130                 }
131                 low := b.addr()
132                 high := b.addr()
133                 if low == 0 && high == 0 {
134                         return b.err
135                 } else if low == highest {
136                         base = high
137                 } else {
138                         u.pc = append(u.pc, addrRange{low + base, high + base})
139                 }
140         }
141 }
142
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) {
146         if u.lines == nil {
147                 if err := d.parseLine(u); err != nil {
148                         return nil, err
149                 }
150         }
151
152         for _, ln := range u.lines {
153                 if pc < ln.addrs[0].pc || pc > ln.addrs[len(ln.addrs)-1].pc {
154                         continue
155                 }
156                 i := sort.Search(len(ln.addrs),
157                         func(i int) bool { return ln.addrs[i].pc > pc })
158                 i--
159                 p := new(Line)
160                 *p = ln.line
161                 p.Line = ln.addrs[i].line
162                 ret := []*Line{p}
163                 for i++; i < len(ln.addrs) && ln.addrs[i].pc == pc; i++ {
164                         p = new(Line)
165                         *p = ln.line
166                         p.Line = ln.addrs[i].line
167                         ret = append(ret, p)
168                 }
169                 return ret, nil
170         }
171
172         return nil, nil
173 }
174
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)
179         if err != nil {
180                 return "", 0, err
181         }
182         if r == nil {
183                 return "", 0, nil
184         }
185         ln := r[len(r)-1]
186         return ln.Filename, ln.Line, nil
187 }
188
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
195 }
196
197 // A list of lines.  This will be sorted by PC.
198 type lineAddrs []oneLineInfo
199
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] }
203
204 // A oneLineInfo is a single PC and line number.
205 type oneLineInfo struct {
206         pc   uint64
207         line int
208 }
209
210 // A lineHdr holds the relevant information from a line number
211 // program header.
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
223 }
224
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")
229         }
230         b := makeBuf(d, u, "line", u.lineoff, d.line[u.lineoff:])
231         len := uint64(b.uint32())
232         dwarf64 := false
233         if len == 0xffffffff {
234                 len = b.uint64()
235                 dwarf64 = true
236         }
237         end := b.off + Offset(len)
238         hdr := d.parseLineHdr(u, &b, dwarf64)
239         if b.err == nil {
240                 d.parseLineProgram(u, &b, hdr, end)
241         }
242         return b.err
243 }
244
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)))
250                 return
251         }
252
253         var hlen Offset
254         if dwarf64 {
255                 hlen = Offset(b.uint64())
256         } else {
257                 hlen = Offset(b.uint32())
258         }
259         end := b.off + hlen
260
261         hdr.minInsnLen = b.uint8()
262         if hdr.version < 4 {
263                 hdr.maxOpsPerInsn = 1
264         } else {
265                 hdr.maxOpsPerInsn = b.uint8()
266         }
267
268         if b.uint8() == 0 {
269                 hdr.defStmt = false
270         } else {
271                 hdr.defStmt = true
272         }
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))
277
278         for d := b.string(); len(d) > 0; d = b.string() {
279                 hdr.dirs = append(hdr.dirs, d)
280         }
281
282         for f := b.string(); len(f) > 0; f = b.string() {
283                 d := b.uint()
284                 if !filepath.IsAbs(f) {
285                         if d > 0 {
286                                 if d > uint64(len(hdr.dirs)) {
287                                         b.error("DWARF directory index out of range")
288                                         return
289                                 }
290                                 f = filepath.Join(hdr.dirs[d-1], f)
291                         } else if u.dir != "" {
292                                 f = filepath.Join(u.dir, f)
293                         }
294                 }
295                 b.uint() // file's last mtime
296                 b.uint() // file length
297                 hdr.files = append(hdr.files, f)
298         }
299
300         if end > b.off {
301                 b.bytes(int(end - b.off))
302         }
303
304         return
305 }
306
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) {
310         address := uint64(0)
311         line := 1
312         resetLineInfo := Line{
313                 Filename:      "",
314                 OpIndex:       0,
315                 Line:          0,
316                 Column:        0,
317                 ISA:           0,
318                 Discriminator: 0,
319                 Stmt:          hdr.defStmt,
320                 Block:         false,
321                 EndPrologue:   false,
322                 BeginEpilogue: false,
323         }
324         if len(hdr.files) > 0 {
325                 resetLineInfo.Filename = hdr.files[0]
326         }
327         lineInfo := resetLineInfo
328
329         var lines []mapLineInfo
330
331         minInsnLen := uint64(hdr.minInsnLen)
332         maxOpsPerInsn := uint64(hdr.maxOpsPerInsn)
333         lineBase := int(hdr.lineBase)
334         lineRange := hdr.lineRange
335         newLineInfo := true
336         for b.off < end && b.err == nil {
337                 op := b.uint8()
338                 if op >= hdr.opBase {
339                         // This is a special opcode.
340                         op -= hdr.opBase
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
348                                 newLineInfo = true
349                         }
350                         lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
351                 } else if op == LineExtendedOp {
352                         c := b.uint()
353                         op = b.uint8()
354                         switch op {
355                         case LineExtEndSequence:
356                                 u.lines = append(u.lines, lines...)
357                                 lineInfo = resetLineInfo
358                                 lines = nil
359                                 newLineInfo = true
360                         case LineExtSetAddress:
361                                 address = b.addr()
362                         case LineExtDefineFile:
363                                 f := b.string()
364                                 d := b.uint()
365                                 b.uint() // mtime
366                                 b.uint() // length
367                                 if d > 0 && !filepath.IsAbs(f) {
368                                         if d >= uint64(len(hdr.dirs)) {
369                                                 b.error("DWARF directory index out of range")
370                                                 return
371                                         }
372                                         f = filepath.Join(hdr.dirs[d-1], f)
373                                 }
374                                 hdr.files = append(hdr.files, f)
375                         case LineExtSetDiscriminator:
376                                 lineInfo.Discriminator = int(b.uint())
377                                 newLineInfo = true
378                         default:
379                                 if c > 0 {
380                                         b.bytes(int(c) - 1)
381                                 }
382                         }
383                 } else {
384                         switch op {
385                         case LineCopy:
386                                 lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
387                         case LineAdvancePC:
388                                 advance := b.uint()
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
394                                         newLineInfo = true
395                                 }
396                         case LineAdvanceLine:
397                                 line += int(b.int())
398                         case LineSetFile:
399                                 i := b.uint()
400                                 if i > uint64(len(hdr.files)) {
401                                         b.error("DWARF file number out of range")
402                                         return
403                                 }
404                                 lineInfo.Filename = hdr.files[i-1]
405                                 newLineInfo = true
406                         case LineSetColumn:
407                                 lineInfo.Column = int(b.uint())
408                                 newLineInfo = true
409                         case LineNegateStmt:
410                                 lineInfo.Stmt = !lineInfo.Stmt
411                                 newLineInfo = true
412                         case LineSetBasicBlock:
413                                 lineInfo.Block = true
414                                 newLineInfo = true
415                         case LineConstAddPC:
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
423                                         newLineInfo = true
424                                 }
425                         case LineFixedAdvancePC:
426                                 address += uint64(b.uint16())
427                                 if lineInfo.OpIndex != 0 {
428                                         lineInfo.OpIndex = 0
429                                         newLineInfo = true
430                                 }
431                         case LineSetPrologueEnd:
432                                 lineInfo.EndPrologue = true
433                                 newLineInfo = true
434                         case LineSetEpilogueBegin:
435                                 lineInfo.BeginEpilogue = true
436                                 newLineInfo = true
437                         case LineSetISA:
438                                 lineInfo.ISA = int(b.uint())
439                                 newLineInfo = true
440                         default:
441                                 if int(op) >= len(hdr.opLen) {
442                                         b.error("DWARF line opcode has unknown length")
443                                         return
444                                 }
445                                 for i := hdr.opLen[op-1]; i > 0; i-- {
446                                         b.int()
447                                 }
448                         }
449                 }
450         }
451 }
452
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) {
457         if newLineInfo {
458                 if len(lines) > 0 {
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})
463                         }
464                 }
465                 lines = append(lines, mapLineInfo{line: lineInfo})
466         }
467         p := &lines[len(lines)-1]
468         p.addrs = append(p.addrs, oneLineInfo{address, line})
469
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
475                 newLineInfo = true
476         } else {
477                 newLineInfo = false
478         }
479
480         return lines, lineInfo, newLineInfo
481 }