1// Copyright 2019 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 Go object files.
6
7package obj
8
9import (
10	"bytes"
11	"cmd/internal/bio"
12	"cmd/internal/goobj2"
13	"cmd/internal/objabi"
14	"fmt"
15	"path/filepath"
16	"strings"
17)
18
19// Entry point of writing new object file.
20func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) {
21	if ctxt.Debugasm > 0 {
22		ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
23	}
24
25	genFuncInfoSyms(ctxt)
26
27	w := writer{
28		Writer:  goobj2.NewWriter(b),
29		ctxt:    ctxt,
30		pkgpath: objabi.PathToPrefix(pkgpath),
31	}
32
33	start := b.Offset()
34	w.init()
35
36	// Header
37	// We just reserve the space. We'll fill in the offsets later.
38	flags := uint32(0)
39	if ctxt.Flag_shared {
40		flags |= goobj2.ObjFlagShared
41	}
42	h := goobj2.Header{Magic: goobj2.Magic, Flags: flags}
43	h.Write(w.Writer)
44
45	// String table
46	w.StringTable()
47
48	// Autolib
49	h.Offsets[goobj2.BlkAutolib] = w.Offset()
50	for _, pkg := range ctxt.Imports {
51		w.StringRef(pkg)
52	}
53
54	// Package references
55	h.Offsets[goobj2.BlkPkgIdx] = w.Offset()
56	for _, pkg := range w.pkglist {
57		w.StringRef(pkg)
58	}
59
60	// DWARF file table
61	h.Offsets[goobj2.BlkDwarfFile] = w.Offset()
62	for _, f := range ctxt.PosTable.DebugLinesFileTable() {
63		w.StringRef(f)
64	}
65
66	// Symbol definitions
67	h.Offsets[goobj2.BlkSymdef] = w.Offset()
68	for _, s := range ctxt.defs {
69		w.Sym(s)
70	}
71
72	// Non-pkg symbol definitions
73	h.Offsets[goobj2.BlkNonpkgdef] = w.Offset()
74	for _, s := range ctxt.nonpkgdefs {
75		w.Sym(s)
76	}
77
78	// Non-pkg symbol references
79	h.Offsets[goobj2.BlkNonpkgref] = w.Offset()
80	for _, s := range ctxt.nonpkgrefs {
81		w.Sym(s)
82	}
83
84	// Reloc indexes
85	h.Offsets[goobj2.BlkRelocIdx] = w.Offset()
86	nreloc := uint32(0)
87	lists := [][]*LSym{ctxt.defs, ctxt.nonpkgdefs}
88	for _, list := range lists {
89		for _, s := range list {
90			w.Uint32(nreloc)
91			nreloc += uint32(len(s.R))
92		}
93	}
94	w.Uint32(nreloc)
95
96	// Symbol Info indexes
97	h.Offsets[goobj2.BlkAuxIdx] = w.Offset()
98	naux := uint32(0)
99	for _, list := range lists {
100		for _, s := range list {
101			w.Uint32(naux)
102			naux += uint32(nAuxSym(s))
103		}
104	}
105	w.Uint32(naux)
106
107	// Data indexes
108	h.Offsets[goobj2.BlkDataIdx] = w.Offset()
109	dataOff := uint32(0)
110	for _, list := range lists {
111		for _, s := range list {
112			w.Uint32(dataOff)
113			dataOff += uint32(len(s.P))
114		}
115	}
116	w.Uint32(dataOff)
117
118	// Relocs
119	h.Offsets[goobj2.BlkReloc] = w.Offset()
120	for _, list := range lists {
121		for _, s := range list {
122			for i := range s.R {
123				w.Reloc(&s.R[i])
124			}
125		}
126	}
127
128	// Aux symbol info
129	h.Offsets[goobj2.BlkAux] = w.Offset()
130	for _, list := range lists {
131		for _, s := range list {
132			w.Aux(s)
133		}
134	}
135
136	// Data
137	h.Offsets[goobj2.BlkData] = w.Offset()
138	for _, list := range lists {
139		for _, s := range list {
140			w.Bytes(s.P)
141		}
142	}
143
144	// Pcdata
145	h.Offsets[goobj2.BlkPcdata] = w.Offset()
146	for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms
147		if s.Func != nil {
148			pc := &s.Func.Pcln
149			w.Bytes(pc.Pcsp.P)
150			w.Bytes(pc.Pcfile.P)
151			w.Bytes(pc.Pcline.P)
152			w.Bytes(pc.Pcinline.P)
153			for i := range pc.Pcdata {
154				w.Bytes(pc.Pcdata[i].P)
155			}
156		}
157	}
158
159	// Fix up block offsets in the header
160	end := start + int64(w.Offset())
161	b.MustSeek(start, 0)
162	h.Write(w.Writer)
163	b.MustSeek(end, 0)
164}
165
166type writer struct {
167	*goobj2.Writer
168	ctxt    *Link
169	pkgpath string   // the package import path (escaped), "" if unknown
170	pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx
171}
172
173// prepare package index list
174func (w *writer) init() {
175	w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1)
176	w.pkglist[0] = "" // dummy invalid package for index 0
177	for pkg, i := range w.ctxt.pkgIdx {
178		w.pkglist[i] = pkg
179	}
180}
181
182func (w *writer) StringTable() {
183	w.AddString("")
184	for _, pkg := range w.ctxt.Imports {
185		w.AddString(pkg)
186	}
187	for _, pkg := range w.pkglist {
188		w.AddString(pkg)
189	}
190	w.ctxt.traverseSyms(traverseAll, func(s *LSym) {
191		if w.pkgpath != "" {
192			s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
193		}
194		w.AddString(s.Name)
195	})
196	w.ctxt.traverseSyms(traverseDefs, func(s *LSym) {
197		if s.Type != objabi.STEXT {
198			return
199		}
200		pc := &s.Func.Pcln
201		for _, f := range pc.File {
202			w.AddString(filepath.ToSlash(f))
203		}
204		for _, call := range pc.InlTree.nodes {
205			f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
206			w.AddString(filepath.ToSlash(f))
207		}
208	})
209	for _, f := range w.ctxt.PosTable.DebugLinesFileTable() {
210		w.AddString(f)
211	}
212}
213
214func (w *writer) Sym(s *LSym) {
215	abi := uint16(s.ABI())
216	if s.Static() {
217		abi = goobj2.SymABIstatic
218	}
219	flag := uint8(0)
220	if s.DuplicateOK() {
221		flag |= goobj2.SymFlagDupok
222	}
223	if s.Local() {
224		flag |= goobj2.SymFlagLocal
225	}
226	if s.MakeTypelink() {
227		flag |= goobj2.SymFlagTypelink
228	}
229	if s.Leaf() {
230		flag |= goobj2.SymFlagLeaf
231	}
232	if s.CFunc() {
233		flag |= goobj2.SymFlagCFunc
234	}
235	if s.ReflectMethod() {
236		flag |= goobj2.SymFlagReflectMethod
237	}
238	if s.TopFrame() {
239		flag |= goobj2.SymFlagTopFrame
240	}
241	if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
242		flag |= goobj2.SymFlagGoType
243	}
244	name := s.Name
245	if strings.HasPrefix(name, "gofile..") {
246		name = filepath.ToSlash(name)
247	}
248	o := goobj2.Sym{
249		Name: name,
250		ABI:  abi,
251		Type: uint8(s.Type),
252		Flag: flag,
253		Siz:  uint32(s.Size),
254	}
255	o.Write(w.Writer)
256}
257
258func makeSymRef(s *LSym) goobj2.SymRef {
259	if s == nil {
260		return goobj2.SymRef{}
261	}
262	if s.PkgIdx == 0 || !s.Indexed() {
263		fmt.Printf("unindexed symbol reference: %v\n", s)
264		panic("unindexed symbol reference")
265	}
266	return goobj2.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
267}
268
269func (w *writer) Reloc(r *Reloc) {
270	o := goobj2.Reloc{
271		Off:  r.Off,
272		Siz:  r.Siz,
273		Type: uint8(r.Type),
274		Add:  r.Add,
275		Sym:  makeSymRef(r.Sym),
276	}
277	o.Write(w.Writer)
278}
279
280func (w *writer) Aux(s *LSym) {
281	if s.Gotype != nil {
282		o := goobj2.Aux{
283			Type: goobj2.AuxGotype,
284			Sym:  makeSymRef(s.Gotype),
285		}
286		o.Write(w.Writer)
287	}
288	if s.Func != nil {
289		o := goobj2.Aux{
290			Type: goobj2.AuxFuncInfo,
291			Sym:  makeSymRef(s.Func.FuncInfoSym),
292		}
293		o.Write(w.Writer)
294
295		for _, d := range s.Func.Pcln.Funcdata {
296			o := goobj2.Aux{
297				Type: goobj2.AuxFuncdata,
298				Sym:  makeSymRef(d),
299			}
300			o.Write(w.Writer)
301		}
302
303		if s.Func.dwarfInfoSym != nil {
304			o := goobj2.Aux{
305				Type: goobj2.AuxDwarfInfo,
306				Sym:  makeSymRef(s.Func.dwarfInfoSym),
307			}
308			o.Write(w.Writer)
309		}
310		if s.Func.dwarfLocSym != nil {
311			o := goobj2.Aux{
312				Type: goobj2.AuxDwarfLoc,
313				Sym:  makeSymRef(s.Func.dwarfLocSym),
314			}
315			o.Write(w.Writer)
316		}
317		if s.Func.dwarfRangesSym != nil {
318			o := goobj2.Aux{
319				Type: goobj2.AuxDwarfRanges,
320				Sym:  makeSymRef(s.Func.dwarfRangesSym),
321			}
322			o.Write(w.Writer)
323		}
324		if s.Func.dwarfDebugLinesSym != nil {
325			o := goobj2.Aux{
326				Type: goobj2.AuxDwarfLines,
327				Sym:  makeSymRef(s.Func.dwarfDebugLinesSym),
328			}
329			o.Write(w.Writer)
330		}
331	}
332}
333
334// return the number of aux symbols s have.
335func nAuxSym(s *LSym) int {
336	n := 0
337	if s.Gotype != nil {
338		n++
339	}
340	if s.Func != nil {
341		// FuncInfo is an aux symbol, each Funcdata is an aux symbol
342		n += 1 + len(s.Func.Pcln.Funcdata)
343		if s.Func.dwarfInfoSym != nil {
344			n++
345		}
346		if s.Func.dwarfLocSym != nil {
347			n++
348		}
349		if s.Func.dwarfRangesSym != nil {
350			n++
351		}
352		if s.Func.dwarfDebugLinesSym != nil {
353			n++
354		}
355	}
356	return n
357}
358
359// generate symbols for FuncInfo.
360func genFuncInfoSyms(ctxt *Link) {
361	infosyms := make([]*LSym, 0, len(ctxt.Text))
362	var pcdataoff uint32
363	var b bytes.Buffer
364	symidx := int32(len(ctxt.defs))
365	for _, s := range ctxt.Text {
366		if s.Func == nil {
367			continue
368		}
369		nosplit := uint8(0)
370		if s.NoSplit() {
371			nosplit = 1
372		}
373		o := goobj2.FuncInfo{
374			NoSplit: nosplit,
375			Args:    uint32(s.Func.Args),
376			Locals:  uint32(s.Func.Locals),
377		}
378		pc := &s.Func.Pcln
379		o.Pcsp = pcdataoff
380		pcdataoff += uint32(len(pc.Pcsp.P))
381		o.Pcfile = pcdataoff
382		pcdataoff += uint32(len(pc.Pcfile.P))
383		o.Pcline = pcdataoff
384		pcdataoff += uint32(len(pc.Pcline.P))
385		o.Pcinline = pcdataoff
386		pcdataoff += uint32(len(pc.Pcinline.P))
387		o.Pcdata = make([]uint32, len(pc.Pcdata))
388		for i, pcd := range pc.Pcdata {
389			o.Pcdata[i] = pcdataoff
390			pcdataoff += uint32(len(pcd.P))
391		}
392		o.PcdataEnd = pcdataoff
393		o.Funcdataoff = make([]uint32, len(pc.Funcdataoff))
394		for i, x := range pc.Funcdataoff {
395			o.Funcdataoff[i] = uint32(x)
396		}
397		o.File = make([]goobj2.SymRef, len(pc.File))
398		for i, f := range pc.File {
399			fsym := ctxt.Lookup(f)
400			o.File[i] = makeSymRef(fsym)
401		}
402		o.InlTree = make([]goobj2.InlTreeNode, len(pc.InlTree.nodes))
403		for i, inl := range pc.InlTree.nodes {
404			f, l := linkgetlineFromPos(ctxt, inl.Pos)
405			fsym := ctxt.Lookup(f)
406			o.InlTree[i] = goobj2.InlTreeNode{
407				Parent:   int32(inl.Parent),
408				File:     makeSymRef(fsym),
409				Line:     l,
410				Func:     makeSymRef(inl.Func),
411				ParentPC: inl.ParentPC,
412			}
413		}
414
415		o.Write(&b)
416		isym := &LSym{
417			Type:   objabi.SDATA, // for now, I don't think it matters
418			PkgIdx: goobj2.PkgIdxSelf,
419			SymIdx: symidx,
420			P:      append([]byte(nil), b.Bytes()...),
421		}
422		isym.Set(AttrIndexed, true)
423		symidx++
424		infosyms = append(infosyms, isym)
425		s.Func.FuncInfoSym = isym
426		b.Reset()
427	}
428	ctxt.defs = append(ctxt.defs, infosyms...)
429}
430