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 str []byte
27 lineStr []byte
28
29
30 version uint16
31 addrsize int
32 segmentSelectorSize int
33 minInstructionLength int
34 maxOpsPerInstruction int
35 defaultIsStmt bool
36 lineBase int
37 lineRange int
38 opcodeBase int
39 opcodeLengths []int
40 directories []string
41 fileEntries []*LineFile
42
43 programOffset Offset
44 endOffset Offset
45
46 initialFileEntries int
47
48
49 state LineEntry
50 fileIndex int
51 }
52
53
54 type LineEntry struct {
55
56
57
58
59 Address uint64
60
61
62
63
64
65
66
67 OpIndex int
68
69
70
71 File *LineFile
72
73
74
75
76
77 Line int
78
79
80
81
82 Column int
83
84
85
86
87 IsStmt bool
88
89
90
91 BasicBlock bool
92
93
94
95
96
97
98 PrologueEnd bool
99
100
101
102
103
104
105 EpilogueBegin bool
106
107
108
109
110
111
112 ISA int
113
114
115
116
117
118
119
120
121 Discriminator int
122
123
124
125
126
127
128
129 EndSequence bool
130 }
131
132
133 type LineFile struct {
134 Name string
135 Mtime uint64
136 Length int
137 }
138
139
140
141
142
143 func (d *Data) LineReader(cu *Entry) (*LineReader, error) {
144 if d.line == nil {
145
146 return nil, nil
147 }
148
149
150 off, ok := cu.Val(AttrStmtList).(int64)
151 if !ok {
152
153 return nil, nil
154 }
155 if off > int64(len(d.line)) {
156 return nil, errors.New("AttrStmtList value out of range")
157 }
158
159
160 compDir, _ := cu.Val(AttrCompDir).(string)
161
162
163 u := &d.unit[d.offsetToUnit(cu.Offset)]
164 buf := makeBuf(d, u, "line", Offset(off), d.line[off:])
165
166 r := LineReader{
167 buf: buf,
168 section: d.line,
169 str: d.str,
170 lineStr: d.lineStr,
171 }
172
173
174 if err := r.readHeader(compDir); err != nil {
175 return nil, err
176 }
177
178
179 r.Reset()
180
181 return &r, nil
182 }
183
184
185
186 func (r *LineReader) readHeader(compDir string) error {
187 buf := &r.buf
188
189
190 hdrOffset := buf.off
191 unitLength, dwarf64 := buf.unitLength()
192 r.endOffset = buf.off + unitLength
193 if r.endOffset > buf.off+Offset(len(buf.data)) {
194 return DecodeError{"line", hdrOffset, fmt.Sprintf("line table end %d exceeds section size %d", r.endOffset, buf.off+Offset(len(buf.data)))}
195 }
196 r.version = buf.uint16()
197 if buf.err == nil && (r.version < 2 || r.version > 5) {
198
199
200
201
202
203 return DecodeError{"line", hdrOffset, fmt.Sprintf("unknown line table version %d", r.version)}
204 }
205 if r.version >= 5 {
206 r.addrsize = int(buf.uint8())
207 r.segmentSelectorSize = int(buf.uint8())
208 } else {
209 r.addrsize = buf.format.addrsize()
210 r.segmentSelectorSize = 0
211 }
212 var headerLength Offset
213 if dwarf64 {
214 headerLength = Offset(buf.uint64())
215 } else {
216 headerLength = Offset(buf.uint32())
217 }
218 r.programOffset = buf.off + headerLength
219 r.minInstructionLength = int(buf.uint8())
220 if r.version >= 4 {
221
222 r.maxOpsPerInstruction = int(buf.uint8())
223 } else {
224 r.maxOpsPerInstruction = 1
225 }
226 r.defaultIsStmt = buf.uint8() != 0
227 r.lineBase = int(int8(buf.uint8()))
228 r.lineRange = int(buf.uint8())
229
230
231 if buf.err != nil {
232 return buf.err
233 }
234 if r.maxOpsPerInstruction == 0 {
235 return DecodeError{"line", hdrOffset, "invalid maximum operations per instruction: 0"}
236 }
237 if r.lineRange == 0 {
238 return DecodeError{"line", hdrOffset, "invalid line range: 0"}
239 }
240
241
242 r.opcodeBase = int(buf.uint8())
243 r.opcodeLengths = make([]int, r.opcodeBase)
244 for i := 1; i < r.opcodeBase; i++ {
245 r.opcodeLengths[i] = int(buf.uint8())
246 }
247
248
249 if buf.err != nil {
250 return buf.err
251 }
252 for i, length := range r.opcodeLengths {
253 if known, ok := knownOpcodeLengths[i]; ok && known != length {
254 return DecodeError{"line", hdrOffset, fmt.Sprintf("opcode %d expected to have length %d, but has length %d", i, known, length)}
255 }
256 }
257
258 if r.version < 5 {
259
260 r.directories = []string{compDir}
261 for {
262 directory := buf.string()
263 if buf.err != nil {
264 return buf.err
265 }
266 if len(directory) == 0 {
267 break
268 }
269 if !pathIsAbs(directory) {
270
271
272 directory = pathJoin(compDir, directory)
273 }
274 r.directories = append(r.directories, directory)
275 }
276
277
278
279 r.fileEntries = make([]*LineFile, 1)
280 for {
281 if done, err := r.readFileEntry(); err != nil {
282 return err
283 } else if done {
284 break
285 }
286 }
287 } else {
288 dirFormat := r.readLNCTFormat()
289 c := buf.uint()
290 r.directories = make([]string, c)
291 for i := range r.directories {
292 dir, _, _, err := r.readLNCT(dirFormat, dwarf64)
293 if err != nil {
294 return err
295 }
296 r.directories[i] = dir
297 }
298 fileFormat := r.readLNCTFormat()
299 c = buf.uint()
300 r.fileEntries = make([]*LineFile, c)
301 for i := range r.fileEntries {
302 name, mtime, size, err := r.readLNCT(fileFormat, dwarf64)
303 if err != nil {
304 return err
305 }
306 r.fileEntries[i] = &LineFile{name, mtime, int(size)}
307 }
308 }
309
310 r.initialFileEntries = len(r.fileEntries)
311
312 return buf.err
313 }
314
315
316
317
318 type lnctForm struct {
319 lnct int
320 form format
321 }
322
323
324 func (r *LineReader) readLNCTFormat() []lnctForm {
325 c := r.buf.uint8()
326 ret := make([]lnctForm, c)
327 for i := range ret {
328 ret[i].lnct = int(r.buf.uint())
329 ret[i].form = format(r.buf.uint())
330 }
331 return ret
332 }
333
334
335 func (r *LineReader) readLNCT(s []lnctForm, dwarf64 bool) (path string, mtime uint64, size uint64, err error) {
336 var dir string
337 for _, lf := range s {
338 var str string
339 var val uint64
340 switch lf.form {
341 case formString:
342 str = r.buf.string()
343 case formStrp, formLineStrp:
344 var off uint64
345 if dwarf64 {
346 off = r.buf.uint64()
347 } else {
348 off = uint64(r.buf.uint32())
349 }
350 if uint64(int(off)) != off {
351 return "", 0, 0, DecodeError{"line", r.buf.off, "strp/line_strp offset out of range"}
352 }
353 var b1 buf
354 if lf.form == formStrp {
355 b1 = makeBuf(r.buf.dwarf, r.buf.format, "str", 0, r.str)
356 } else {
357 b1 = makeBuf(r.buf.dwarf, r.buf.format, "line_str", 0, r.lineStr)
358 }
359 b1.skip(int(off))
360 str = b1.string()
361 if b1.err != nil {
362 return "", 0, 0, DecodeError{"line", r.buf.off, b1.err.Error()}
363 }
364 case formStrpSup:
365
366 if dwarf64 {
367 r.buf.uint64()
368 } else {
369 r.buf.uint32()
370 }
371 case formStrx:
372
373 r.buf.uint()
374 case formStrx1:
375 r.buf.uint8()
376 case formStrx2:
377 r.buf.uint16()
378 case formStrx3:
379 r.buf.uint24()
380 case formStrx4:
381 r.buf.uint32()
382 case formData1:
383 val = uint64(r.buf.uint8())
384 case formData2:
385 val = uint64(r.buf.uint16())
386 case formData4:
387 val = uint64(r.buf.uint32())
388 case formData8:
389 val = r.buf.uint64()
390 case formData16:
391 r.buf.bytes(16)
392 case formDwarfBlock:
393 r.buf.bytes(int(r.buf.uint()))
394 case formUdata:
395 val = r.buf.uint()
396 }
397
398 switch lf.lnct {
399 case lnctPath:
400 path = str
401 case lnctDirectoryIndex:
402 if val >= uint64(len(r.directories)) {
403 return "", 0, 0, DecodeError{"line", r.buf.off, "directory index out of range"}
404 }
405 dir = r.directories[val]
406 case lnctTimestamp:
407 mtime = val
408 case lnctSize:
409 size = val
410 case lnctMD5:
411
412 }
413 }
414
415 if dir != "" && path != "" {
416 path = pathJoin(dir, path)
417 }
418
419 return path, mtime, size, nil
420 }
421
422
423
424
425 func (r *LineReader) readFileEntry() (bool, error) {
426 name := r.buf.string()
427 if r.buf.err != nil {
428 return false, r.buf.err
429 }
430 if len(name) == 0 {
431 return true, nil
432 }
433 off := r.buf.off
434 dirIndex := int(r.buf.uint())
435 if !pathIsAbs(name) {
436 if dirIndex >= len(r.directories) {
437 return false, DecodeError{"line", off, "directory index too large"}
438 }
439 name = pathJoin(r.directories[dirIndex], name)
440 }
441 mtime := r.buf.uint()
442 length := int(r.buf.uint())
443
444
445
446
447
448
449 if len(r.fileEntries) < cap(r.fileEntries) {
450 fe := r.fileEntries[:len(r.fileEntries)+1]
451 if fe[len(fe)-1] != nil {
452
453 r.fileEntries = fe
454 return false, nil
455 }
456 }
457 r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length})
458 return false, nil
459 }
460
461
462
463 func (r *LineReader) updateFile() {
464 if r.fileIndex < len(r.fileEntries) {
465 r.state.File = r.fileEntries[r.fileIndex]
466 } else {
467 r.state.File = nil
468 }
469 }
470
471
472
473
474
475
476
477 func (r *LineReader) Next(entry *LineEntry) error {
478 if r.buf.err != nil {
479 return r.buf.err
480 }
481
482
483
484 for {
485 if len(r.buf.data) == 0 {
486 return io.EOF
487 }
488 emit := r.step(entry)
489 if r.buf.err != nil {
490 return r.buf.err
491 }
492 if emit {
493 return nil
494 }
495 }
496 }
497
498
499
500 var knownOpcodeLengths = map[int]int{
501 lnsCopy: 0,
502 lnsAdvancePC: 1,
503 lnsAdvanceLine: 1,
504 lnsSetFile: 1,
505 lnsNegateStmt: 0,
506 lnsSetBasicBlock: 0,
507 lnsConstAddPC: 0,
508 lnsSetPrologueEnd: 0,
509 lnsSetEpilogueBegin: 0,
510 lnsSetISA: 1,
511
512
513
514 }
515
516
517
518
519 func (r *LineReader) step(entry *LineEntry) bool {
520 opcode := int(r.buf.uint8())
521
522 if opcode >= r.opcodeBase {
523
524 adjustedOpcode := opcode - r.opcodeBase
525 r.advancePC(adjustedOpcode / r.lineRange)
526 lineDelta := r.lineBase + adjustedOpcode%r.lineRange
527 r.state.Line += lineDelta
528 goto emit
529 }
530
531 switch opcode {
532 case 0:
533
534 length := Offset(r.buf.uint())
535 startOff := r.buf.off
536 opcode := r.buf.uint8()
537
538 switch opcode {
539 case lneEndSequence:
540 r.state.EndSequence = true
541 *entry = r.state
542 r.resetState()
543
544 case lneSetAddress:
545 switch r.addrsize {
546 case 1:
547 r.state.Address = uint64(r.buf.uint8())
548 case 2:
549 r.state.Address = uint64(r.buf.uint16())
550 case 4:
551 r.state.Address = uint64(r.buf.uint32())
552 case 8:
553 r.state.Address = r.buf.uint64()
554 default:
555 r.buf.error("unknown address size")
556 }
557
558 case lneDefineFile:
559 if done, err := r.readFileEntry(); err != nil {
560 r.buf.err = err
561 return false
562 } else if done {
563 r.buf.err = DecodeError{"line", startOff, "malformed DW_LNE_define_file operation"}
564 return false
565 }
566 r.updateFile()
567
568 case lneSetDiscriminator:
569
570 r.state.Discriminator = int(r.buf.uint())
571 }
572
573 r.buf.skip(int(startOff + length - r.buf.off))
574
575 if opcode == lneEndSequence {
576 return true
577 }
578
579
580 case lnsCopy:
581 goto emit
582
583 case lnsAdvancePC:
584 r.advancePC(int(r.buf.uint()))
585
586 case lnsAdvanceLine:
587 r.state.Line += int(r.buf.int())
588
589 case lnsSetFile:
590 r.fileIndex = int(r.buf.uint())
591 r.updateFile()
592
593 case lnsSetColumn:
594 r.state.Column = int(r.buf.uint())
595
596 case lnsNegateStmt:
597 r.state.IsStmt = !r.state.IsStmt
598
599 case lnsSetBasicBlock:
600 r.state.BasicBlock = true
601
602 case lnsConstAddPC:
603 r.advancePC((255 - r.opcodeBase) / r.lineRange)
604
605 case lnsFixedAdvancePC:
606 r.state.Address += uint64(r.buf.uint16())
607
608
609 case lnsSetPrologueEnd:
610 r.state.PrologueEnd = true
611
612 case lnsSetEpilogueBegin:
613 r.state.EpilogueBegin = true
614
615 case lnsSetISA:
616 r.state.ISA = int(r.buf.uint())
617
618 default:
619
620
621 for i := 0; i < r.opcodeLengths[opcode]; i++ {
622 r.buf.uint()
623 }
624 }
625 return false
626
627 emit:
628 *entry = r.state
629 r.state.BasicBlock = false
630 r.state.PrologueEnd = false
631 r.state.EpilogueBegin = false
632 r.state.Discriminator = 0
633 return true
634 }
635
636
637
638 func (r *LineReader) advancePC(opAdvance int) {
639 opIndex := r.state.OpIndex + opAdvance
640 r.state.Address += uint64(r.minInstructionLength * (opIndex / r.maxOpsPerInstruction))
641 r.state.OpIndex = opIndex % r.maxOpsPerInstruction
642 }
643
644
645 type LineReaderPos struct {
646
647 off Offset
648
649 numFileEntries int
650
651
652 state LineEntry
653 fileIndex int
654 }
655
656
657 func (r *LineReader) Tell() LineReaderPos {
658 return LineReaderPos{r.buf.off, len(r.fileEntries), r.state, r.fileIndex}
659 }
660
661
662
663
664
665 func (r *LineReader) Seek(pos LineReaderPos) {
666 r.buf.off = pos.off
667 r.buf.data = r.section[r.buf.off:r.endOffset]
668 r.fileEntries = r.fileEntries[:pos.numFileEntries]
669 r.state = pos.state
670 r.fileIndex = pos.fileIndex
671 }
672
673
674
675 func (r *LineReader) Reset() {
676
677 r.buf.off = r.programOffset
678 r.buf.data = r.section[r.buf.off:r.endOffset]
679
680
681 r.fileEntries = r.fileEntries[:r.initialFileEntries]
682
683
684 r.resetState()
685 }
686
687
688 func (r *LineReader) resetState() {
689
690
691 r.state = LineEntry{
692 Address: 0,
693 OpIndex: 0,
694 File: nil,
695 Line: 1,
696 Column: 0,
697 IsStmt: r.defaultIsStmt,
698 BasicBlock: false,
699 PrologueEnd: false,
700 EpilogueBegin: false,
701 ISA: 0,
702 Discriminator: 0,
703 }
704 r.fileIndex = 1
705 r.updateFile()
706 }
707
708
709
710
711
712
713
714
715
716
717
718
719
720 func (r *LineReader) Files() []*LineFile {
721 return r.fileEntries
722 }
723
724
725
726 var ErrUnknownPC = errors.New("ErrUnknownPC")
727
728
729
730
731
732
733
734
735
736
737
738
739
740 func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error {
741 if err := r.Next(entry); err != nil {
742 return err
743 }
744 if entry.Address > pc {
745
746 r.Reset()
747 if err := r.Next(entry); err != nil {
748 return err
749 }
750 if entry.Address > pc {
751
752 r.Reset()
753 return ErrUnknownPC
754 }
755 }
756
757
758 for {
759 var next LineEntry
760 pos := r.Tell()
761 if err := r.Next(&next); err != nil {
762 if err == io.EOF {
763 return ErrUnknownPC
764 }
765 return err
766 }
767 if next.Address > pc {
768 if entry.EndSequence {
769
770 return ErrUnknownPC
771 }
772
773
774 r.Seek(pos)
775 return nil
776 }
777 *entry = next
778 }
779 }
780
781
782
783
784
785
786
787 func pathIsAbs(path string) bool {
788 _, path = splitDrive(path)
789 return len(path) > 0 && (path[0] == '/' || path[0] == '\\')
790 }
791
792
793
794 func pathJoin(dirname, filename string) string {
795 if len(dirname) == 0 {
796 return filename
797 }
798
799
800
801 drive, dirname := splitDrive(dirname)
802 if drive == "" {
803
804 return path.Join(dirname, filename)
805 }
806
807 drive2, filename := splitDrive(filename)
808 if drive2 != "" {
809 if !strings.EqualFold(drive, drive2) {
810
811
812 return drive2 + filename
813 }
814
815 }
816 if !(strings.HasSuffix(dirname, "/") || strings.HasSuffix(dirname, `\`)) && dirname != "" {
817 sep := `\`
818 if strings.HasPrefix(dirname, "/") {
819 sep = `/`
820 }
821 dirname += sep
822 }
823 return drive + dirname + filename
824 }
825
826
827
828 func splitDrive(path string) (drive, rest string) {
829 if len(path) >= 2 && path[1] == ':' {
830 if c := path[0]; 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' {
831 return path[:2], path[2:]
832 }
833 }
834 if len(path) > 3 && (path[0] == '\\' || path[0] == '/') && (path[1] == '\\' || path[1] == '/') {
835
836 npath := strings.Replace(path, "/", `\`, -1)
837
838 slash1 := strings.IndexByte(npath[2:], '\\') + 2
839 if slash1 > 2 {
840
841 slash2 := strings.IndexByte(npath[slash1+1:], '\\') + slash1 + 1
842 if slash2 > slash1 {
843 return path[:slash2], path[slash2:]
844 }
845 }
846 }
847 return "", path
848 }
849
View as plain text