Source file src/pkg/debug/dwarf/line.go
1
2
3
4
5 package dwarf
6
7 import (
8 "errors"
9 "fmt"
10 "io"
11 "path"
12 "strings"
13 )
14
15
16
17
18
19
20 type LineReader struct {
21 buf buf
22
23
24 section []byte
25
26
27 version uint16
28 minInstructionLength int
29 maxOpsPerInstruction int
30 defaultIsStmt bool
31 lineBase int
32 lineRange int
33 opcodeBase int
34 opcodeLengths []int
35 directories []string
36 fileEntries []*LineFile
37
38 programOffset Offset
39 endOffset Offset
40
41 initialFileEntries int
42
43
44 state LineEntry
45 fileIndex int
46 }
47
48
49 type LineEntry struct {
50
51
52
53
54 Address uint64
55
56
57
58
59
60
61
62 OpIndex int
63
64
65
66 File *LineFile
67
68
69
70
71
72 Line int
73
74
75
76
77 Column int
78
79
80
81
82 IsStmt bool
83
84
85
86 BasicBlock bool
87
88
89
90
91
92
93 PrologueEnd bool
94
95
96
97
98
99
100 EpilogueBegin bool
101
102
103
104
105
106
107 ISA int
108
109
110
111
112
113
114
115
116 Discriminator int
117
118
119
120
121
122
123
124 EndSequence bool
125 }
126
127
128 type LineFile struct {
129 Name string
130 Mtime uint64
131 Length int
132 }
133
134
135
136
137
138 func (d *Data) LineReader(cu *Entry) (*LineReader, error) {
139 if d.line == nil {
140
141 return nil, nil
142 }
143
144
145 off, ok := cu.Val(AttrStmtList).(int64)
146 if !ok {
147
148 return nil, nil
149 }
150 if off > int64(len(d.line)) {
151 return nil, errors.New("AttrStmtList value out of range")
152 }
153
154
155 compDir, _ := cu.Val(AttrCompDir).(string)
156
157
158 u := &d.unit[d.offsetToUnit(cu.Offset)]
159 buf := makeBuf(d, u, "line", Offset(off), d.line[off:])
160
161 r := LineReader{buf: buf, section: d.line, directories: []string{compDir}}
162
163
164 if err := r.readHeader(); err != nil {
165 return nil, err
166 }
167
168
169 r.Reset()
170
171 return &r, nil
172 }
173
174
175
176 func (r *LineReader) readHeader() error {
177 buf := &r.buf
178
179
180 hdrOffset := buf.off
181 unitLength, dwarf64 := buf.unitLength()
182 r.endOffset = buf.off + unitLength
183 if r.endOffset > buf.off+Offset(len(buf.data)) {
184 return DecodeError{"line", hdrOffset, fmt.Sprintf("line table end %d exceeds section size %d", r.endOffset, buf.off+Offset(len(buf.data)))}
185 }
186 r.version = buf.uint16()
187 if buf.err == nil && (r.version < 2 || r.version > 4) {
188
189
190
191
192
193 return DecodeError{"line", hdrOffset, fmt.Sprintf("unknown line table version %d", r.version)}
194 }
195 var headerLength Offset
196 if dwarf64 {
197 headerLength = Offset(buf.uint64())
198 } else {
199 headerLength = Offset(buf.uint32())
200 }
201 r.programOffset = buf.off + headerLength
202 r.minInstructionLength = int(buf.uint8())
203 if r.version >= 4 {
204
205 r.maxOpsPerInstruction = int(buf.uint8())
206 } else {
207 r.maxOpsPerInstruction = 1
208 }
209 r.defaultIsStmt = buf.uint8() != 0
210 r.lineBase = int(int8(buf.uint8()))
211 r.lineRange = int(buf.uint8())
212
213
214 if buf.err != nil {
215 return buf.err
216 }
217 if r.maxOpsPerInstruction == 0 {
218 return DecodeError{"line", hdrOffset, "invalid maximum operations per instruction: 0"}
219 }
220 if r.lineRange == 0 {
221 return DecodeError{"line", hdrOffset, "invalid line range: 0"}
222 }
223
224
225 r.opcodeBase = int(buf.uint8())
226 r.opcodeLengths = make([]int, r.opcodeBase)
227 for i := 1; i < r.opcodeBase; i++ {
228 r.opcodeLengths[i] = int(buf.uint8())
229 }
230
231
232 if buf.err != nil {
233 return buf.err
234 }
235 for i, length := range r.opcodeLengths {
236 if known, ok := knownOpcodeLengths[i]; ok && known != length {
237 return DecodeError{"line", hdrOffset, fmt.Sprintf("opcode %d expected to have length %d, but has length %d", i, known, length)}
238 }
239 }
240
241
242
243 for {
244 directory := buf.string()
245 if buf.err != nil {
246 return buf.err
247 }
248 if len(directory) == 0 {
249 break
250 }
251 if !pathIsAbs(directory) {
252
253
254 directory = pathJoin(r.directories[0], directory)
255 }
256 r.directories = append(r.directories, directory)
257 }
258
259
260
261 r.fileEntries = make([]*LineFile, 1)
262 for {
263 if done, err := r.readFileEntry(); err != nil {
264 return err
265 } else if done {
266 break
267 }
268 }
269 r.initialFileEntries = len(r.fileEntries)
270
271 return buf.err
272 }
273
274
275
276
277 func (r *LineReader) readFileEntry() (bool, error) {
278 name := r.buf.string()
279 if r.buf.err != nil {
280 return false, r.buf.err
281 }
282 if len(name) == 0 {
283 return true, nil
284 }
285 off := r.buf.off
286 dirIndex := int(r.buf.uint())
287 if !pathIsAbs(name) {
288 if dirIndex >= len(r.directories) {
289 return false, DecodeError{"line", off, "directory index too large"}
290 }
291 name = pathJoin(r.directories[dirIndex], name)
292 }
293 mtime := r.buf.uint()
294 length := int(r.buf.uint())
295
296 r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length})
297 return false, nil
298 }
299
300
301
302 func (r *LineReader) updateFile() {
303 if r.fileIndex < len(r.fileEntries) {
304 r.state.File = r.fileEntries[r.fileIndex]
305 } else {
306 r.state.File = nil
307 }
308 }
309
310
311
312
313
314
315
316 func (r *LineReader) Next(entry *LineEntry) error {
317 if r.buf.err != nil {
318 return r.buf.err
319 }
320
321
322
323 for {
324 if len(r.buf.data) == 0 {
325 return io.EOF
326 }
327 emit := r.step(entry)
328 if r.buf.err != nil {
329 return r.buf.err
330 }
331 if emit {
332 return nil
333 }
334 }
335 }
336
337
338
339 var knownOpcodeLengths = map[int]int{
340 lnsCopy: 0,
341 lnsAdvancePC: 1,
342 lnsAdvanceLine: 1,
343 lnsSetFile: 1,
344 lnsNegateStmt: 0,
345 lnsSetBasicBlock: 0,
346 lnsConstAddPC: 0,
347 lnsSetPrologueEnd: 0,
348 lnsSetEpilogueBegin: 0,
349 lnsSetISA: 1,
350
351
352
353 }
354
355
356
357
358 func (r *LineReader) step(entry *LineEntry) bool {
359 opcode := int(r.buf.uint8())
360
361 if opcode >= r.opcodeBase {
362
363 adjustedOpcode := opcode - r.opcodeBase
364 r.advancePC(adjustedOpcode / r.lineRange)
365 lineDelta := r.lineBase + adjustedOpcode%r.lineRange
366 r.state.Line += lineDelta
367 goto emit
368 }
369
370 switch opcode {
371 case 0:
372
373 length := Offset(r.buf.uint())
374 startOff := r.buf.off
375 opcode := r.buf.uint8()
376
377 switch opcode {
378 case lneEndSequence:
379 r.state.EndSequence = true
380 *entry = r.state
381 r.resetState()
382
383 case lneSetAddress:
384 r.state.Address = r.buf.addr()
385
386 case lneDefineFile:
387 if done, err := r.readFileEntry(); err != nil {
388 r.buf.err = err
389 return false
390 } else if done {
391 r.buf.err = DecodeError{"line", startOff, "malformed DW_LNE_define_file operation"}
392 return false
393 }
394 r.updateFile()
395
396 case lneSetDiscriminator:
397
398 r.state.Discriminator = int(r.buf.uint())
399 }
400
401 r.buf.skip(int(startOff + length - r.buf.off))
402
403 if opcode == lneEndSequence {
404 return true
405 }
406
407
408 case lnsCopy:
409 goto emit
410
411 case lnsAdvancePC:
412 r.advancePC(int(r.buf.uint()))
413
414 case lnsAdvanceLine:
415 r.state.Line += int(r.buf.int())
416
417 case lnsSetFile:
418 r.fileIndex = int(r.buf.uint())
419 r.updateFile()
420
421 case lnsSetColumn:
422 r.state.Column = int(r.buf.uint())
423
424 case lnsNegateStmt:
425 r.state.IsStmt = !r.state.IsStmt
426
427 case lnsSetBasicBlock:
428 r.state.BasicBlock = true
429
430 case lnsConstAddPC:
431 r.advancePC((255 - r.opcodeBase) / r.lineRange)
432
433 case lnsFixedAdvancePC:
434 r.state.Address += uint64(r.buf.uint16())
435
436
437 case lnsSetPrologueEnd:
438 r.state.PrologueEnd = true
439
440 case lnsSetEpilogueBegin:
441 r.state.EpilogueBegin = true
442
443 case lnsSetISA:
444 r.state.ISA = int(r.buf.uint())
445
446 default:
447
448
449 for i := 0; i < r.opcodeLengths[opcode]; i++ {
450 r.buf.uint()
451 }
452 }
453 return false
454
455 emit:
456 *entry = r.state
457 r.state.BasicBlock = false
458 r.state.PrologueEnd = false
459 r.state.EpilogueBegin = false
460 r.state.Discriminator = 0
461 return true
462 }
463
464
465
466 func (r *LineReader) advancePC(opAdvance int) {
467 opIndex := r.state.OpIndex + opAdvance
468 r.state.Address += uint64(r.minInstructionLength * (opIndex / r.maxOpsPerInstruction))
469 r.state.OpIndex = opIndex % r.maxOpsPerInstruction
470 }
471
472
473 type LineReaderPos struct {
474
475 off Offset
476
477 numFileEntries int
478
479
480 state LineEntry
481 fileIndex int
482 }
483
484
485 func (r *LineReader) Tell() LineReaderPos {
486 return LineReaderPos{r.buf.off, len(r.fileEntries), r.state, r.fileIndex}
487 }
488
489
490
491
492
493 func (r *LineReader) Seek(pos LineReaderPos) {
494 r.buf.off = pos.off
495 r.buf.data = r.section[r.buf.off:r.endOffset]
496 r.fileEntries = r.fileEntries[:pos.numFileEntries]
497 r.state = pos.state
498 r.fileIndex = pos.fileIndex
499 }
500
501
502
503 func (r *LineReader) Reset() {
504
505 r.buf.off = r.programOffset
506 r.buf.data = r.section[r.buf.off:r.endOffset]
507
508
509 r.fileEntries = r.fileEntries[:r.initialFileEntries]
510
511
512 r.resetState()
513 }
514
515
516 func (r *LineReader) resetState() {
517
518
519 r.state = LineEntry{
520 Address: 0,
521 OpIndex: 0,
522 File: nil,
523 Line: 1,
524 Column: 0,
525 IsStmt: r.defaultIsStmt,
526 BasicBlock: false,
527 PrologueEnd: false,
528 EpilogueBegin: false,
529 ISA: 0,
530 Discriminator: 0,
531 }
532 r.fileIndex = 1
533 r.updateFile()
534 }
535
536
537
538 var ErrUnknownPC = errors.New("ErrUnknownPC")
539
540
541
542
543
544
545
546
547
548
549
550
551
552 func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error {
553 if err := r.Next(entry); err != nil {
554 return err
555 }
556 if entry.Address > pc {
557
558 r.Reset()
559 if err := r.Next(entry); err != nil {
560 return err
561 }
562 if entry.Address > pc {
563
564 r.Reset()
565 return ErrUnknownPC
566 }
567 }
568
569
570 for {
571 var next LineEntry
572 pos := r.Tell()
573 if err := r.Next(&next); err != nil {
574 if err == io.EOF {
575 return ErrUnknownPC
576 }
577 return err
578 }
579 if next.Address > pc {
580 if entry.EndSequence {
581
582 return ErrUnknownPC
583 }
584
585
586 r.Seek(pos)
587 return nil
588 }
589 *entry = next
590 }
591 }
592
593
594
595
596
597
598
599 func pathIsAbs(path string) bool {
600 _, path = splitDrive(path)
601 return len(path) > 0 && (path[0] == '/' || path[0] == '\\')
602 }
603
604
605
606 func pathJoin(dirname, filename string) string {
607 if len(dirname) == 0 {
608 return filename
609 }
610
611
612
613 drive, dirname := splitDrive(dirname)
614 if drive == "" {
615
616 return path.Join(dirname, filename)
617 }
618
619 drive2, filename := splitDrive(filename)
620 if drive2 != "" {
621 if strings.ToLower(drive) != strings.ToLower(drive2) {
622
623
624 return drive2 + filename
625 }
626
627 }
628 if !(strings.HasSuffix(dirname, "/") || strings.HasSuffix(dirname, `\`)) && dirname != "" {
629 dirname += `\`
630 }
631 return drive + dirname + filename
632 }
633
634
635
636 func splitDrive(path string) (drive, rest string) {
637 if len(path) >= 2 && path[1] == ':' {
638 if c := path[0]; 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' {
639 return path[:2], path[2:]
640 }
641 }
642 if len(path) > 3 && (path[0] == '\\' || path[0] == '/') && (path[1] == '\\' || path[1] == '/') {
643
644 npath := strings.Replace(path, "/", `\`, -1)
645
646 slash1 := strings.IndexByte(npath[2:], '\\') + 2
647 if slash1 > 2 {
648
649 slash2 := strings.IndexByte(npath[slash1+1:], '\\') + slash1 + 1
650 if slash2 > slash1 {
651 return path[:slash2], path[slash2:]
652 }
653 }
654 }
655 return "", path
656 }
657
View as plain text