1// Copyright 2013 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// Writing of Go object files.
6
7package obj
8
9import (
10	"bufio"
11	"cmd/internal/bio"
12	"cmd/internal/dwarf"
13	"cmd/internal/objabi"
14	"cmd/internal/sys"
15	"fmt"
16	"log"
17	"path/filepath"
18	"sort"
19	"strings"
20	"sync"
21)
22
23// objWriter writes Go object files.
24type objWriter struct {
25	wr   *bufio.Writer
26	ctxt *Link
27	// Temporary buffer for zigzag int writing.
28	varintbuf [10]uint8
29
30	// Number of objects written of each type.
31	nRefs     int
32	nData     int
33	nReloc    int
34	nPcdata   int
35	nFuncdata int
36	nFile     int
37
38	pkgpath string // the package import path (escaped), "" if unknown
39}
40
41func (w *objWriter) addLengths(s *LSym) {
42	w.nData += len(s.P)
43	w.nReloc += len(s.R)
44
45	if s.Type != objabi.STEXT {
46		return
47	}
48
49	pc := &s.Func.Pcln
50
51	data := 0
52	data += len(pc.Pcsp.P)
53	data += len(pc.Pcfile.P)
54	data += len(pc.Pcline.P)
55	data += len(pc.Pcinline.P)
56	for _, pcd := range pc.Pcdata {
57		data += len(pcd.P)
58	}
59
60	w.nData += data
61	w.nPcdata += len(pc.Pcdata)
62
63	w.nFuncdata += len(pc.Funcdataoff)
64	w.nFile += len(pc.File)
65}
66
67func (w *objWriter) writeLengths() {
68	w.writeInt(int64(w.nData))
69	w.writeInt(int64(w.nReloc))
70	w.writeInt(int64(w.nPcdata))
71	w.writeInt(int64(0)) // TODO: remove at next object file rev
72	w.writeInt(int64(w.nFuncdata))
73	w.writeInt(int64(w.nFile))
74}
75
76func newObjWriter(ctxt *Link, b *bufio.Writer, pkgpath string) *objWriter {
77	return &objWriter{
78		ctxt:    ctxt,
79		wr:      b,
80		pkgpath: objabi.PathToPrefix(pkgpath),
81	}
82}
83
84func WriteObjFile(ctxt *Link, bout *bio.Writer, pkgpath string) {
85	if ctxt.Flag_newobj {
86		WriteObjFile2(ctxt, bout, pkgpath)
87		return
88	}
89
90	b := bout.Writer
91	w := newObjWriter(ctxt, b, pkgpath)
92
93	// Magic header
94	w.wr.WriteString("\x00go114ld")
95
96	// Version
97	w.wr.WriteByte(1)
98
99	// Autolib
100	for _, pkg := range ctxt.Imports {
101		w.writeString(pkg)
102	}
103	w.writeString("")
104
105	// DWARF File Table
106	fileTable := ctxt.PosTable.DebugLinesFileTable()
107	w.writeInt(int64(len(fileTable)))
108	for _, str := range fileTable {
109		w.writeString(filepath.ToSlash(str))
110	}
111
112	// Symbol references
113	for _, s := range ctxt.Text {
114		w.writeRefs(s)
115		w.addLengths(s)
116	}
117
118	if ctxt.Headtype == objabi.Haix {
119		// Data must be sorted to keep a constant order in TOC symbols.
120		// As they are created during Progedit, two symbols can be switched between
121		// two different compilations. Therefore, BuildID will be different.
122		// TODO: find a better place and optimize to only sort TOC symbols
123		sort.Slice(ctxt.Data, func(i, j int) bool {
124			return ctxt.Data[i].Name < ctxt.Data[j].Name
125		})
126	}
127
128	for _, s := range ctxt.Data {
129		w.writeRefs(s)
130		w.addLengths(s)
131	}
132	for _, s := range ctxt.ABIAliases {
133		w.writeRefs(s)
134		w.addLengths(s)
135	}
136	// End symbol references
137	w.wr.WriteByte(0xff)
138
139	// Lengths
140	w.writeLengths()
141
142	// Data block
143	for _, s := range ctxt.Text {
144		w.wr.Write(s.P)
145		pc := &s.Func.Pcln
146		w.wr.Write(pc.Pcsp.P)
147		w.wr.Write(pc.Pcfile.P)
148		w.wr.Write(pc.Pcline.P)
149		w.wr.Write(pc.Pcinline.P)
150		for _, pcd := range pc.Pcdata {
151			w.wr.Write(pcd.P)
152		}
153	}
154	for _, s := range ctxt.Data {
155		if len(s.P) > 0 {
156			switch s.Type {
157			case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
158				ctxt.Diag("cannot provide data for %v sym %v", s.Type, s.Name)
159			}
160		}
161		w.wr.Write(s.P)
162	}
163
164	// Symbols
165	for _, s := range ctxt.Text {
166		w.writeSym(s)
167	}
168	for _, s := range ctxt.Data {
169		w.writeSym(s)
170	}
171	for _, s := range ctxt.ABIAliases {
172		w.writeSym(s)
173	}
174
175	// Magic footer
176	w.wr.WriteString("\xffgo114ld")
177}
178
179// Symbols are prefixed so their content doesn't get confused with the magic footer.
180const symPrefix = 0xfe
181
182func (w *objWriter) writeRef(s *LSym, isPath bool) {
183	if s == nil || s.RefIdx != 0 {
184		return
185	}
186	w.wr.WriteByte(symPrefix)
187	if isPath {
188		w.writeString(filepath.ToSlash(s.Name))
189	} else if w.pkgpath != "" {
190		// w.pkgpath is already escaped.
191		n := strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
192		w.writeString(n)
193	} else {
194		w.writeString(s.Name)
195	}
196	// Write ABI/static information.
197	abi := int64(s.ABI())
198	if s.Static() {
199		abi = -1
200	}
201	w.writeInt(abi)
202	w.nRefs++
203	s.RefIdx = w.nRefs
204}
205
206func (w *objWriter) writeRefs(s *LSym) {
207	w.writeRef(s, false)
208	w.writeRef(s.Gotype, false)
209	for _, r := range s.R {
210		w.writeRef(r.Sym, false)
211	}
212
213	if s.Type == objabi.STEXT {
214		pc := &s.Func.Pcln
215		for _, d := range pc.Funcdata {
216			w.writeRef(d, false)
217		}
218		for _, f := range pc.File {
219			fsym := w.ctxt.Lookup(f)
220			w.writeRef(fsym, true)
221		}
222		for _, call := range pc.InlTree.nodes {
223			w.writeRef(call.Func, false)
224			f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
225			fsym := w.ctxt.Lookup(f)
226			w.writeRef(fsym, true)
227		}
228	}
229}
230
231func (ctxt *Link) writeSymDebug(s *LSym) {
232	fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
233	if s.Type != 0 {
234		fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
235	}
236	if s.Static() {
237		fmt.Fprint(ctxt.Bso, "static ")
238	}
239	if s.DuplicateOK() {
240		fmt.Fprintf(ctxt.Bso, "dupok ")
241	}
242	if s.CFunc() {
243		fmt.Fprintf(ctxt.Bso, "cfunc ")
244	}
245	if s.NoSplit() {
246		fmt.Fprintf(ctxt.Bso, "nosplit ")
247	}
248	if s.TopFrame() {
249		fmt.Fprintf(ctxt.Bso, "topframe ")
250	}
251	fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
252	if s.Type == objabi.STEXT {
253		fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Func.Args), uint64(s.Func.Locals))
254		if s.Leaf() {
255			fmt.Fprintf(ctxt.Bso, " leaf")
256		}
257	}
258	fmt.Fprintf(ctxt.Bso, "\n")
259	if s.Type == objabi.STEXT {
260		for p := s.Func.Text; p != nil; p = p.Link {
261			var s string
262			if ctxt.Debugasm > 1 {
263				s = p.String()
264			} else {
265				s = p.InnermostString()
266			}
267			fmt.Fprintf(ctxt.Bso, "\t%#04x %s\n", uint(int(p.Pc)), s)
268		}
269	}
270	for i := 0; i < len(s.P); i += 16 {
271		fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
272		j := i
273		for ; j < i+16 && j < len(s.P); j++ {
274			fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
275		}
276		for ; j < i+16; j++ {
277			fmt.Fprintf(ctxt.Bso, "   ")
278		}
279		fmt.Fprintf(ctxt.Bso, "  ")
280		for j = i; j < i+16 && j < len(s.P); j++ {
281			c := int(s.P[j])
282			if ' ' <= c && c <= 0x7e {
283				fmt.Fprintf(ctxt.Bso, "%c", c)
284			} else {
285				fmt.Fprintf(ctxt.Bso, ".")
286			}
287		}
288
289		fmt.Fprintf(ctxt.Bso, "\n")
290	}
291
292	sort.Sort(relocByOff(s.R)) // generate stable output
293	for _, r := range s.R {
294		name := ""
295		if r.Sym != nil {
296			name = r.Sym.Name
297		} else if r.Type == objabi.R_TLS_LE {
298			name = "TLS"
299		}
300		if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
301			fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add))
302		} else {
303			fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add)
304		}
305	}
306}
307
308func (w *objWriter) writeSym(s *LSym) {
309	ctxt := w.ctxt
310	if ctxt.Debugasm > 0 {
311		w.ctxt.writeSymDebug(s)
312	}
313
314	w.wr.WriteByte(symPrefix)
315	w.wr.WriteByte(byte(s.Type))
316	w.writeRefIndex(s)
317	flags := int64(0)
318	if s.DuplicateOK() {
319		flags |= 1
320	}
321	if s.Local() {
322		flags |= 1 << 1
323	}
324	if s.MakeTypelink() {
325		flags |= 1 << 2
326	}
327	w.writeInt(flags)
328	w.writeInt(s.Size)
329	w.writeRefIndex(s.Gotype)
330	w.writeInt(int64(len(s.P)))
331
332	w.writeInt(int64(len(s.R)))
333	var r *Reloc
334	for i := range s.R {
335		r = &s.R[i]
336		w.writeInt(int64(r.Off))
337		w.writeInt(int64(r.Siz))
338		w.writeInt(int64(r.Type))
339		w.writeInt(r.Add)
340		w.writeRefIndex(r.Sym)
341	}
342
343	if s.Type != objabi.STEXT {
344		return
345	}
346
347	w.writeInt(int64(s.Func.Args))
348	w.writeInt(int64(s.Func.Locals))
349	w.writeBool(s.NoSplit())
350	flags = int64(0)
351	if s.Leaf() {
352		flags |= 1
353	}
354	if s.CFunc() {
355		flags |= 1 << 1
356	}
357	if s.ReflectMethod() {
358		flags |= 1 << 2
359	}
360	if ctxt.Flag_shared {
361		flags |= 1 << 3
362	}
363	if s.TopFrame() {
364		flags |= 1 << 4
365	}
366	w.writeInt(flags)
367	w.writeInt(int64(0)) // TODO: remove at next object file rev
368
369	pc := &s.Func.Pcln
370	w.writeInt(int64(len(pc.Pcsp.P)))
371	w.writeInt(int64(len(pc.Pcfile.P)))
372	w.writeInt(int64(len(pc.Pcline.P)))
373	w.writeInt(int64(len(pc.Pcinline.P)))
374	w.writeInt(int64(len(pc.Pcdata)))
375	for _, pcd := range pc.Pcdata {
376		w.writeInt(int64(len(pcd.P)))
377	}
378	w.writeInt(int64(len(pc.Funcdataoff)))
379	for i := range pc.Funcdataoff {
380		w.writeRefIndex(pc.Funcdata[i])
381	}
382	for i := range pc.Funcdataoff {
383		w.writeInt(pc.Funcdataoff[i])
384	}
385	w.writeInt(int64(len(pc.File)))
386	for _, f := range pc.File {
387		fsym := ctxt.Lookup(f)
388		w.writeRefIndex(fsym)
389	}
390	w.writeInt(int64(len(pc.InlTree.nodes)))
391	for _, call := range pc.InlTree.nodes {
392		w.writeInt(int64(call.Parent))
393		f, l := linkgetlineFromPos(w.ctxt, call.Pos)
394		fsym := ctxt.Lookup(f)
395		w.writeRefIndex(fsym)
396		w.writeInt(int64(l))
397		w.writeRefIndex(call.Func)
398		w.writeInt(int64(call.ParentPC))
399	}
400}
401
402func (w *objWriter) writeBool(b bool) {
403	if b {
404		w.writeInt(1)
405	} else {
406		w.writeInt(0)
407	}
408}
409
410func (w *objWriter) writeInt(sval int64) {
411	var v uint64
412	uv := (uint64(sval) << 1) ^ uint64(sval>>63)
413	p := w.varintbuf[:]
414	for v = uv; v >= 0x80; v >>= 7 {
415		p[0] = uint8(v | 0x80)
416		p = p[1:]
417	}
418	p[0] = uint8(v)
419	p = p[1:]
420	w.wr.Write(w.varintbuf[:len(w.varintbuf)-len(p)])
421}
422
423func (w *objWriter) writeString(s string) {
424	w.writeInt(int64(len(s)))
425	w.wr.WriteString(s)
426}
427
428func (w *objWriter) writeRefIndex(s *LSym) {
429	if s == nil {
430		w.writeInt(0)
431		return
432	}
433	if s.RefIdx == 0 {
434		log.Fatalln("writing an unreferenced symbol", s.Name)
435	}
436	w.writeInt(int64(s.RefIdx))
437}
438
439// relocByOff sorts relocations by their offsets.
440type relocByOff []Reloc
441
442func (x relocByOff) Len() int           { return len(x) }
443func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off }
444func (x relocByOff) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
445
446// implement dwarf.Context
447type dwCtxt struct{ *Link }
448
449func (c dwCtxt) PtrSize() int {
450	return c.Arch.PtrSize
451}
452func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) {
453	ls := s.(*LSym)
454	ls.WriteInt(c.Link, ls.Size, size, i)
455}
456func (c dwCtxt) AddUint16(s dwarf.Sym, i uint16) {
457	c.AddInt(s, 2, int64(i))
458}
459func (c dwCtxt) AddUint8(s dwarf.Sym, i uint8) {
460	b := []byte{byte(i)}
461	c.AddBytes(s, b)
462}
463func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) {
464	ls := s.(*LSym)
465	ls.WriteBytes(c.Link, ls.Size, b)
466}
467func (c dwCtxt) AddString(s dwarf.Sym, v string) {
468	ls := s.(*LSym)
469	ls.WriteString(c.Link, ls.Size, len(v), v)
470	ls.WriteInt(c.Link, ls.Size, 1, 0)
471}
472func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
473	ls := s.(*LSym)
474	size := c.PtrSize()
475	if data != nil {
476		rsym := data.(*LSym)
477		ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
478	} else {
479		ls.WriteInt(c.Link, ls.Size, size, value)
480	}
481}
482func (c dwCtxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
483	ls := s.(*LSym)
484	rsym := data.(*LSym)
485	ls.WriteCURelativeAddr(c.Link, ls.Size, rsym, value)
486}
487func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
488	panic("should be used only in the linker")
489}
490func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
491	size := 4
492	if isDwarf64(c.Link) {
493		size = 8
494	}
495
496	ls := s.(*LSym)
497	rsym := t.(*LSym)
498	ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
499	r := &ls.R[len(ls.R)-1]
500	r.Type = objabi.R_DWARFSECREF
501}
502func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) {
503	ls := s.(*LSym)
504	rsym := f.(*LSym)
505	ls.WriteAddr(c.Link, ls.Size, 4, rsym, 0)
506	r := &ls.R[len(ls.R)-1]
507	r.Type = objabi.R_DWARFFILEREF
508}
509
510func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 {
511	ls := s.(*LSym)
512	return ls.Size
513}
514
515// Here "from" is a symbol corresponding to an inlined or concrete
516// function, "to" is the symbol for the corresponding abstract
517// function, and "dclIdx" is the index of the symbol of interest with
518// respect to the Dcl slice of the original pre-optimization version
519// of the inlined function.
520func (c dwCtxt) RecordDclReference(from dwarf.Sym, to dwarf.Sym, dclIdx int, inlIndex int) {
521	ls := from.(*LSym)
522	tls := to.(*LSym)
523	ridx := len(ls.R) - 1
524	c.Link.DwFixups.ReferenceChildDIE(ls, ridx, tls, dclIdx, inlIndex)
525}
526
527func (c dwCtxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
528	ls := s.(*LSym)
529	c.Link.DwFixups.RegisterChildDIEOffsets(ls, vars, offsets)
530}
531
532func (c dwCtxt) Logf(format string, args ...interface{}) {
533	c.Link.Logf(format, args...)
534}
535
536func isDwarf64(ctxt *Link) bool {
537	return ctxt.Headtype == objabi.Haix
538}
539
540func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, dwarfAbsFnSym, dwarfDebugLines *LSym) {
541	if s.Type != objabi.STEXT {
542		ctxt.Diag("dwarfSym of non-TEXT %v", s)
543	}
544	if s.Func.dwarfInfoSym == nil {
545		s.Func.dwarfInfoSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name)
546		if ctxt.Flag_locationlists {
547			s.Func.dwarfLocSym = ctxt.LookupDerived(s, dwarf.LocPrefix+s.Name)
548		}
549		s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name)
550		if s.WasInlined() {
551			s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s)
552		}
553		s.Func.dwarfDebugLinesSym = ctxt.LookupDerived(s, dwarf.DebugLinesPrefix+s.Name)
554	}
555	return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym
556}
557
558func (s *LSym) Len() int64 {
559	return s.Size
560}
561
562// fileSymbol returns a symbol corresponding to the source file of the
563// first instruction (prog) of the specified function. This will
564// presumably be the file in which the function is defined.
565func (ctxt *Link) fileSymbol(fn *LSym) *LSym {
566	p := fn.Func.Text
567	if p != nil {
568		f, _ := linkgetlineFromPos(ctxt, p.Pos)
569		fsym := ctxt.Lookup(f)
570		return fsym
571	}
572	return nil
573}
574
575// populateDWARF fills in the DWARF Debugging Information Entries for
576// TEXT symbol 's'. The various DWARF symbols must already have been
577// initialized in InitTextSym.
578func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string) {
579	info, loc, ranges, absfunc, lines := ctxt.dwarfSym(s)
580	if info.Size != 0 {
581		ctxt.Diag("makeFuncDebugEntry double process %v", s)
582	}
583	var scopes []dwarf.Scope
584	var inlcalls dwarf.InlCalls
585	if ctxt.DebugInfo != nil {
586		scopes, inlcalls = ctxt.DebugInfo(s, info, curfn)
587	}
588	var err error
589	dwctxt := dwCtxt{ctxt}
590	filesym := ctxt.fileSymbol(s)
591	fnstate := &dwarf.FnState{
592		Name:          s.Name,
593		Importpath:    myimportpath,
594		Info:          info,
595		Filesym:       filesym,
596		Loc:           loc,
597		Ranges:        ranges,
598		Absfn:         absfunc,
599		StartPC:       s,
600		Size:          s.Size,
601		External:      !s.Static(),
602		Scopes:        scopes,
603		InlCalls:      inlcalls,
604		UseBASEntries: ctxt.UseBASEntries,
605	}
606	if absfunc != nil {
607		err = dwarf.PutAbstractFunc(dwctxt, fnstate)
608		if err != nil {
609			ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
610		}
611		err = dwarf.PutConcreteFunc(dwctxt, fnstate)
612	} else {
613		err = dwarf.PutDefaultFunc(dwctxt, fnstate)
614	}
615	if err != nil {
616		ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
617	}
618	// Fill in the debug lines symbol.
619	ctxt.generateDebugLinesSymbol(s, lines)
620}
621
622// DwarfIntConst creates a link symbol for an integer constant with the
623// given name, type and value.
624func (ctxt *Link) DwarfIntConst(myimportpath, name, typename string, val int64) {
625	if myimportpath == "" {
626		return
627	}
628	s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) {
629		s.Type = objabi.SDWARFINFO
630		ctxt.Data = append(ctxt.Data, s)
631	})
632	dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val)
633}
634
635func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath string) {
636	absfn := ctxt.DwFixups.AbsFuncDwarfSym(s)
637	if absfn.Size != 0 {
638		ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s)
639	}
640	if s.Func == nil {
641		s.Func = new(FuncInfo)
642	}
643	scopes, _ := ctxt.DebugInfo(s, absfn, curfn)
644	dwctxt := dwCtxt{ctxt}
645	filesym := ctxt.fileSymbol(s)
646	fnstate := dwarf.FnState{
647		Name:          s.Name,
648		Importpath:    myimportpath,
649		Info:          absfn,
650		Filesym:       filesym,
651		Absfn:         absfn,
652		External:      !s.Static(),
653		Scopes:        scopes,
654		UseBASEntries: ctxt.UseBASEntries,
655	}
656	if err := dwarf.PutAbstractFunc(dwctxt, &fnstate); err != nil {
657		ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
658	}
659}
660
661// This table is designed to aid in the creation of references between
662// DWARF subprogram DIEs.
663//
664// In most cases when one DWARF DIE has to refer to another DWARF DIE,
665// the target of the reference has an LSym, which makes it easy to use
666// the existing relocation mechanism. For DWARF inlined routine DIEs,
667// however, the subprogram DIE has to refer to a child
668// parameter/variable DIE of the abstract subprogram. This child DIE
669// doesn't have an LSym, and also of interest is the fact that when
670// DWARF generation is happening for inlined function F within caller
671// G, it's possible that DWARF generation hasn't happened yet for F,
672// so there is no way to know the offset of a child DIE within F's
673// abstract function. Making matters more complex, each inlined
674// instance of F may refer to a subset of the original F's variables
675// (depending on what happens with optimization, some vars may be
676// eliminated).
677//
678// The fixup table below helps overcome this hurdle. At the point
679// where a parameter/variable reference is made (via a call to
680// "ReferenceChildDIE"), a fixup record is generate that records
681// the relocation that is targeting that child variable. At a later
682// point when the abstract function DIE is emitted, there will be
683// a call to "RegisterChildDIEOffsets", at which point the offsets
684// needed to apply fixups are captured. Finally, once the parallel
685// portion of the compilation is done, fixups can actually be applied
686// during the "Finalize" method (this can't be done during the
687// parallel portion of the compile due to the possibility of data
688// races).
689//
690// This table is also used to record the "precursor" function node for
691// each function that is the target of an inline -- child DIE references
692// have to be made with respect to the original pre-optimization
693// version of the function (to allow for the fact that each inlined
694// body may be optimized differently).
695type DwarfFixupTable struct {
696	ctxt      *Link
697	mu        sync.Mutex
698	symtab    map[*LSym]int // maps abstract fn LSYM to index in svec
699	svec      []symFixups
700	precursor map[*LSym]fnState // maps fn Lsym to precursor Node, absfn sym
701}
702
703type symFixups struct {
704	fixups   []relFixup
705	doffsets []declOffset
706	inlIndex int32
707	defseen  bool
708}
709
710type declOffset struct {
711	// Index of variable within DCL list of pre-optimization function
712	dclIdx int32
713	// Offset of var's child DIE with respect to containing subprogram DIE
714	offset int32
715}
716
717type relFixup struct {
718	refsym *LSym
719	relidx int32
720	dclidx int32
721}
722
723type fnState struct {
724	// precursor function (really *gc.Node)
725	precursor interface{}
726	// abstract function symbol
727	absfn *LSym
728}
729
730func NewDwarfFixupTable(ctxt *Link) *DwarfFixupTable {
731	return &DwarfFixupTable{
732		ctxt:      ctxt,
733		symtab:    make(map[*LSym]int),
734		precursor: make(map[*LSym]fnState),
735	}
736}
737
738func (ft *DwarfFixupTable) GetPrecursorFunc(s *LSym) interface{} {
739	if fnstate, found := ft.precursor[s]; found {
740		return fnstate.precursor
741	}
742	return nil
743}
744
745func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) {
746	if _, found := ft.precursor[s]; found {
747		ft.ctxt.Diag("internal error: DwarfFixupTable.SetPrecursorFunc double call on %v", s)
748	}
749
750	// initialize abstract function symbol now. This is done here so
751	// as to avoid data races later on during the parallel portion of
752	// the back end.
753	absfn := ft.ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name+dwarf.AbstractFuncSuffix)
754	absfn.Set(AttrDuplicateOK, true)
755	absfn.Type = objabi.SDWARFINFO
756	ft.ctxt.Data = append(ft.ctxt.Data, absfn)
757
758	ft.precursor[s] = fnState{precursor: fn, absfn: absfn}
759}
760
761// Make a note of a child DIE reference: relocation 'ridx' within symbol 's'
762// is targeting child 'c' of DIE with symbol 'tgt'.
763func (ft *DwarfFixupTable) ReferenceChildDIE(s *LSym, ridx int, tgt *LSym, dclidx int, inlIndex int) {
764	// Protect against concurrent access if multiple backend workers
765	ft.mu.Lock()
766	defer ft.mu.Unlock()
767
768	// Create entry for symbol if not already present.
769	idx, found := ft.symtab[tgt]
770	if !found {
771		ft.svec = append(ft.svec, symFixups{inlIndex: int32(inlIndex)})
772		idx = len(ft.svec) - 1
773		ft.symtab[tgt] = idx
774	}
775
776	// Do we have child DIE offsets available? If so, then apply them,
777	// otherwise create a fixup record.
778	sf := &ft.svec[idx]
779	if len(sf.doffsets) > 0 {
780		found := false
781		for _, do := range sf.doffsets {
782			if do.dclIdx == int32(dclidx) {
783				off := do.offset
784				s.R[ridx].Add += int64(off)
785				found = true
786				break
787			}
788		}
789		if !found {
790			ft.ctxt.Diag("internal error: DwarfFixupTable.ReferenceChildDIE unable to locate child DIE offset for dclIdx=%d src=%v tgt=%v", dclidx, s, tgt)
791		}
792	} else {
793		sf.fixups = append(sf.fixups, relFixup{s, int32(ridx), int32(dclidx)})
794	}
795}
796
797// Called once DWARF generation is complete for a given abstract function,
798// whose children might have been referenced via a call above. Stores
799// the offsets for any child DIEs (vars, params) so that they can be
800// consumed later in on DwarfFixupTable.Finalize, which applies any
801// outstanding fixups.
802func (ft *DwarfFixupTable) RegisterChildDIEOffsets(s *LSym, vars []*dwarf.Var, coffsets []int32) {
803	// Length of these two slices should agree
804	if len(vars) != len(coffsets) {
805		ft.ctxt.Diag("internal error: RegisterChildDIEOffsets vars/offsets length mismatch")
806		return
807	}
808
809	// Generate the slice of declOffset's based in vars/coffsets
810	doffsets := make([]declOffset, len(coffsets))
811	for i := range coffsets {
812		doffsets[i].dclIdx = vars[i].ChildIndex
813		doffsets[i].offset = coffsets[i]
814	}
815
816	ft.mu.Lock()
817	defer ft.mu.Unlock()
818
819	// Store offsets for this symbol.
820	idx, found := ft.symtab[s]
821	if !found {
822		sf := symFixups{inlIndex: -1, defseen: true, doffsets: doffsets}
823		ft.svec = append(ft.svec, sf)
824		ft.symtab[s] = len(ft.svec) - 1
825	} else {
826		sf := &ft.svec[idx]
827		sf.doffsets = doffsets
828		sf.defseen = true
829	}
830}
831
832func (ft *DwarfFixupTable) processFixups(slot int, s *LSym) {
833	sf := &ft.svec[slot]
834	for _, f := range sf.fixups {
835		dfound := false
836		for _, doffset := range sf.doffsets {
837			if doffset.dclIdx == f.dclidx {
838				f.refsym.R[f.relidx].Add += int64(doffset.offset)
839				dfound = true
840				break
841			}
842		}
843		if !dfound {
844			ft.ctxt.Diag("internal error: DwarfFixupTable has orphaned fixup on %v targeting %v relidx=%d dclidx=%d", f.refsym, s, f.relidx, f.dclidx)
845		}
846	}
847}
848
849// return the LSym corresponding to the 'abstract subprogram' DWARF
850// info entry for a function.
851func (ft *DwarfFixupTable) AbsFuncDwarfSym(fnsym *LSym) *LSym {
852	// Protect against concurrent access if multiple backend workers
853	ft.mu.Lock()
854	defer ft.mu.Unlock()
855
856	if fnstate, found := ft.precursor[fnsym]; found {
857		return fnstate.absfn
858	}
859	ft.ctxt.Diag("internal error: AbsFuncDwarfSym requested for %v, not seen during inlining", fnsym)
860	return nil
861}
862
863// Called after all functions have been compiled; the main job of this
864// function is to identify cases where there are outstanding fixups.
865// This scenario crops up when we have references to variables of an
866// inlined routine, but that routine is defined in some other package.
867// This helper walks through and locate these fixups, then invokes a
868// helper to create an abstract subprogram DIE for each one.
869func (ft *DwarfFixupTable) Finalize(myimportpath string, trace bool) {
870	if trace {
871		ft.ctxt.Logf("DwarfFixupTable.Finalize invoked for %s\n", myimportpath)
872	}
873
874	// Collect up the keys from the precursor map, then sort the
875	// resulting list (don't want to rely on map ordering here).
876	fns := make([]*LSym, len(ft.precursor))
877	idx := 0
878	for fn := range ft.precursor {
879		fns[idx] = fn
880		idx++
881	}
882	sort.Sort(BySymName(fns))
883
884	// Should not be called during parallel portion of compilation.
885	if ft.ctxt.InParallel {
886		ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize call during parallel backend")
887	}
888
889	// Generate any missing abstract functions.
890	for _, s := range fns {
891		absfn := ft.AbsFuncDwarfSym(s)
892		slot, found := ft.symtab[absfn]
893		if !found || !ft.svec[slot].defseen {
894			ft.ctxt.GenAbstractFunc(s)
895		}
896	}
897
898	// Apply fixups.
899	for _, s := range fns {
900		absfn := ft.AbsFuncDwarfSym(s)
901		slot, found := ft.symtab[absfn]
902		if !found {
903			ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize orphan abstract function for %v", s)
904		} else {
905			ft.processFixups(slot, s)
906		}
907	}
908}
909
910type BySymName []*LSym
911
912func (s BySymName) Len() int           { return len(s) }
913func (s BySymName) Less(i, j int) bool { return s[i].Name < s[j].Name }
914func (s BySymName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
915