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