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 base
6
7import (
8	"cmd/internal/cov/covcmd"
9	"cmd/internal/telemetry/counter"
10	"encoding/json"
11	"flag"
12	"fmt"
13	"internal/buildcfg"
14	"internal/platform"
15	"log"
16	"os"
17	"reflect"
18	"runtime"
19	"strings"
20
21	"cmd/internal/obj"
22	"cmd/internal/objabi"
23	"cmd/internal/sys"
24)
25
26func usage() {
27	fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
28	objabi.Flagprint(os.Stderr)
29	Exit(2)
30}
31
32// Flag holds the parsed command-line flags.
33// See ParseFlag for non-zero defaults.
34var Flag CmdFlags
35
36// A CountFlag is a counting integer flag.
37// It accepts -name=value to set the value directly,
38// but it also accepts -name with no =value to increment the count.
39type CountFlag int
40
41// CmdFlags defines the command-line flags (see var Flag).
42// Each struct field is a different flag, by default named for the lower-case of the field name.
43// If the flag name is a single letter, the default flag name is left upper-case.
44// If the flag name is "Lower" followed by a single letter, the default flag name is the lower-case of the last letter.
45//
46// If this default flag name can't be made right, the `flag` struct tag can be used to replace it,
47// but this should be done only in exceptional circumstances: it helps everyone if the flag name
48// is obvious from the field name when the flag is used elsewhere in the compiler sources.
49// The `flag:"-"` struct tag makes a field invisible to the flag logic and should also be used sparingly.
50//
51// Each field must have a `help` struct tag giving the flag help message.
52//
53// The allowed field types are bool, int, string, pointers to those (for values stored elsewhere),
54// CountFlag (for a counting flag), and func(string) (for a flag that uses special code for parsing).
55type CmdFlags struct {
56	// Single letters
57	B CountFlag    "help:\"disable bounds checking\""
58	C CountFlag    "help:\"disable printing of columns in error messages\""
59	D string       "help:\"set relative `path` for local imports\""
60	E CountFlag    "help:\"debug symbol export\""
61	I func(string) "help:\"add `directory` to import search path\""
62	K CountFlag    "help:\"debug missing line numbers\""
63	L CountFlag    "help:\"also show actual source file names in error messages for positions affected by //line directives\""
64	N CountFlag    "help:\"disable optimizations\""
65	S CountFlag    "help:\"print assembly listing\""
66	// V is added by objabi.AddVersionFlag
67	W CountFlag "help:\"debug parse tree after type checking\""
68
69	LowerC int        "help:\"concurrency during compilation (1 means no concurrency)\""
70	LowerD flag.Value "help:\"enable debugging settings; try -d help\""
71	LowerE CountFlag  "help:\"no limit on number of errors reported\""
72	LowerH CountFlag  "help:\"halt on error\""
73	LowerJ CountFlag  "help:\"debug runtime-initialized variables\""
74	LowerL CountFlag  "help:\"disable inlining\""
75	LowerM CountFlag  "help:\"print optimization decisions\""
76	LowerO string     "help:\"write output to `file`\""
77	LowerP *string    "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
78	LowerR CountFlag  "help:\"debug generated wrappers\""
79	LowerT bool       "help:\"enable tracing for debugging the compiler\""
80	LowerW CountFlag  "help:\"debug type checking\""
81	LowerV *bool      "help:\"increase debug verbosity\""
82
83	// Special characters
84	Percent          CountFlag "flag:\"%\" help:\"debug non-static initializers\""
85	CompilingRuntime bool      "flag:\"+\" help:\"compiling runtime\""
86
87	// Longer names
88	AsmHdr             string       "help:\"write assembly header to `file`\""
89	ASan               bool         "help:\"build code compatible with C/C++ address sanitizer\""
90	Bench              string       "help:\"append benchmark times to `file`\""
91	BlockProfile       string       "help:\"write block profile to `file`\""
92	BuildID            string       "help:\"record `id` as the build id in the export metadata\""
93	CPUProfile         string       "help:\"write cpu profile to `file`\""
94	Complete           bool         "help:\"compiling complete package (no C or assembly)\""
95	ClobberDead        bool         "help:\"clobber dead stack slots (for debugging)\""
96	ClobberDeadReg     bool         "help:\"clobber dead registers (for debugging)\""
97	Dwarf              bool         "help:\"generate DWARF symbols\""
98	DwarfBASEntries    *bool        "help:\"use base address selection entries in DWARF\""                        // &Ctxt.UseBASEntries, set below
99	DwarfLocationLists *bool        "help:\"add location lists to DWARF in optimized mode\""                      // &Ctxt.Flag_locationlists, set below
100	Dynlink            *bool        "help:\"support references to Go symbols defined in other shared libraries\"" // &Ctxt.Flag_dynlink, set below
101	EmbedCfg           func(string) "help:\"read go:embed configuration from `file`\""
102	Env                func(string) "help:\"add `definition` of the form key=value to environment\""
103	GenDwarfInl        int          "help:\"generate DWARF inline info records\"" // 0=disabled, 1=funcs, 2=funcs+formals/locals
104	GoVersion          string       "help:\"required version of the runtime\""
105	ImportCfg          func(string) "help:\"read import configuration from `file`\""
106	InstallSuffix      string       "help:\"set pkg directory `suffix`\""
107	JSON               string       "help:\"version,file for JSON compiler/optimizer detail output\""
108	Lang               string       "help:\"Go language version source code expects\""
109	LinkObj            string       "help:\"write linker-specific object to `file`\""
110	LinkShared         *bool        "help:\"generate code that will be linked against Go shared libraries\"" // &Ctxt.Flag_linkshared, set below
111	Live               CountFlag    "help:\"debug liveness analysis\""
112	MSan               bool         "help:\"build code compatible with C/C++ memory sanitizer\""
113	MemProfile         string       "help:\"write memory profile to `file`\""
114	MemProfileRate     int          "help:\"set runtime.MemProfileRate to `rate`\""
115	MutexProfile       string       "help:\"write mutex profile to `file`\""
116	NoLocalImports     bool         "help:\"reject local (relative) imports\""
117	CoverageCfg        func(string) "help:\"read coverage configuration from `file`\""
118	Pack               bool         "help:\"write to file.a instead of file.o\""
119	Race               bool         "help:\"enable race detector\""
120	Shared             *bool        "help:\"generate code that can be linked into a shared library\"" // &Ctxt.Flag_shared, set below
121	SmallFrames        bool         "help:\"reduce the size limit for stack allocated objects\""      // small stacks, to diagnose GC latency; see golang.org/issue/27732
122	Spectre            string       "help:\"enable spectre mitigations in `list` (all, index, ret)\""
123	Std                bool         "help:\"compiling standard library\""
124	SymABIs            string       "help:\"read symbol ABIs from `file`\""
125	TraceProfile       string       "help:\"write an execution trace to `file`\""
126	TrimPath           string       "help:\"remove `prefix` from recorded source file paths\""
127	WB                 bool         "help:\"enable write barrier\"" // TODO: remove
128	PgoProfile         string       "help:\"read profile or pre-process profile from `file`\""
129	ErrorURL           bool         "help:\"print explanatory URL with error message if applicable\""
130
131	// Configuration derived from flags; not a flag itself.
132	Cfg struct {
133		Embed struct { // set by -embedcfg
134			Patterns map[string][]string
135			Files    map[string]string
136		}
137		ImportDirs   []string                 // appended to by -I
138		ImportMap    map[string]string        // set by -importcfg
139		PackageFile  map[string]string        // set by -importcfg; nil means not in use
140		CoverageInfo *covcmd.CoverFixupConfig // set by -coveragecfg
141		SpectreIndex bool                     // set by -spectre=index or -spectre=all
142		// Whether we are adding any sort of code instrumentation, such as
143		// when the race detector is enabled.
144		Instrumenting bool
145	}
146}
147
148func addEnv(s string) {
149	i := strings.Index(s, "=")
150	if i < 0 {
151		log.Fatal("-env argument must be of the form key=value")
152	}
153	os.Setenv(s[:i], s[i+1:])
154}
155
156// ParseFlags parses the command-line flags into Flag.
157func ParseFlags() {
158	Flag.I = addImportDir
159
160	Flag.LowerC = runtime.GOMAXPROCS(0)
161	Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
162	Flag.LowerP = &Ctxt.Pkgpath
163	Flag.LowerV = &Ctxt.Debugvlog
164
165	Flag.Dwarf = buildcfg.GOARCH != "wasm"
166	Flag.DwarfBASEntries = &Ctxt.UseBASEntries
167	Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
168	*Flag.DwarfLocationLists = true
169	Flag.Dynlink = &Ctxt.Flag_dynlink
170	Flag.EmbedCfg = readEmbedCfg
171	Flag.Env = addEnv
172	Flag.GenDwarfInl = 2
173	Flag.ImportCfg = readImportCfg
174	Flag.CoverageCfg = readCoverageCfg
175	Flag.LinkShared = &Ctxt.Flag_linkshared
176	Flag.Shared = &Ctxt.Flag_shared
177	Flag.WB = true
178
179	Debug.ConcurrentOk = true
180	Debug.MaxShapeLen = 500
181	Debug.AlignHot = 1
182	Debug.InlFuncsWithClosures = 1
183	Debug.InlStaticInit = 1
184	Debug.PGOInline = 1
185	Debug.PGODevirtualize = 2
186	Debug.SyncFrames = -1 // disable sync markers by default
187	Debug.ZeroCopy = 1
188	Debug.RangeFuncCheck = 1
189	Debug.MergeLocals = 1
190
191	Debug.Checkptr = -1 // so we can tell whether it is set explicitly
192
193	Flag.Cfg.ImportMap = make(map[string]string)
194
195	objabi.AddVersionFlag() // -V
196	registerFlags()
197	objabi.Flagparse(usage)
198	counter.CountFlags("compile/flag:", *flag.CommandLine)
199
200	if gcd := os.Getenv("GOCOMPILEDEBUG"); gcd != "" {
201		// This will only override the flags set in gcd;
202		// any others set on the command line remain set.
203		Flag.LowerD.Set(gcd)
204	}
205
206	if Debug.Gossahash != "" {
207		hashDebug = NewHashDebug("gossahash", Debug.Gossahash, nil)
208	}
209
210	// Compute whether we're compiling the runtime from the package path. Test
211	// code can also use the flag to set this explicitly.
212	if Flag.Std && objabi.LookupPkgSpecial(Ctxt.Pkgpath).Runtime {
213		Flag.CompilingRuntime = true
214	}
215
216	Ctxt.Std = Flag.Std
217
218	// Three inputs govern loop iteration variable rewriting, hash, experiment, flag.
219	// The loop variable rewriting is:
220	// IF non-empty hash, then hash determines behavior (function+line match) (*)
221	// ELSE IF experiment and flag==0, then experiment (set flag=1)
222	// ELSE flag (note that build sets flag per-package), with behaviors:
223	//  -1 => no change to behavior.
224	//   0 => no change to behavior (unless non-empty hash, see above)
225	//   1 => apply change to likely-iteration-variable-escaping loops
226	//   2 => apply change, log results
227	//   11 => apply change EVERYWHERE, do not log results (for debugging/benchmarking)
228	//   12 => apply change EVERYWHERE, log results (for debugging/benchmarking)
229	//
230	// The expected uses of the these inputs are, in believed most-likely to least likely:
231	//  GOEXPERIMENT=loopvar -- apply change to entire application
232	//  -gcflags=some_package=-d=loopvar=1 -- apply change to some_package (**)
233	//  -gcflags=some_package=-d=loopvar=2 -- apply change to some_package, log it
234	//  GOEXPERIMENT=loopvar -gcflags=some_package=-d=loopvar=-1 -- apply change to all but one package
235	//  GOCOMPILEDEBUG=loopvarhash=... -- search for failure cause
236	//
237	//  (*) For debugging purposes, providing loopvar flag >= 11 will expand the hash-eligible set of loops to all.
238	// (**) Loop semantics, changed or not, follow code from a package when it is inlined; that is, the behavior
239	//      of an application compiled with partially modified loop semantics does not depend on inlining.
240
241	if Debug.LoopVarHash != "" {
242		// This first little bit controls the inputs for debug-hash-matching.
243		mostInlineOnly := true
244		if strings.HasPrefix(Debug.LoopVarHash, "IL") {
245			// When hash-searching on a position that is an inline site, default is to use the
246			// most-inlined position only.  This makes the hash faster, plus there's no point
247			// reporting a problem with all the inlining; there's only one copy of the source.
248			// However, if for some reason you wanted it per-site, you can get this.  (The default
249			// hash-search behavior for compiler debugging is at an inline site.)
250			Debug.LoopVarHash = Debug.LoopVarHash[2:]
251			mostInlineOnly = false
252		}
253		// end of testing trickiness
254		LoopVarHash = NewHashDebug("loopvarhash", Debug.LoopVarHash, nil)
255		if Debug.LoopVar < 11 { // >= 11 means all loops are rewrite-eligible
256			Debug.LoopVar = 1 // 1 means those loops that syntactically escape their dcl vars are eligible.
257		}
258		LoopVarHash.SetInlineSuffixOnly(mostInlineOnly)
259	} else if buildcfg.Experiment.LoopVar && Debug.LoopVar == 0 {
260		Debug.LoopVar = 1
261	}
262
263	if Debug.Fmahash != "" {
264		FmaHash = NewHashDebug("fmahash", Debug.Fmahash, nil)
265	}
266	if Debug.PGOHash != "" {
267		PGOHash = NewHashDebug("pgohash", Debug.PGOHash, nil)
268	}
269	if Debug.MergeLocalsHash != "" {
270		MergeLocalsHash = NewHashDebug("mergelocals", Debug.MergeLocalsHash, nil)
271	}
272
273	if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
274		log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
275	}
276	if Flag.ASan && !platform.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
277		log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
278	}
279	if Flag.Race && !platform.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
280		log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
281	}
282	if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) {
283		log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH)
284	}
285	parseSpectre(Flag.Spectre) // left as string for RecordFlags
286
287	Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
288	Ctxt.Flag_optimize = Flag.N == 0
289	Ctxt.Debugasm = int(Flag.S)
290	Ctxt.Flag_maymorestack = Debug.MayMoreStack
291	Ctxt.Flag_noRefName = Debug.NoRefName != 0
292
293	if flag.NArg() < 1 {
294		usage()
295	}
296
297	if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
298		fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
299		Exit(2)
300	}
301
302	if *Flag.LowerP == "" {
303		*Flag.LowerP = obj.UnlinkablePkg
304	}
305
306	if Flag.LowerO == "" {
307		p := flag.Arg(0)
308		if i := strings.LastIndex(p, "/"); i >= 0 {
309			p = p[i+1:]
310		}
311		if runtime.GOOS == "windows" {
312			if i := strings.LastIndex(p, `\`); i >= 0 {
313				p = p[i+1:]
314			}
315		}
316		if i := strings.LastIndex(p, "."); i >= 0 {
317			p = p[:i]
318		}
319		suffix := ".o"
320		if Flag.Pack {
321			suffix = ".a"
322		}
323		Flag.LowerO = p + suffix
324	}
325	switch {
326	case Flag.Race && Flag.MSan:
327		log.Fatal("cannot use both -race and -msan")
328	case Flag.Race && Flag.ASan:
329		log.Fatal("cannot use both -race and -asan")
330	case Flag.MSan && Flag.ASan:
331		log.Fatal("cannot use both -msan and -asan")
332	}
333	if Flag.Race || Flag.MSan || Flag.ASan {
334		// -race, -msan and -asan imply -d=checkptr for now.
335		if Debug.Checkptr == -1 { // if not set explicitly
336			Debug.Checkptr = 1
337		}
338	}
339
340	if Flag.LowerC < 1 {
341		log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
342	}
343	if !concurrentBackendAllowed() {
344		Flag.LowerC = 1
345	}
346
347	if Flag.CompilingRuntime {
348		// It is not possible to build the runtime with no optimizations,
349		// because the compiler cannot eliminate enough write barriers.
350		Flag.N = 0
351		Ctxt.Flag_optimize = true
352
353		// Runtime can't use -d=checkptr, at least not yet.
354		Debug.Checkptr = 0
355
356		// Fuzzing the runtime isn't interesting either.
357		Debug.Libfuzzer = 0
358	}
359
360	if Debug.Checkptr == -1 { // if not set explicitly
361		Debug.Checkptr = 0
362	}
363
364	// set via a -d flag
365	Ctxt.Debugpcln = Debug.PCTab
366
367	// https://golang.org/issue/67502
368	if buildcfg.GOOS == "plan9" && buildcfg.GOARCH == "386" {
369		Debug.AlignHot = 0
370	}
371}
372
373// registerFlags adds flag registrations for all the fields in Flag.
374// See the comment on type CmdFlags for the rules.
375func registerFlags() {
376	var (
377		boolType      = reflect.TypeOf(bool(false))
378		intType       = reflect.TypeOf(int(0))
379		stringType    = reflect.TypeOf(string(""))
380		ptrBoolType   = reflect.TypeOf(new(bool))
381		ptrIntType    = reflect.TypeOf(new(int))
382		ptrStringType = reflect.TypeOf(new(string))
383		countType     = reflect.TypeOf(CountFlag(0))
384		funcType      = reflect.TypeOf((func(string))(nil))
385	)
386
387	v := reflect.ValueOf(&Flag).Elem()
388	t := v.Type()
389	for i := 0; i < t.NumField(); i++ {
390		f := t.Field(i)
391		if f.Name == "Cfg" {
392			continue
393		}
394
395		var name string
396		if len(f.Name) == 1 {
397			name = f.Name
398		} else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
399			name = string(rune(f.Name[5] + 'a' - 'A'))
400		} else {
401			name = strings.ToLower(f.Name)
402		}
403		if tag := f.Tag.Get("flag"); tag != "" {
404			name = tag
405		}
406
407		help := f.Tag.Get("help")
408		if help == "" {
409			panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
410		}
411
412		if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
413			panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
414		}
415
416		switch f.Type {
417		case boolType:
418			p := v.Field(i).Addr().Interface().(*bool)
419			flag.BoolVar(p, name, *p, help)
420		case intType:
421			p := v.Field(i).Addr().Interface().(*int)
422			flag.IntVar(p, name, *p, help)
423		case stringType:
424			p := v.Field(i).Addr().Interface().(*string)
425			flag.StringVar(p, name, *p, help)
426		case ptrBoolType:
427			p := v.Field(i).Interface().(*bool)
428			flag.BoolVar(p, name, *p, help)
429		case ptrIntType:
430			p := v.Field(i).Interface().(*int)
431			flag.IntVar(p, name, *p, help)
432		case ptrStringType:
433			p := v.Field(i).Interface().(*string)
434			flag.StringVar(p, name, *p, help)
435		case countType:
436			p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
437			objabi.Flagcount(name, help, p)
438		case funcType:
439			f := v.Field(i).Interface().(func(string))
440			objabi.Flagfn1(name, help, f)
441		default:
442			if val, ok := v.Field(i).Interface().(flag.Value); ok {
443				flag.Var(val, name, help)
444			} else {
445				panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
446			}
447		}
448	}
449}
450
451// concurrentFlagOk reports whether the current compiler flags
452// are compatible with concurrent compilation.
453func concurrentFlagOk() bool {
454	// TODO(rsc): Many of these are fine. Remove them.
455	return Flag.Percent == 0 &&
456		Flag.E == 0 &&
457		Flag.K == 0 &&
458		Flag.L == 0 &&
459		Flag.LowerH == 0 &&
460		Flag.LowerJ == 0 &&
461		Flag.LowerM == 0 &&
462		Flag.LowerR == 0
463}
464
465func concurrentBackendAllowed() bool {
466	if !concurrentFlagOk() {
467		return false
468	}
469
470	// Debug.S by itself is ok, because all printing occurs
471	// while writing the object file, and that is non-concurrent.
472	// Adding Debug_vlog, however, causes Debug.S to also print
473	// while flushing the plist, which happens concurrently.
474	if Ctxt.Debugvlog || !Debug.ConcurrentOk || Flag.Live > 0 {
475		return false
476	}
477	// TODO: Test and delete this condition.
478	if buildcfg.Experiment.FieldTrack {
479		return false
480	}
481	// TODO: fix races and enable the following flags
482	if Ctxt.Flag_dynlink || Flag.Race {
483		return false
484	}
485	return true
486}
487
488func addImportDir(dir string) {
489	if dir != "" {
490		Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
491	}
492}
493
494func readImportCfg(file string) {
495	if Flag.Cfg.ImportMap == nil {
496		Flag.Cfg.ImportMap = make(map[string]string)
497	}
498	Flag.Cfg.PackageFile = map[string]string{}
499	data, err := os.ReadFile(file)
500	if err != nil {
501		log.Fatalf("-importcfg: %v", err)
502	}
503
504	for lineNum, line := range strings.Split(string(data), "\n") {
505		lineNum++ // 1-based
506		line = strings.TrimSpace(line)
507		if line == "" || strings.HasPrefix(line, "#") {
508			continue
509		}
510
511		verb, args, found := strings.Cut(line, " ")
512		if found {
513			args = strings.TrimSpace(args)
514		}
515		before, after, hasEq := strings.Cut(args, "=")
516
517		switch verb {
518		default:
519			log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
520		case "importmap":
521			if !hasEq || before == "" || after == "" {
522				log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
523			}
524			Flag.Cfg.ImportMap[before] = after
525		case "packagefile":
526			if !hasEq || before == "" || after == "" {
527				log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
528			}
529			Flag.Cfg.PackageFile[before] = after
530		}
531	}
532}
533
534func readCoverageCfg(file string) {
535	var cfg covcmd.CoverFixupConfig
536	data, err := os.ReadFile(file)
537	if err != nil {
538		log.Fatalf("-coveragecfg: %v", err)
539	}
540	if err := json.Unmarshal(data, &cfg); err != nil {
541		log.Fatalf("error reading -coveragecfg file %q: %v", file, err)
542	}
543	Flag.Cfg.CoverageInfo = &cfg
544}
545
546func readEmbedCfg(file string) {
547	data, err := os.ReadFile(file)
548	if err != nil {
549		log.Fatalf("-embedcfg: %v", err)
550	}
551	if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
552		log.Fatalf("%s: %v", file, err)
553	}
554	if Flag.Cfg.Embed.Patterns == nil {
555		log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
556	}
557	if Flag.Cfg.Embed.Files == nil {
558		log.Fatalf("%s: invalid embedcfg: missing Files", file)
559	}
560}
561
562// parseSpectre parses the spectre configuration from the string s.
563func parseSpectre(s string) {
564	for _, f := range strings.Split(s, ",") {
565		f = strings.TrimSpace(f)
566		switch f {
567		default:
568			log.Fatalf("unknown setting -spectre=%s", f)
569		case "":
570			// nothing
571		case "all":
572			Flag.Cfg.SpectreIndex = true
573			Ctxt.Retpoline = true
574		case "index":
575			Flag.Cfg.SpectreIndex = true
576		case "ret":
577			Ctxt.Retpoline = true
578		}
579	}
580
581	if Flag.Cfg.SpectreIndex {
582		switch buildcfg.GOARCH {
583		case "amd64":
584			// ok
585		default:
586			log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH)
587		}
588	}
589}
590