1// Copyright 2009 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 pkginit
6
7import (
8	"cmd/compile/internal/base"
9	"cmd/compile/internal/ir"
10	"cmd/compile/internal/noder"
11	"cmd/compile/internal/objw"
12	"cmd/compile/internal/staticinit"
13	"cmd/compile/internal/typecheck"
14	"cmd/compile/internal/types"
15	"cmd/internal/obj"
16	"cmd/internal/objabi"
17	"cmd/internal/src"
18)
19
20// MakeTask makes an initialization record for the package, if necessary.
21// See runtime/proc.go:initTask for its layout.
22// The 3 tasks for initialization are:
23//  1. Initialize all of the packages the current package depends on.
24//  2. Initialize all the variables that have initializers.
25//  3. Run any init functions.
26func MakeTask() {
27	var deps []*obj.LSym // initTask records for packages the current package depends on
28	var fns []*obj.LSym  // functions to call for package initialization
29
30	// Find imported packages with init tasks.
31	for _, pkg := range typecheck.Target.Imports {
32		n, ok := pkg.Lookup(".inittask").Def.(*ir.Name)
33		if !ok {
34			continue
35		}
36		if n.Op() != ir.ONAME || n.Class != ir.PEXTERN {
37			base.Fatalf("bad inittask: %v", n)
38		}
39		deps = append(deps, n.Linksym())
40	}
41	if base.Flag.ASan {
42		// Make an initialization function to call runtime.asanregisterglobals to register an
43		// array of instrumented global variables when -asan is enabled. An instrumented global
44		// variable is described by a structure.
45		// See the _asan_global structure declared in src/runtime/asan/asan.go.
46		//
47		// func init {
48		// 		var globals []_asan_global {...}
49		// 		asanregisterglobals(&globals[0], len(globals))
50		// }
51		for _, n := range typecheck.Target.Externs {
52			if canInstrumentGlobal(n) {
53				name := n.Sym().Name
54				InstrumentGlobalsMap[name] = n
55				InstrumentGlobalsSlice = append(InstrumentGlobalsSlice, n)
56			}
57		}
58		ni := len(InstrumentGlobalsMap)
59		if ni != 0 {
60			// Make an init._ function.
61			pos := base.AutogeneratedPos
62			base.Pos = pos
63
64			sym := noder.Renameinit()
65			fnInit := ir.NewFunc(pos, pos, sym, types.NewSignature(nil, nil, nil))
66			typecheck.DeclFunc(fnInit)
67
68			// Get an array of instrumented global variables.
69			globals := instrumentGlobals(fnInit)
70
71			// Call runtime.asanregisterglobals function to poison redzones.
72			// runtime.asanregisterglobals(unsafe.Pointer(&globals[0]), ni)
73			asancall := ir.NewCallExpr(base.Pos, ir.OCALL, typecheck.LookupRuntime("asanregisterglobals"), nil)
74			asancall.Args.Append(typecheck.ConvNop(typecheck.NodAddr(
75				ir.NewIndexExpr(base.Pos, globals, ir.NewInt(base.Pos, 0))), types.Types[types.TUNSAFEPTR]))
76			asancall.Args.Append(typecheck.DefaultLit(ir.NewInt(base.Pos, int64(ni)), types.Types[types.TUINTPTR]))
77
78			fnInit.Body.Append(asancall)
79			typecheck.FinishFuncBody()
80			ir.CurFunc = fnInit
81			typecheck.Stmts(fnInit.Body)
82			ir.CurFunc = nil
83
84			typecheck.Target.Inits = append(typecheck.Target.Inits, fnInit)
85		}
86	}
87
88	// Record user init functions.
89	for _, fn := range typecheck.Target.Inits {
90		if fn.Sym().Name == "init" {
91			// Synthetic init function for initialization of package-scope
92			// variables. We can use staticinit to optimize away static
93			// assignments.
94			s := staticinit.Schedule{
95				Plans: make(map[ir.Node]*staticinit.Plan),
96				Temps: make(map[ir.Node]*ir.Name),
97			}
98			for _, n := range fn.Body {
99				s.StaticInit(n)
100			}
101			fn.Body = s.Out
102			ir.WithFunc(fn, func() {
103				typecheck.Stmts(fn.Body)
104			})
105
106			if len(fn.Body) == 0 {
107				fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
108			}
109		}
110
111		// Skip init functions with empty bodies.
112		if len(fn.Body) == 1 {
113			if stmt := fn.Body[0]; stmt.Op() == ir.OBLOCK && len(stmt.(*ir.BlockStmt).List) == 0 {
114				continue
115			}
116		}
117		fns = append(fns, fn.Nname.Linksym())
118	}
119
120	if len(deps) == 0 && len(fns) == 0 && types.LocalPkg.Path != "main" && types.LocalPkg.Path != "runtime" {
121		return // nothing to initialize
122	}
123
124	// Make an .inittask structure.
125	sym := typecheck.Lookup(".inittask")
126	task := ir.NewNameAt(base.Pos, sym, types.Types[types.TUINT8]) // fake type
127	task.Class = ir.PEXTERN
128	sym.Def = task
129	lsym := task.Linksym()
130	ot := 0
131	ot = objw.Uint32(lsym, ot, 0) // state: not initialized yet
132	ot = objw.Uint32(lsym, ot, uint32(len(fns)))
133	for _, f := range fns {
134		ot = objw.SymPtr(lsym, ot, f, 0)
135	}
136
137	// Add relocations which tell the linker all of the packages
138	// that this package depends on (and thus, all of the packages
139	// that need to be initialized before this one).
140	for _, d := range deps {
141		r := obj.Addrel(lsym)
142		r.Type = objabi.R_INITORDER
143		r.Sym = d
144	}
145	// An initTask has pointers, but none into the Go heap.
146	// It's not quite read only, the state field must be modifiable.
147	objw.Global(lsym, int32(ot), obj.NOPTR)
148}
149