1// Copyright 2011 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
5package dwarfgen
6
7import (
8	"bytes"
9	"flag"
10	"fmt"
11	"internal/buildcfg"
12	"sort"
13
14	"cmd/compile/internal/base"
15	"cmd/compile/internal/ir"
16	"cmd/compile/internal/reflectdata"
17	"cmd/compile/internal/ssa"
18	"cmd/compile/internal/ssagen"
19	"cmd/compile/internal/typecheck"
20	"cmd/compile/internal/types"
21	"cmd/internal/dwarf"
22	"cmd/internal/obj"
23	"cmd/internal/objabi"
24	"cmd/internal/src"
25)
26
27func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (scopes []dwarf.Scope, inlcalls dwarf.InlCalls) {
28	fn := curfn.(*ir.Func)
29
30	if fn.Nname != nil {
31		expect := fn.Linksym()
32		if fnsym.ABI() == obj.ABI0 {
33			expect = fn.LinksymABI(obj.ABI0)
34		}
35		if fnsym != expect {
36			base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
37		}
38	}
39
40	// Back when there were two different *Funcs for a function, this code
41	// was not consistent about whether a particular *Node being processed
42	// was an ODCLFUNC or ONAME node. Partly this is because inlined function
43	// bodies have no ODCLFUNC node, which was it's own inconsistency.
44	// In any event, the handling of the two different nodes for DWARF purposes
45	// was subtly different, likely in unintended ways. CL 272253 merged the
46	// two nodes' Func fields, so that code sees the same *Func whether it is
47	// holding the ODCLFUNC or the ONAME. This resulted in changes in the
48	// DWARF output. To preserve the existing DWARF output and leave an
49	// intentional change for a future CL, this code does the following when
50	// fn.Op == ONAME:
51	//
52	// 1. Disallow use of createComplexVars in createDwarfVars.
53	//    It was not possible to reach that code for an ONAME before,
54	//    because the DebugInfo was set only on the ODCLFUNC Func.
55	//    Calling into it in the ONAME case causes an index out of bounds panic.
56	//
57	// 2. Do not populate apdecls. fn.Func.Dcl was in the ODCLFUNC Func,
58	//    not the ONAME Func. Populating apdecls for the ONAME case results
59	//    in selected being populated after createSimpleVars is called in
60	//    createDwarfVars, and then that causes the loop to skip all the entries
61	//    in dcl, meaning that the RecordAutoType calls don't happen.
62	//
63	// These two adjustments keep toolstash -cmp working for now.
64	// Deciding the right answer is, as they say, future work.
65	//
66	// We can tell the difference between the old ODCLFUNC and ONAME
67	// cases by looking at the infosym.Name. If it's empty, DebugInfo is
68	// being called from (*obj.Link).populateDWARF, which used to use
69	// the ODCLFUNC. If it's non-empty (the name will end in $abstract),
70	// DebugInfo is being called from (*obj.Link).DwarfAbstractFunc,
71	// which used to use the ONAME form.
72	isODCLFUNC := infosym.Name == ""
73
74	var apdecls []*ir.Name
75	// Populate decls for fn.
76	if isODCLFUNC {
77		for _, n := range fn.Dcl {
78			if n.Op() != ir.ONAME { // might be OTYPE or OLITERAL
79				continue
80			}
81			switch n.Class {
82			case ir.PAUTO:
83				if !n.Used() {
84					// Text == nil -> generating abstract function
85					if fnsym.Func().Text != nil {
86						base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
87					}
88					continue
89				}
90			case ir.PPARAM, ir.PPARAMOUT:
91			default:
92				continue
93			}
94			if !ssa.IsVarWantedForDebug(n) {
95				continue
96			}
97			apdecls = append(apdecls, n)
98			if n.Type().Kind() == types.TSSA {
99				// Can happen for TypeInt128 types. This only happens for
100				// spill locations, so not a huge deal.
101				continue
102			}
103			fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
104		}
105	}
106
107	var closureVars map[*ir.Name]int64
108	if fn.Needctxt() {
109		closureVars = make(map[*ir.Name]int64)
110		csiter := typecheck.NewClosureStructIter(fn.ClosureVars)
111		for {
112			n, _, offset := csiter.Next()
113			if n == nil {
114				break
115			}
116			closureVars[n] = offset
117			if n.Heapaddr != nil {
118				closureVars[n.Heapaddr] = offset
119			}
120		}
121	}
122
123	decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls, closureVars)
124
125	// For each type referenced by the functions auto vars but not
126	// already referenced by a dwarf var, attach an R_USETYPE relocation to
127	// the function symbol to insure that the type included in DWARF
128	// processing during linking.
129	typesyms := []*obj.LSym{}
130	for t := range fnsym.Func().Autot {
131		typesyms = append(typesyms, t)
132	}
133	sort.Sort(obj.BySymName(typesyms))
134	for _, sym := range typesyms {
135		r := obj.Addrel(infosym)
136		r.Sym = sym
137		r.Type = objabi.R_USETYPE
138	}
139	fnsym.Func().Autot = nil
140
141	var varScopes []ir.ScopeID
142	for _, decl := range decls {
143		pos := declPos(decl)
144		varScopes = append(varScopes, findScope(fn.Marks, pos))
145	}
146
147	scopes = assembleScopes(fnsym, fn, dwarfVars, varScopes)
148	if base.Flag.GenDwarfInl > 0 {
149		inlcalls = assembleInlines(fnsym, dwarfVars)
150	}
151	return scopes, inlcalls
152}
153
154func declPos(decl *ir.Name) src.XPos {
155	return decl.Canonical().Pos()
156}
157
158// createDwarfVars process fn, returning a list of DWARF variables and the
159// Nodes they represent.
160func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var) {
161	// Collect a raw list of DWARF vars.
162	var vars []*dwarf.Var
163	var decls []*ir.Name
164	var selected ir.NameSet
165
166	if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK {
167		decls, vars, selected = createComplexVars(fnsym, fn, closureVars)
168	} else if fn.ABI == obj.ABIInternal && base.Flag.N != 0 && complexOK {
169		decls, vars, selected = createABIVars(fnsym, fn, apDecls, closureVars)
170	} else {
171		decls, vars, selected = createSimpleVars(fnsym, apDecls, closureVars)
172	}
173	if fn.DebugInfo != nil {
174		// Recover zero sized variables eliminated by the stackframe pass
175		for _, n := range fn.DebugInfo.(*ssa.FuncDebug).OptDcl {
176			if n.Class != ir.PAUTO {
177				continue
178			}
179			types.CalcSize(n.Type())
180			if n.Type().Size() == 0 {
181				decls = append(decls, n)
182				vars = append(vars, createSimpleVar(fnsym, n, closureVars))
183				vars[len(vars)-1].StackOffset = 0
184				fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
185			}
186		}
187	}
188
189	dcl := apDecls
190	if fnsym.WasInlined() {
191		dcl = preInliningDcls(fnsym)
192	} else {
193		// The backend's stackframe pass prunes away entries from the
194		// fn's Dcl list, including PARAMOUT nodes that correspond to
195		// output params passed in registers. Add back in these
196		// entries here so that we can process them properly during
197		// DWARF-gen. See issue 48573 for more details.
198		debugInfo := fn.DebugInfo.(*ssa.FuncDebug)
199		for _, n := range debugInfo.RegOutputParams {
200			if !ssa.IsVarWantedForDebug(n) {
201				continue
202			}
203			if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() {
204				panic("invalid ir.Name on debugInfo.RegOutputParams list")
205			}
206			dcl = append(dcl, n)
207		}
208	}
209
210	// If optimization is enabled, the list above will typically be
211	// missing some of the original pre-optimization variables in the
212	// function (they may have been promoted to registers, folded into
213	// constants, dead-coded away, etc).  Input arguments not eligible
214	// for SSA optimization are also missing.  Here we add back in entries
215	// for selected missing vars. Note that the recipe below creates a
216	// conservative location. The idea here is that we want to
217	// communicate to the user that "yes, there is a variable named X
218	// in this function, but no, I don't have enough information to
219	// reliably report its contents."
220	// For non-SSA-able arguments, however, the correct information
221	// is known -- they have a single home on the stack.
222	for _, n := range dcl {
223		if selected.Has(n) {
224			continue
225		}
226		c := n.Sym().Name[0]
227		if c == '.' || n.Type().IsUntyped() {
228			continue
229		}
230		if n.Class == ir.PPARAM && !ssa.CanSSA(n.Type()) {
231			// SSA-able args get location lists, and may move in and
232			// out of registers, so those are handled elsewhere.
233			// Autos and named output params seem to get handled
234			// with VARDEF, which creates location lists.
235			// Args not of SSA-able type are treated here; they
236			// are homed on the stack in a single place for the
237			// entire call.
238			vars = append(vars, createSimpleVar(fnsym, n, closureVars))
239			decls = append(decls, n)
240			continue
241		}
242		typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
243		decls = append(decls, n)
244		tag := dwarf.DW_TAG_variable
245		isReturnValue := (n.Class == ir.PPARAMOUT)
246		if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
247			tag = dwarf.DW_TAG_formal_parameter
248		}
249		if n.Esc() == ir.EscHeap {
250			// The variable in question has been promoted to the heap.
251			// Its address is in n.Heapaddr.
252			// TODO(thanm): generate a better location expression
253		}
254		inlIndex := 0
255		if base.Flag.GenDwarfInl > 1 {
256			if n.InlFormal() || n.InlLocal() {
257				inlIndex = posInlIndex(n.Pos()) + 1
258				if n.InlFormal() {
259					tag = dwarf.DW_TAG_formal_parameter
260				}
261			}
262		}
263		declpos := base.Ctxt.InnermostPos(n.Pos())
264		vars = append(vars, &dwarf.Var{
265			Name:          n.Sym().Name,
266			IsReturnValue: isReturnValue,
267			Tag:           tag,
268			WithLoclist:   true,
269			StackOffset:   int32(n.FrameOffset()),
270			Type:          base.Ctxt.Lookup(typename),
271			DeclFile:      declpos.RelFilename(),
272			DeclLine:      declpos.RelLine(),
273			DeclCol:       declpos.RelCol(),
274			InlIndex:      int32(inlIndex),
275			ChildIndex:    -1,
276			DictIndex:     n.DictIndex,
277			ClosureOffset: closureOffset(n, closureVars),
278		})
279		// Record go type of to insure that it gets emitted by the linker.
280		fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
281	}
282
283	// Sort decls and vars.
284	sortDeclsAndVars(fn, decls, vars)
285
286	return decls, vars
287}
288
289// sortDeclsAndVars sorts the decl and dwarf var lists according to
290// parameter declaration order, so as to insure that when a subprogram
291// DIE is emitted, its parameter children appear in declaration order.
292// Prior to the advent of the register ABI, sorting by frame offset
293// would achieve this; with the register we now need to go back to the
294// original function signature.
295func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) {
296	paramOrder := make(map[*ir.Name]int)
297	idx := 1
298	for _, f := range fn.Type().RecvParamsResults() {
299		if n, ok := f.Nname.(*ir.Name); ok {
300			paramOrder[n] = idx
301			idx++
302		}
303	}
304	sort.Stable(varsAndDecls{decls, vars, paramOrder})
305}
306
307type varsAndDecls struct {
308	decls      []*ir.Name
309	vars       []*dwarf.Var
310	paramOrder map[*ir.Name]int
311}
312
313func (v varsAndDecls) Len() int {
314	return len(v.decls)
315}
316
317func (v varsAndDecls) Less(i, j int) bool {
318	nameLT := func(ni, nj *ir.Name) bool {
319		oi, foundi := v.paramOrder[ni]
320		oj, foundj := v.paramOrder[nj]
321		if foundi {
322			if foundj {
323				return oi < oj
324			} else {
325				return true
326			}
327		}
328		return false
329	}
330	return nameLT(v.decls[i], v.decls[j])
331}
332
333func (v varsAndDecls) Swap(i, j int) {
334	v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
335	v.decls[i], v.decls[j] = v.decls[j], v.decls[i]
336}
337
338// Given a function that was inlined at some point during the
339// compilation, return a sorted list of nodes corresponding to the
340// autos/locals in that function prior to inlining. If this is a
341// function that is not local to the package being compiled, then the
342// names of the variables may have been "versioned" to avoid conflicts
343// with local vars; disregard this versioning when sorting.
344func preInliningDcls(fnsym *obj.LSym) []*ir.Name {
345	fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func)
346	var rdcl []*ir.Name
347	for _, n := range fn.Inl.Dcl {
348		c := n.Sym().Name[0]
349		// Avoid reporting "_" parameters, since if there are more than
350		// one, it can result in a collision later on, as in #23179.
351		if n.Sym().Name == "_" || c == '.' || n.Type().IsUntyped() {
352			continue
353		}
354		rdcl = append(rdcl, n)
355	}
356	return rdcl
357}
358
359// createSimpleVars creates a DWARF entry for every variable declared in the
360// function, claiming that they are permanently on the stack.
361func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
362	var vars []*dwarf.Var
363	var decls []*ir.Name
364	var selected ir.NameSet
365	for _, n := range apDecls {
366		if ir.IsAutoTmp(n) {
367			continue
368		}
369
370		decls = append(decls, n)
371		vars = append(vars, createSimpleVar(fnsym, n, closureVars))
372		selected.Add(n)
373	}
374	return decls, vars, selected
375}
376
377func createSimpleVar(fnsym *obj.LSym, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var {
378	var tag int
379	var offs int64
380
381	localAutoOffset := func() int64 {
382		offs = n.FrameOffset()
383		if base.Ctxt.Arch.FixedFrameSize == 0 {
384			offs -= int64(types.PtrSize)
385		}
386		if buildcfg.FramePointerEnabled {
387			offs -= int64(types.PtrSize)
388		}
389		return offs
390	}
391
392	switch n.Class {
393	case ir.PAUTO:
394		offs = localAutoOffset()
395		tag = dwarf.DW_TAG_variable
396	case ir.PPARAM, ir.PPARAMOUT:
397		tag = dwarf.DW_TAG_formal_parameter
398		if n.IsOutputParamInRegisters() {
399			offs = localAutoOffset()
400		} else {
401			offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize
402		}
403
404	default:
405		base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n)
406	}
407
408	typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
409	delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type()))
410	inlIndex := 0
411	if base.Flag.GenDwarfInl > 1 {
412		if n.InlFormal() || n.InlLocal() {
413			inlIndex = posInlIndex(n.Pos()) + 1
414			if n.InlFormal() {
415				tag = dwarf.DW_TAG_formal_parameter
416			}
417		}
418	}
419	declpos := base.Ctxt.InnermostPos(declPos(n))
420	return &dwarf.Var{
421		Name:          n.Sym().Name,
422		IsReturnValue: n.Class == ir.PPARAMOUT,
423		IsInlFormal:   n.InlFormal(),
424		Tag:           tag,
425		StackOffset:   int32(offs),
426		Type:          base.Ctxt.Lookup(typename),
427		DeclFile:      declpos.RelFilename(),
428		DeclLine:      declpos.RelLine(),
429		DeclCol:       declpos.RelCol(),
430		InlIndex:      int32(inlIndex),
431		ChildIndex:    -1,
432		DictIndex:     n.DictIndex,
433		ClosureOffset: closureOffset(n, closureVars),
434	}
435}
436
437// createABIVars creates DWARF variables for functions in which the
438// register ABI is enabled but optimization is turned off. It uses a
439// hybrid approach in which register-resident input params are
440// captured with location lists, and all other vars use the "simple"
441// strategy.
442func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
443
444	// Invoke createComplexVars to generate dwarf vars for input parameters
445	// that are register-allocated according to the ABI rules.
446	decls, vars, selected := createComplexVars(fnsym, fn, closureVars)
447
448	// Now fill in the remainder of the variables: input parameters
449	// that are not register-resident, output parameters, and local
450	// variables.
451	for _, n := range apDecls {
452		if ir.IsAutoTmp(n) {
453			continue
454		}
455		if _, ok := selected[n]; ok {
456			// already handled
457			continue
458		}
459
460		decls = append(decls, n)
461		vars = append(vars, createSimpleVar(fnsym, n, closureVars))
462		selected.Add(n)
463	}
464
465	return decls, vars, selected
466}
467
468// createComplexVars creates recomposed DWARF vars with location lists,
469// suitable for describing optimized code.
470func createComplexVars(fnsym *obj.LSym, fn *ir.Func, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
471	debugInfo := fn.DebugInfo.(*ssa.FuncDebug)
472
473	// Produce a DWARF variable entry for each user variable.
474	var decls []*ir.Name
475	var vars []*dwarf.Var
476	var ssaVars ir.NameSet
477
478	for varID, dvar := range debugInfo.Vars {
479		n := dvar
480		ssaVars.Add(n)
481		for _, slot := range debugInfo.VarSlots[varID] {
482			ssaVars.Add(debugInfo.Slots[slot].N)
483		}
484
485		if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID), closureVars); dvar != nil {
486			decls = append(decls, n)
487			vars = append(vars, dvar)
488		}
489	}
490
491	return decls, vars, ssaVars
492}
493
494// createComplexVar builds a single DWARF variable entry and location list.
495func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars map[*ir.Name]int64) *dwarf.Var {
496	debug := fn.DebugInfo.(*ssa.FuncDebug)
497	n := debug.Vars[varID]
498
499	var tag int
500	switch n.Class {
501	case ir.PAUTO:
502		tag = dwarf.DW_TAG_variable
503	case ir.PPARAM, ir.PPARAMOUT:
504		tag = dwarf.DW_TAG_formal_parameter
505	default:
506		return nil
507	}
508
509	gotype := reflectdata.TypeLinksym(n.Type())
510	delete(fnsym.Func().Autot, gotype)
511	typename := dwarf.InfoPrefix + gotype.Name[len("type:"):]
512	inlIndex := 0
513	if base.Flag.GenDwarfInl > 1 {
514		if n.InlFormal() || n.InlLocal() {
515			inlIndex = posInlIndex(n.Pos()) + 1
516			if n.InlFormal() {
517				tag = dwarf.DW_TAG_formal_parameter
518			}
519		}
520	}
521	declpos := base.Ctxt.InnermostPos(n.Pos())
522	dvar := &dwarf.Var{
523		Name:          n.Sym().Name,
524		IsReturnValue: n.Class == ir.PPARAMOUT,
525		IsInlFormal:   n.InlFormal(),
526		Tag:           tag,
527		WithLoclist:   true,
528		Type:          base.Ctxt.Lookup(typename),
529		// The stack offset is used as a sorting key, so for decomposed
530		// variables just give it the first one. It's not used otherwise.
531		// This won't work well if the first slot hasn't been assigned a stack
532		// location, but it's not obvious how to do better.
533		StackOffset:   ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]),
534		DeclFile:      declpos.RelFilename(),
535		DeclLine:      declpos.RelLine(),
536		DeclCol:       declpos.RelCol(),
537		InlIndex:      int32(inlIndex),
538		ChildIndex:    -1,
539		DictIndex:     n.DictIndex,
540		ClosureOffset: closureOffset(n, closureVars),
541	}
542	list := debug.LocationLists[varID]
543	if len(list) != 0 {
544		dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
545			debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
546		}
547	}
548	return dvar
549}
550
551// RecordFlags records the specified command-line flags to be placed
552// in the DWARF info.
553func RecordFlags(flags ...string) {
554	if base.Ctxt.Pkgpath == "" {
555		panic("missing pkgpath")
556	}
557
558	type BoolFlag interface {
559		IsBoolFlag() bool
560	}
561	type CountFlag interface {
562		IsCountFlag() bool
563	}
564	var cmd bytes.Buffer
565	for _, name := range flags {
566		f := flag.Lookup(name)
567		if f == nil {
568			continue
569		}
570		getter := f.Value.(flag.Getter)
571		if getter.String() == f.DefValue {
572			// Flag has default value, so omit it.
573			continue
574		}
575		if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() {
576			val, ok := getter.Get().(bool)
577			if ok && val {
578				fmt.Fprintf(&cmd, " -%s", f.Name)
579				continue
580			}
581		}
582		if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() {
583			val, ok := getter.Get().(int)
584			if ok && val == 1 {
585				fmt.Fprintf(&cmd, " -%s", f.Name)
586				continue
587			}
588		}
589		fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
590	}
591
592	// Adds flag to producer string signaling whether regabi is turned on or
593	// off.
594	// Once regabi is turned on across the board and the relative GOEXPERIMENT
595	// knobs no longer exist this code should be removed.
596	if buildcfg.Experiment.RegabiArgs {
597		cmd.Write([]byte(" regabi"))
598	}
599
600	if cmd.Len() == 0 {
601		return
602	}
603	s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath)
604	s.Type = objabi.SDWARFCUINFO
605	// Sometimes (for example when building tests) we can link
606	// together two package main archives. So allow dups.
607	s.Set(obj.AttrDuplicateOK, true)
608	base.Ctxt.Data = append(base.Ctxt.Data, s)
609	s.P = cmd.Bytes()[1:]
610}
611
612// RecordPackageName records the name of the package being
613// compiled, so that the linker can save it in the compile unit's DIE.
614func RecordPackageName() {
615	s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath)
616	s.Type = objabi.SDWARFCUINFO
617	// Sometimes (for example when building tests) we can link
618	// together two package main archives. So allow dups.
619	s.Set(obj.AttrDuplicateOK, true)
620	base.Ctxt.Data = append(base.Ctxt.Data, s)
621	s.P = []byte(types.LocalPkg.Name)
622}
623
624func closureOffset(n *ir.Name, closureVars map[*ir.Name]int64) int64 {
625	return closureVars[n]
626}
627