1
2
3
4
5
6
7 package obj
8
9 import (
10 "github.com/twitchyliquid64/golang-asm/dwarf"
11 "github.com/twitchyliquid64/golang-asm/objabi"
12 "github.com/twitchyliquid64/golang-asm/src"
13 "fmt"
14 "sort"
15 "sync"
16 )
17
18
19
20 const (
21 LINE_BASE = -4
22 LINE_RANGE = 10
23 PC_RANGE = (255 - OPCODE_BASE) / LINE_RANGE
24 OPCODE_BASE = 11
25 )
26
27
28
29
30
31
32
33
34
35 func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) {
36 dctxt := dwCtxt{ctxt}
37
38
39
40 dctxt.AddUint8(lines, 0)
41 dwarf.Uleb128put(dctxt, lines, 1+int64(ctxt.Arch.PtrSize))
42 dctxt.AddUint8(lines, dwarf.DW_LNE_set_address)
43 dctxt.AddAddress(lines, s, 0)
44
45
46
47 stmt := true
48 line := int64(1)
49 pc := s.Func.Text.Pc
50 var lastpc int64
51 name := ""
52 prologue, wrotePrologue := false, false
53
54 for p := s.Func.Text; p != nil; p = p.Link {
55 prologue = prologue || (p.Pos.Xlogue() == src.PosPrologueEnd)
56
57 if p.Pos.Line() == 0 || (p.Link != nil && p.Link.Pc == p.Pc) {
58 continue
59 }
60 newStmt := p.Pos.IsStmt() != src.PosNotStmt
61 newName, newLine := linkgetlineFromPos(ctxt, p.Pos)
62
63
64 wrote := false
65 if name != newName {
66 newFile := ctxt.PosTable.FileIndex(newName) + 1
67 dctxt.AddUint8(lines, dwarf.DW_LNS_set_file)
68 dwarf.Uleb128put(dctxt, lines, int64(newFile))
69 name = newName
70 wrote = true
71 }
72 if prologue && !wrotePrologue {
73 dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_set_prologue_end))
74 wrotePrologue = true
75 wrote = true
76 }
77 if stmt != newStmt {
78 dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_negate_stmt))
79 stmt = newStmt
80 wrote = true
81 }
82
83 if line != int64(newLine) || wrote {
84 pcdelta := p.Pc - pc
85 lastpc = p.Pc
86 putpclcdelta(ctxt, dctxt, lines, uint64(pcdelta), int64(newLine)-line)
87 line, pc = int64(newLine), p.Pc
88 }
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 lastlen := uint64(s.Size - (lastpc - s.Func.Text.Pc))
107 putpclcdelta(ctxt, dctxt, lines, lastlen, 0)
108 dctxt.AddUint8(lines, 0)
109 dwarf.Uleb128put(dctxt, lines, 1)
110 dctxt.AddUint8(lines, dwarf.DW_LNE_end_sequence)
111 }
112
113 func putpclcdelta(linkctxt *Link, dctxt dwCtxt, s *LSym, deltaPC uint64, deltaLC int64) {
114
115
116 var opcode int64
117 if deltaLC < LINE_BASE {
118 if deltaPC >= PC_RANGE {
119 opcode = OPCODE_BASE + (LINE_RANGE * PC_RANGE)
120 } else {
121 opcode = OPCODE_BASE + (LINE_RANGE * int64(deltaPC))
122 }
123 } else if deltaLC < LINE_BASE+LINE_RANGE {
124 if deltaPC >= PC_RANGE {
125 opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * PC_RANGE)
126 if opcode > 255 {
127 opcode -= LINE_RANGE
128 }
129 } else {
130 opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * int64(deltaPC))
131 }
132 } else {
133 if deltaPC <= PC_RANGE {
134 opcode = OPCODE_BASE + (LINE_RANGE - 1) + (LINE_RANGE * int64(deltaPC))
135 if opcode > 255 {
136 opcode = 255
137 }
138 } else {
139
140
141
142
143
144
145
146
147
148
149 switch deltaPC - PC_RANGE {
150
151
152
153
154
155
156
157
158 case PC_RANGE, (1 << 7) - 1, (1 << 16) - 1, (1 << 21) - 1, (1 << 28) - 1,
159 (1 << 35) - 1, (1 << 42) - 1, (1 << 49) - 1, (1 << 56) - 1, (1 << 63) - 1:
160 opcode = 255
161 default:
162 opcode = OPCODE_BASE + LINE_RANGE*PC_RANGE - 1
163 }
164 }
165 }
166 if opcode < OPCODE_BASE || opcode > 255 {
167 panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
168 }
169
170
171 deltaPC -= uint64((opcode - OPCODE_BASE) / LINE_RANGE)
172 deltaLC -= (opcode-OPCODE_BASE)%LINE_RANGE + LINE_BASE
173
174
175 if deltaPC != 0 {
176 if deltaPC <= PC_RANGE {
177
178
179 opcode -= LINE_RANGE * int64(PC_RANGE-deltaPC)
180 if opcode < OPCODE_BASE {
181 panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
182 }
183 dctxt.AddUint8(s, dwarf.DW_LNS_const_add_pc)
184 } else if (1<<14) <= deltaPC && deltaPC < (1<<16) {
185 dctxt.AddUint8(s, dwarf.DW_LNS_fixed_advance_pc)
186 dctxt.AddUint16(s, uint16(deltaPC))
187 } else {
188 dctxt.AddUint8(s, dwarf.DW_LNS_advance_pc)
189 dwarf.Uleb128put(dctxt, s, int64(deltaPC))
190 }
191 }
192
193
194 if deltaLC != 0 {
195 dctxt.AddUint8(s, dwarf.DW_LNS_advance_line)
196 dwarf.Sleb128put(dctxt, s, deltaLC)
197 }
198
199
200 dctxt.AddUint8(s, uint8(opcode))
201 }
202
203
204 type dwCtxt struct{ *Link }
205
206 func (c dwCtxt) PtrSize() int {
207 return c.Arch.PtrSize
208 }
209 func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) {
210 ls := s.(*LSym)
211 ls.WriteInt(c.Link, ls.Size, size, i)
212 }
213 func (c dwCtxt) AddUint16(s dwarf.Sym, i uint16) {
214 c.AddInt(s, 2, int64(i))
215 }
216 func (c dwCtxt) AddUint8(s dwarf.Sym, i uint8) {
217 b := []byte{byte(i)}
218 c.AddBytes(s, b)
219 }
220 func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) {
221 ls := s.(*LSym)
222 ls.WriteBytes(c.Link, ls.Size, b)
223 }
224 func (c dwCtxt) AddString(s dwarf.Sym, v string) {
225 ls := s.(*LSym)
226 ls.WriteString(c.Link, ls.Size, len(v), v)
227 ls.WriteInt(c.Link, ls.Size, 1, 0)
228 }
229 func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
230 ls := s.(*LSym)
231 size := c.PtrSize()
232 if data != nil {
233 rsym := data.(*LSym)
234 ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
235 } else {
236 ls.WriteInt(c.Link, ls.Size, size, value)
237 }
238 }
239 func (c dwCtxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
240 ls := s.(*LSym)
241 rsym := data.(*LSym)
242 ls.WriteCURelativeAddr(c.Link, ls.Size, rsym, value)
243 }
244 func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
245 panic("should be used only in the linker")
246 }
247 func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
248 size := 4
249 if isDwarf64(c.Link) {
250 size = 8
251 }
252
253 ls := s.(*LSym)
254 rsym := t.(*LSym)
255 ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
256 r := &ls.R[len(ls.R)-1]
257 r.Type = objabi.R_DWARFSECREF
258 }
259
260 func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) {
261 ls := s.(*LSym)
262 rsym := f.(*LSym)
263 fidx := c.Link.PosTable.FileIndex(rsym.Name)
264
265
266
267 ls.WriteInt(c.Link, ls.Size, 4, int64(fidx+1))
268 }
269
270 func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 {
271 ls := s.(*LSym)
272 return ls.Size
273 }
274
275
276
277
278
279
280 func (c dwCtxt) RecordDclReference(from dwarf.Sym, to dwarf.Sym, dclIdx int, inlIndex int) {
281 ls := from.(*LSym)
282 tls := to.(*LSym)
283 ridx := len(ls.R) - 1
284 c.Link.DwFixups.ReferenceChildDIE(ls, ridx, tls, dclIdx, inlIndex)
285 }
286
287 func (c dwCtxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
288 ls := s.(*LSym)
289 c.Link.DwFixups.RegisterChildDIEOffsets(ls, vars, offsets)
290 }
291
292 func (c dwCtxt) Logf(format string, args ...interface{}) {
293 c.Link.Logf(format, args...)
294 }
295
296 func isDwarf64(ctxt *Link) bool {
297 return ctxt.Headtype == objabi.Haix
298 }
299
300 func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, dwarfAbsFnSym, dwarfDebugLines *LSym) {
301 if s.Type != objabi.STEXT {
302 ctxt.Diag("dwarfSym of non-TEXT %v", s)
303 }
304 if s.Func.dwarfInfoSym == nil {
305 s.Func.dwarfInfoSym = &LSym{
306 Type: objabi.SDWARFFCN,
307 }
308 if ctxt.Flag_locationlists {
309 s.Func.dwarfLocSym = &LSym{
310 Type: objabi.SDWARFLOC,
311 }
312 }
313 s.Func.dwarfRangesSym = &LSym{
314 Type: objabi.SDWARFRANGE,
315 }
316 s.Func.dwarfDebugLinesSym = &LSym{
317 Type: objabi.SDWARFLINES,
318 }
319 if s.WasInlined() {
320 s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s)
321 }
322 }
323 return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym
324 }
325
326 func (s *LSym) Length(dwarfContext interface{}) int64 {
327 return s.Size
328 }
329
330
331
332
333 func (ctxt *Link) fileSymbol(fn *LSym) *LSym {
334 p := fn.Func.Text
335 if p != nil {
336 f, _ := linkgetlineFromPos(ctxt, p.Pos)
337 fsym := ctxt.Lookup(f)
338 return fsym
339 }
340 return nil
341 }
342
343
344
345
346 func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string) {
347 info, loc, ranges, absfunc, lines := ctxt.dwarfSym(s)
348 if info.Size != 0 {
349 ctxt.Diag("makeFuncDebugEntry double process %v", s)
350 }
351 var scopes []dwarf.Scope
352 var inlcalls dwarf.InlCalls
353 if ctxt.DebugInfo != nil {
354 scopes, inlcalls = ctxt.DebugInfo(s, info, curfn)
355 }
356 var err error
357 dwctxt := dwCtxt{ctxt}
358 filesym := ctxt.fileSymbol(s)
359 fnstate := &dwarf.FnState{
360 Name: s.Name,
361 Importpath: myimportpath,
362 Info: info,
363 Filesym: filesym,
364 Loc: loc,
365 Ranges: ranges,
366 Absfn: absfunc,
367 StartPC: s,
368 Size: s.Size,
369 External: !s.Static(),
370 Scopes: scopes,
371 InlCalls: inlcalls,
372 UseBASEntries: ctxt.UseBASEntries,
373 }
374 if absfunc != nil {
375 err = dwarf.PutAbstractFunc(dwctxt, fnstate)
376 if err != nil {
377 ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
378 }
379 err = dwarf.PutConcreteFunc(dwctxt, fnstate)
380 } else {
381 err = dwarf.PutDefaultFunc(dwctxt, fnstate)
382 }
383 if err != nil {
384 ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
385 }
386
387 ctxt.generateDebugLinesSymbol(s, lines)
388 }
389
390
391
392 func (ctxt *Link) DwarfIntConst(myimportpath, name, typename string, val int64) {
393 if myimportpath == "" {
394 return
395 }
396 s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) {
397 s.Type = objabi.SDWARFCONST
398 ctxt.Data = append(ctxt.Data, s)
399 })
400 dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val)
401 }
402
403 func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath string) {
404 absfn := ctxt.DwFixups.AbsFuncDwarfSym(s)
405 if absfn.Size != 0 {
406 ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s)
407 }
408 if s.Func == nil {
409 s.Func = new(FuncInfo)
410 }
411 scopes, _ := ctxt.DebugInfo(s, absfn, curfn)
412 dwctxt := dwCtxt{ctxt}
413 filesym := ctxt.fileSymbol(s)
414 fnstate := dwarf.FnState{
415 Name: s.Name,
416 Importpath: myimportpath,
417 Info: absfn,
418 Filesym: filesym,
419 Absfn: absfn,
420 External: !s.Static(),
421 Scopes: scopes,
422 UseBASEntries: ctxt.UseBASEntries,
423 }
424 if err := dwarf.PutAbstractFunc(dwctxt, &fnstate); err != nil {
425 ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
426 }
427 }
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463 type DwarfFixupTable struct {
464 ctxt *Link
465 mu sync.Mutex
466 symtab map[*LSym]int
467 svec []symFixups
468 precursor map[*LSym]fnState
469 }
470
471 type symFixups struct {
472 fixups []relFixup
473 doffsets []declOffset
474 inlIndex int32
475 defseen bool
476 }
477
478 type declOffset struct {
479
480 dclIdx int32
481
482 offset int32
483 }
484
485 type relFixup struct {
486 refsym *LSym
487 relidx int32
488 dclidx int32
489 }
490
491 type fnState struct {
492
493 precursor interface{}
494
495 absfn *LSym
496 }
497
498 func NewDwarfFixupTable(ctxt *Link) *DwarfFixupTable {
499 return &DwarfFixupTable{
500 ctxt: ctxt,
501 symtab: make(map[*LSym]int),
502 precursor: make(map[*LSym]fnState),
503 }
504 }
505
506 func (ft *DwarfFixupTable) GetPrecursorFunc(s *LSym) interface{} {
507 if fnstate, found := ft.precursor[s]; found {
508 return fnstate.precursor
509 }
510 return nil
511 }
512
513 func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) {
514 if _, found := ft.precursor[s]; found {
515 ft.ctxt.Diag("internal error: DwarfFixupTable.SetPrecursorFunc double call on %v", s)
516 }
517
518
519
520
521 absfn := ft.ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name+dwarf.AbstractFuncSuffix)
522 absfn.Set(AttrDuplicateOK, true)
523 absfn.Type = objabi.SDWARFABSFCN
524 ft.ctxt.Data = append(ft.ctxt.Data, absfn)
525
526
527
528
529
530 if s.Func != nil && s.Func.dwarfAbsFnSym == nil {
531 s.Func.dwarfAbsFnSym = absfn
532 }
533
534 ft.precursor[s] = fnState{precursor: fn, absfn: absfn}
535 }
536
537
538
539 func (ft *DwarfFixupTable) ReferenceChildDIE(s *LSym, ridx int, tgt *LSym, dclidx int, inlIndex int) {
540
541 ft.mu.Lock()
542 defer ft.mu.Unlock()
543
544
545 idx, found := ft.symtab[tgt]
546 if !found {
547 ft.svec = append(ft.svec, symFixups{inlIndex: int32(inlIndex)})
548 idx = len(ft.svec) - 1
549 ft.symtab[tgt] = idx
550 }
551
552
553
554 sf := &ft.svec[idx]
555 if len(sf.doffsets) > 0 {
556 found := false
557 for _, do := range sf.doffsets {
558 if do.dclIdx == int32(dclidx) {
559 off := do.offset
560 s.R[ridx].Add += int64(off)
561 found = true
562 break
563 }
564 }
565 if !found {
566 ft.ctxt.Diag("internal error: DwarfFixupTable.ReferenceChildDIE unable to locate child DIE offset for dclIdx=%d src=%v tgt=%v", dclidx, s, tgt)
567 }
568 } else {
569 sf.fixups = append(sf.fixups, relFixup{s, int32(ridx), int32(dclidx)})
570 }
571 }
572
573
574
575
576
577
578 func (ft *DwarfFixupTable) RegisterChildDIEOffsets(s *LSym, vars []*dwarf.Var, coffsets []int32) {
579
580 if len(vars) != len(coffsets) {
581 ft.ctxt.Diag("internal error: RegisterChildDIEOffsets vars/offsets length mismatch")
582 return
583 }
584
585
586 doffsets := make([]declOffset, len(coffsets))
587 for i := range coffsets {
588 doffsets[i].dclIdx = vars[i].ChildIndex
589 doffsets[i].offset = coffsets[i]
590 }
591
592 ft.mu.Lock()
593 defer ft.mu.Unlock()
594
595
596 idx, found := ft.symtab[s]
597 if !found {
598 sf := symFixups{inlIndex: -1, defseen: true, doffsets: doffsets}
599 ft.svec = append(ft.svec, sf)
600 ft.symtab[s] = len(ft.svec) - 1
601 } else {
602 sf := &ft.svec[idx]
603 sf.doffsets = doffsets
604 sf.defseen = true
605 }
606 }
607
608 func (ft *DwarfFixupTable) processFixups(slot int, s *LSym) {
609 sf := &ft.svec[slot]
610 for _, f := range sf.fixups {
611 dfound := false
612 for _, doffset := range sf.doffsets {
613 if doffset.dclIdx == f.dclidx {
614 f.refsym.R[f.relidx].Add += int64(doffset.offset)
615 dfound = true
616 break
617 }
618 }
619 if !dfound {
620 ft.ctxt.Diag("internal error: DwarfFixupTable has orphaned fixup on %v targeting %v relidx=%d dclidx=%d", f.refsym, s, f.relidx, f.dclidx)
621 }
622 }
623 }
624
625
626
627 func (ft *DwarfFixupTable) AbsFuncDwarfSym(fnsym *LSym) *LSym {
628
629 ft.mu.Lock()
630 defer ft.mu.Unlock()
631
632 if fnstate, found := ft.precursor[fnsym]; found {
633 return fnstate.absfn
634 }
635 ft.ctxt.Diag("internal error: AbsFuncDwarfSym requested for %v, not seen during inlining", fnsym)
636 return nil
637 }
638
639
640
641
642
643
644
645 func (ft *DwarfFixupTable) Finalize(myimportpath string, trace bool) {
646 if trace {
647 ft.ctxt.Logf("DwarfFixupTable.Finalize invoked for %s\n", myimportpath)
648 }
649
650
651
652 fns := make([]*LSym, len(ft.precursor))
653 idx := 0
654 for fn := range ft.precursor {
655 fns[idx] = fn
656 idx++
657 }
658 sort.Sort(BySymName(fns))
659
660
661 if ft.ctxt.InParallel {
662 ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize call during parallel backend")
663 }
664
665
666 for _, s := range fns {
667 absfn := ft.AbsFuncDwarfSym(s)
668 slot, found := ft.symtab[absfn]
669 if !found || !ft.svec[slot].defseen {
670 ft.ctxt.GenAbstractFunc(s)
671 }
672 }
673
674
675 for _, s := range fns {
676 absfn := ft.AbsFuncDwarfSym(s)
677 slot, found := ft.symtab[absfn]
678 if !found {
679 ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize orphan abstract function for %v", s)
680 } else {
681 ft.processFixups(slot, s)
682 }
683 }
684 }
685
686 type BySymName []*LSym
687
688 func (s BySymName) Len() int { return len(s) }
689 func (s BySymName) Less(i, j int) bool { return s[i].Name < s[j].Name }
690 func (s BySymName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
691
View as plain text