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
5package obj
6
7import (
8	"cmd/internal/objabi"
9	"cmd/internal/src"
10	"fmt"
11	"internal/abi"
12	"strings"
13)
14
15type Plist struct {
16	Firstpc *Prog
17	Curfn   Func
18}
19
20// ProgAlloc is a function that allocates Progs.
21// It is used to provide access to cached/bulk-allocated Progs to the assemblers.
22type ProgAlloc func() *Prog
23
24func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc) {
25	if ctxt.Pkgpath == "" {
26		panic("Flushplist called without Pkgpath")
27	}
28
29	// Build list of symbols, and assign instructions to lists.
30	var curtext *LSym
31	var etext *Prog
32	var text []*LSym
33
34	var plink *Prog
35	for p := plist.Firstpc; p != nil; p = plink {
36		if ctxt.Debugasm > 0 && ctxt.Debugvlog {
37			fmt.Printf("obj: %v\n", p)
38		}
39		plink = p.Link
40		p.Link = nil
41
42		switch p.As {
43		case AEND:
44			continue
45
46		case ATEXT:
47			s := p.From.Sym
48			if s == nil {
49				// func _() { }
50				curtext = nil
51				continue
52			}
53			text = append(text, s)
54			etext = p
55			curtext = s
56			continue
57
58		case AFUNCDATA:
59			// Rewrite reference to go_args_stackmap(SB) to the Go-provided declaration information.
60			if curtext == nil { // func _() {}
61				continue
62			}
63			switch p.To.Sym.Name {
64			case "go_args_stackmap":
65				if p.From.Type != TYPE_CONST || p.From.Offset != abi.FUNCDATA_ArgsPointerMaps {
66					ctxt.Diag("%s: FUNCDATA use of go_args_stackmap(SB) without FUNCDATA_ArgsPointerMaps", p.Pos)
67				}
68				p.To.Sym = ctxt.LookupDerived(curtext, curtext.Name+".args_stackmap")
69			case "no_pointers_stackmap":
70				if p.From.Type != TYPE_CONST || p.From.Offset != abi.FUNCDATA_LocalsPointerMaps {
71					ctxt.Diag("%s: FUNCDATA use of no_pointers_stackmap(SB) without FUNCDATA_LocalsPointerMaps", p.Pos)
72				}
73				// funcdata for functions with no local variables in frame.
74				// Define two zero-length bitmaps, because the same index is used
75				// for the local variables as for the argument frame, and assembly
76				// frames have two argument bitmaps, one without results and one with results.
77				// Write []uint32{2, 0}.
78				b := make([]byte, 8)
79				ctxt.Arch.ByteOrder.PutUint32(b, 2)
80				s := ctxt.GCLocalsSym(b)
81				if !s.OnList() {
82					ctxt.Globl(s, int64(len(s.P)), int(RODATA|DUPOK))
83				}
84				p.To.Sym = s
85			}
86
87		}
88
89		if curtext == nil {
90			etext = nil
91			continue
92		}
93		etext.Link = p
94		etext = p
95	}
96
97	if newprog == nil {
98		newprog = ctxt.NewProg
99	}
100
101	// Add reference to Go arguments for assembly functions without them.
102	if ctxt.IsAsm {
103		pkgPrefix := objabi.PathToPrefix(ctxt.Pkgpath) + "."
104		for _, s := range text {
105			if !strings.HasPrefix(s.Name, pkgPrefix) {
106				continue
107			}
108			// The current args_stackmap generation in the compiler assumes
109			// that the function in question is ABI0, so avoid introducing
110			// an args_stackmap reference if the func is not ABI0 (better to
111			// have no stackmap than an incorrect/lying stackmap).
112			if s.ABI() != ABI0 {
113				continue
114			}
115			// runtime.addmoduledata is a host ABI function, so it doesn't
116			// need FUNCDATA anyway. Moreover, cmd/link has special logic
117			// for linking it in eccentric build modes, which breaks if it
118			// has FUNCDATA references (e.g., cmd/cgo/internal/testplugin).
119			//
120			// TODO(cherryyz): Fix cmd/link's handling of plugins (see
121			// discussion on CL 523355).
122			if s.Name == "runtime.addmoduledata" {
123				continue
124			}
125			foundArgMap, foundArgInfo := false, false
126			for p := s.Func().Text; p != nil; p = p.Link {
127				if p.As == AFUNCDATA && p.From.Type == TYPE_CONST {
128					if p.From.Offset == abi.FUNCDATA_ArgsPointerMaps {
129						foundArgMap = true
130					}
131					if p.From.Offset == abi.FUNCDATA_ArgInfo {
132						foundArgInfo = true
133					}
134					if foundArgMap && foundArgInfo {
135						break
136					}
137				}
138			}
139			if !foundArgMap {
140				p := Appendp(s.Func().Text, newprog)
141				p.As = AFUNCDATA
142				p.From.Type = TYPE_CONST
143				p.From.Offset = abi.FUNCDATA_ArgsPointerMaps
144				p.To.Type = TYPE_MEM
145				p.To.Name = NAME_EXTERN
146				p.To.Sym = ctxt.LookupDerived(s, s.Name+".args_stackmap")
147			}
148			if !foundArgInfo {
149				p := Appendp(s.Func().Text, newprog)
150				p.As = AFUNCDATA
151				p.From.Type = TYPE_CONST
152				p.From.Offset = abi.FUNCDATA_ArgInfo
153				p.To.Type = TYPE_MEM
154				p.To.Name = NAME_EXTERN
155				p.To.Sym = ctxt.LookupDerived(s, fmt.Sprintf("%s.arginfo%d", s.Name, s.ABI()))
156			}
157		}
158	}
159
160	// Turn functions into machine code images.
161	for _, s := range text {
162		mkfwd(s)
163		if ctxt.Arch.ErrorCheck != nil {
164			ctxt.Arch.ErrorCheck(ctxt, s)
165		}
166		linkpatch(ctxt, s, newprog)
167		ctxt.Arch.Preprocess(ctxt, s, newprog)
168		ctxt.Arch.Assemble(ctxt, s, newprog)
169		if ctxt.Errors > 0 {
170			continue
171		}
172		linkpcln(ctxt, s)
173		ctxt.populateDWARF(plist.Curfn, s)
174		if ctxt.Headtype == objabi.Hwindows && ctxt.Arch.SEH != nil {
175			s.Func().sehUnwindInfoSym = ctxt.Arch.SEH(ctxt, s)
176		}
177	}
178}
179
180func (ctxt *Link) InitTextSym(s *LSym, flag int, start src.XPos) {
181	if s == nil {
182		// func _() { }
183		return
184	}
185	if s.Func() != nil {
186		ctxt.Diag("%s: symbol %s redeclared\n\t%s: other declaration of symbol %s", ctxt.PosTable.Pos(start), s.Name, ctxt.PosTable.Pos(s.Func().Text.Pos), s.Name)
187		return
188	}
189	s.NewFuncInfo()
190	if s.OnList() {
191		ctxt.Diag("%s: symbol %s redeclared", ctxt.PosTable.Pos(start), s.Name)
192		return
193	}
194	if strings.HasPrefix(s.Name, `"".`) {
195		ctxt.Diag("%s: unqualified symbol name: %s", ctxt.PosTable.Pos(start), s.Name)
196	}
197
198	// startLine should be the same line number that would be displayed via
199	// pcln, etc for the declaration (i.e., relative line number, as
200	// adjusted by //line).
201	_, startLine := ctxt.getFileIndexAndLine(start)
202
203	s.Func().FuncID = objabi.GetFuncID(s.Name, flag&WRAPPER != 0 || flag&ABIWRAPPER != 0)
204	s.Func().FuncFlag = ctxt.toFuncFlag(flag)
205	s.Func().StartLine = startLine
206	s.Set(AttrOnList, true)
207	s.Set(AttrDuplicateOK, flag&DUPOK != 0)
208	s.Set(AttrNoSplit, flag&NOSPLIT != 0)
209	s.Set(AttrReflectMethod, flag&REFLECTMETHOD != 0)
210	s.Set(AttrWrapper, flag&WRAPPER != 0)
211	s.Set(AttrABIWrapper, flag&ABIWRAPPER != 0)
212	s.Set(AttrNeedCtxt, flag&NEEDCTXT != 0)
213	s.Set(AttrNoFrame, flag&NOFRAME != 0)
214	s.Set(AttrPkgInit, flag&PKGINIT != 0)
215	s.Type = objabi.STEXT
216	ctxt.Text = append(ctxt.Text, s)
217
218	// Set up DWARF entries for s
219	ctxt.dwarfSym(s)
220}
221
222func (ctxt *Link) toFuncFlag(flag int) abi.FuncFlag {
223	var out abi.FuncFlag
224	if flag&TOPFRAME != 0 {
225		out |= abi.FuncFlagTopFrame
226	}
227	if ctxt.IsAsm {
228		out |= abi.FuncFlagAsm
229	}
230	return out
231}
232
233func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
234	ctxt.GloblPos(s, size, flag, src.NoXPos)
235}
236func (ctxt *Link) GloblPos(s *LSym, size int64, flag int, pos src.XPos) {
237	if s.OnList() {
238		// TODO: print where the first declaration was.
239		ctxt.Diag("%s: symbol %s redeclared", ctxt.PosTable.Pos(pos), s.Name)
240	}
241	s.Set(AttrOnList, true)
242	ctxt.Data = append(ctxt.Data, s)
243	s.Size = size
244	if s.Type == 0 {
245		s.Type = objabi.SBSS
246	}
247	if flag&DUPOK != 0 {
248		s.Set(AttrDuplicateOK, true)
249	}
250	if flag&RODATA != 0 {
251		s.Type = objabi.SRODATA
252	} else if flag&NOPTR != 0 {
253		if s.Type == objabi.SDATA {
254			s.Type = objabi.SNOPTRDATA
255		} else {
256			s.Type = objabi.SNOPTRBSS
257		}
258	} else if flag&TLSBSS != 0 {
259		s.Type = objabi.STLSBSS
260	}
261}
262
263// EmitEntryLiveness generates PCDATA Progs after p to switch to the
264// liveness map active at the entry of function s. It returns the last
265// Prog generated.
266func (ctxt *Link) EmitEntryLiveness(s *LSym, p *Prog, newprog ProgAlloc) *Prog {
267	pcdata := ctxt.EmitEntryStackMap(s, p, newprog)
268	pcdata = ctxt.EmitEntryUnsafePoint(s, pcdata, newprog)
269	return pcdata
270}
271
272// Similar to EmitEntryLiveness, but just emit stack map.
273func (ctxt *Link) EmitEntryStackMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog {
274	pcdata := Appendp(p, newprog)
275	pcdata.Pos = s.Func().Text.Pos
276	pcdata.As = APCDATA
277	pcdata.From.Type = TYPE_CONST
278	pcdata.From.Offset = abi.PCDATA_StackMapIndex
279	pcdata.To.Type = TYPE_CONST
280	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
281
282	return pcdata
283}
284
285// Similar to EmitEntryLiveness, but just emit unsafe point map.
286func (ctxt *Link) EmitEntryUnsafePoint(s *LSym, p *Prog, newprog ProgAlloc) *Prog {
287	pcdata := Appendp(p, newprog)
288	pcdata.Pos = s.Func().Text.Pos
289	pcdata.As = APCDATA
290	pcdata.From.Type = TYPE_CONST
291	pcdata.From.Offset = abi.PCDATA_UnsafePoint
292	pcdata.To.Type = TYPE_CONST
293	pcdata.To.Offset = -1
294
295	return pcdata
296}
297
298// StartUnsafePoint generates PCDATA Progs after p to mark the
299// beginning of an unsafe point. The unsafe point starts immediately
300// after p.
301// It returns the last Prog generated.
302func (ctxt *Link) StartUnsafePoint(p *Prog, newprog ProgAlloc) *Prog {
303	pcdata := Appendp(p, newprog)
304	pcdata.As = APCDATA
305	pcdata.From.Type = TYPE_CONST
306	pcdata.From.Offset = abi.PCDATA_UnsafePoint
307	pcdata.To.Type = TYPE_CONST
308	pcdata.To.Offset = abi.UnsafePointUnsafe
309
310	return pcdata
311}
312
313// EndUnsafePoint generates PCDATA Progs after p to mark the end of an
314// unsafe point, restoring the register map index to oldval.
315// The unsafe point ends right after p.
316// It returns the last Prog generated.
317func (ctxt *Link) EndUnsafePoint(p *Prog, newprog ProgAlloc, oldval int64) *Prog {
318	pcdata := Appendp(p, newprog)
319	pcdata.As = APCDATA
320	pcdata.From.Type = TYPE_CONST
321	pcdata.From.Offset = abi.PCDATA_UnsafePoint
322	pcdata.To.Type = TYPE_CONST
323	pcdata.To.Offset = oldval
324
325	return pcdata
326}
327
328// MarkUnsafePoints inserts PCDATAs to mark nonpreemptible and restartable
329// instruction sequences, based on isUnsafePoint and isRestartable predicate.
330// p0 is the start of the instruction stream.
331// isUnsafePoint(p) returns true if p is not safe for async preemption.
332// isRestartable(p) returns true if we can restart at the start of p (this Prog)
333// upon async preemption. (Currently multi-Prog restartable sequence is not
334// supported.)
335// isRestartable can be nil. In this case it is treated as always returning false.
336// If isUnsafePoint(p) and isRestartable(p) are both true, it is treated as
337// an unsafe point.
338func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, isRestartable func(*Prog) bool) {
339	if isRestartable == nil {
340		// Default implementation: nothing is restartable.
341		isRestartable = func(*Prog) bool { return false }
342	}
343	prev := p0
344	prevPcdata := int64(-1) // entry PC data value
345	prevRestart := int64(0)
346	for p := prev.Link; p != nil; p, prev = p.Link, p {
347		if p.As == APCDATA && p.From.Offset == abi.PCDATA_UnsafePoint {
348			prevPcdata = p.To.Offset
349			continue
350		}
351		if prevPcdata == abi.UnsafePointUnsafe {
352			continue // already unsafe
353		}
354		if isUnsafePoint(p) {
355			q := ctxt.StartUnsafePoint(prev, newprog)
356			q.Pc = p.Pc
357			q.Link = p
358			// Advance to the end of unsafe point.
359			for p.Link != nil && isUnsafePoint(p.Link) {
360				p = p.Link
361			}
362			if p.Link == nil {
363				break // Reached the end, don't bother marking the end
364			}
365			p = ctxt.EndUnsafePoint(p, newprog, prevPcdata)
366			p.Pc = p.Link.Pc
367			continue
368		}
369		if isRestartable(p) {
370			val := int64(abi.UnsafePointRestart1)
371			if val == prevRestart {
372				val = abi.UnsafePointRestart2
373			}
374			prevRestart = val
375			q := Appendp(prev, newprog)
376			q.As = APCDATA
377			q.From.Type = TYPE_CONST
378			q.From.Offset = abi.PCDATA_UnsafePoint
379			q.To.Type = TYPE_CONST
380			q.To.Offset = val
381			q.Pc = p.Pc
382			q.Link = p
383
384			if p.Link == nil {
385				break // Reached the end, don't bother marking the end
386			}
387			if isRestartable(p.Link) {
388				// Next Prog is also restartable. No need to mark the end
389				// of this sequence. We'll just go ahead mark the next one.
390				continue
391			}
392			p = Appendp(p, newprog)
393			p.As = APCDATA
394			p.From.Type = TYPE_CONST
395			p.From.Offset = abi.PCDATA_UnsafePoint
396			p.To.Type = TYPE_CONST
397			p.To.Offset = prevPcdata
398			p.Pc = p.Link.Pc
399		}
400	}
401}
402