1  
     2  
     3  
     4  
     5  package base
     6  
     7  import (
     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  
    26  func usage() {
    27  	fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
    28  	objabi.Flagprint(os.Stderr)
    29  	Exit(2)
    30  }
    31  
    32  
    33  
    34  var Flag CmdFlags
    35  
    36  
    37  
    38  
    39  type CountFlag int
    40  
    41  
    42  
    43  
    44  
    45  
    46  
    47  
    48  
    49  
    50  
    51  
    52  
    53  
    54  
    55  type CmdFlags struct {
    56  	
    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  	
    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`\"" 
    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  	
    84  	Percent          CountFlag "flag:\"%\" help:\"debug non-static initializers\""
    85  	CompilingRuntime bool      "flag:\"+\" help:\"compiling runtime\""
    86  
    87  	
    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\""                        
    99  	DwarfLocationLists *bool        "help:\"add location lists to DWARF in optimized mode\""                      
   100  	Dynlink            *bool        "help:\"support references to Go symbols defined in other shared libraries\"" 
   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\"" 
   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\"" 
   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\"" 
   121  	SmallFrames        bool         "help:\"reduce the size limit for stack allocated objects\""      
   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\"" 
   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  	
   132  	Cfg struct {
   133  		Embed struct { 
   134  			Patterns map[string][]string
   135  			Files    map[string]string
   136  		}
   137  		ImportDirs   []string                 
   138  		ImportMap    map[string]string        
   139  		PackageFile  map[string]string        
   140  		CoverageInfo *covcmd.CoverFixupConfig 
   141  		SpectreIndex bool                     
   142  		
   143  		
   144  		Instrumenting bool
   145  	}
   146  }
   147  
   148  func 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  
   157  func 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            
   187  	Debug.VariableMakeThreshold = 32 
   188  	Debug.ZeroCopy = 1
   189  	Debug.RangeFuncCheck = 1
   190  	Debug.MergeLocals = 1
   191  
   192  	Debug.Checkptr = -1 
   193  
   194  	Flag.Cfg.ImportMap = make(map[string]string)
   195  
   196  	objabi.AddVersionFlag() 
   197  	registerFlags()
   198  	objabi.Flagparse(usage)
   199  	counter.CountFlags("compile/flag:", *flag.CommandLine)
   200  
   201  	if gcd := os.Getenv("GOCOMPILEDEBUG"); gcd != "" {
   202  		
   203  		
   204  		Flag.LowerD.Set(gcd)
   205  	}
   206  
   207  	if Debug.Gossahash != "" {
   208  		hashDebug = NewHashDebug("gossahash", Debug.Gossahash, nil)
   209  	}
   210  	obj.SetFIPSDebugHash(Debug.FIPSHash)
   211  
   212  	
   213  	
   214  	if Flag.Std && objabi.LookupPkgSpecial(Ctxt.Pkgpath).Runtime {
   215  		Flag.CompilingRuntime = true
   216  	}
   217  
   218  	Ctxt.Std = Flag.Std
   219  
   220  	
   221  	
   222  	
   223  	
   224  	
   225  	
   226  	
   227  	
   228  	
   229  	
   230  	
   231  	
   232  	
   233  	
   234  	
   235  	
   236  	
   237  	
   238  	
   239  	
   240  	
   241  	
   242  
   243  	if Debug.LoopVarHash != "" {
   244  		
   245  		mostInlineOnly := true
   246  		if strings.HasPrefix(Debug.LoopVarHash, "IL") {
   247  			
   248  			
   249  			
   250  			
   251  			
   252  			Debug.LoopVarHash = Debug.LoopVarHash[2:]
   253  			mostInlineOnly = false
   254  		}
   255  		
   256  		LoopVarHash = NewHashDebug("loopvarhash", Debug.LoopVarHash, nil)
   257  		if Debug.LoopVar < 11 { 
   258  			Debug.LoopVar = 1 
   259  		}
   260  		LoopVarHash.SetInlineSuffixOnly(mostInlineOnly)
   261  	} else if buildcfg.Experiment.LoopVar && Debug.LoopVar == 0 {
   262  		Debug.LoopVar = 1
   263  	}
   264  
   265  	if Debug.Fmahash != "" {
   266  		FmaHash = NewHashDebug("fmahash", Debug.Fmahash, nil)
   267  	}
   268  	if Debug.PGOHash != "" {
   269  		PGOHash = NewHashDebug("pgohash", Debug.PGOHash, nil)
   270  	}
   271  	if Debug.LiteralAllocHash != "" {
   272  		LiteralAllocHash = NewHashDebug("literalalloc", Debug.LiteralAllocHash, nil)
   273  	}
   274  
   275  	if Debug.MergeLocalsHash != "" {
   276  		MergeLocalsHash = NewHashDebug("mergelocals", Debug.MergeLocalsHash, nil)
   277  	}
   278  	if Debug.VariableMakeHash != "" {
   279  		VariableMakeHash = NewHashDebug("variablemake", Debug.VariableMakeHash, nil)
   280  	}
   281  
   282  	if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   283  		log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
   284  	}
   285  	if Flag.ASan && !platform.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   286  		log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
   287  	}
   288  	if Flag.Race && !platform.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   289  		log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
   290  	}
   291  	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) {
   292  		log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH)
   293  	}
   294  	parseSpectre(Flag.Spectre) 
   295  
   296  	Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
   297  	Ctxt.Flag_optimize = Flag.N == 0
   298  	Ctxt.Debugasm = int(Flag.S)
   299  	Ctxt.Flag_maymorestack = Debug.MayMoreStack
   300  	Ctxt.Flag_noRefName = Debug.NoRefName != 0
   301  
   302  	if flag.NArg() < 1 {
   303  		usage()
   304  	}
   305  
   306  	if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
   307  		fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
   308  		Exit(2)
   309  	}
   310  
   311  	if *Flag.LowerP == "" {
   312  		*Flag.LowerP = obj.UnlinkablePkg
   313  	}
   314  
   315  	if Flag.LowerO == "" {
   316  		p := flag.Arg(0)
   317  		if i := strings.LastIndex(p, "/"); i >= 0 {
   318  			p = p[i+1:]
   319  		}
   320  		if runtime.GOOS == "windows" {
   321  			if i := strings.LastIndex(p, `\`); i >= 0 {
   322  				p = p[i+1:]
   323  			}
   324  		}
   325  		if i := strings.LastIndex(p, "."); i >= 0 {
   326  			p = p[:i]
   327  		}
   328  		suffix := ".o"
   329  		if Flag.Pack {
   330  			suffix = ".a"
   331  		}
   332  		Flag.LowerO = p + suffix
   333  	}
   334  	switch {
   335  	case Flag.Race && Flag.MSan:
   336  		log.Fatal("cannot use both -race and -msan")
   337  	case Flag.Race && Flag.ASan:
   338  		log.Fatal("cannot use both -race and -asan")
   339  	case Flag.MSan && Flag.ASan:
   340  		log.Fatal("cannot use both -msan and -asan")
   341  	}
   342  	if Flag.Race || Flag.MSan || Flag.ASan {
   343  		
   344  		if Debug.Checkptr == -1 { 
   345  			Debug.Checkptr = 1
   346  		}
   347  	}
   348  
   349  	if Flag.LowerC < 1 {
   350  		log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
   351  	}
   352  	if !concurrentBackendAllowed() {
   353  		Flag.LowerC = 1
   354  	}
   355  
   356  	if Flag.CompilingRuntime {
   357  		
   358  		
   359  		Flag.N = 0
   360  		Ctxt.Flag_optimize = true
   361  
   362  		
   363  		Debug.Checkptr = 0
   364  
   365  		
   366  		Debug.Libfuzzer = 0
   367  	}
   368  
   369  	if Debug.Checkptr == -1 { 
   370  		Debug.Checkptr = 0
   371  	}
   372  
   373  	
   374  	Ctxt.Debugpcln = Debug.PCTab
   375  
   376  	
   377  	if buildcfg.GOOS == "plan9" && buildcfg.GOARCH == "386" {
   378  		Debug.AlignHot = 0
   379  	}
   380  }
   381  
   382  
   383  
   384  func registerFlags() {
   385  	var (
   386  		boolType      = reflect.TypeOf(bool(false))
   387  		intType       = reflect.TypeOf(int(0))
   388  		stringType    = reflect.TypeOf(string(""))
   389  		ptrBoolType   = reflect.TypeOf(new(bool))
   390  		ptrIntType    = reflect.TypeOf(new(int))
   391  		ptrStringType = reflect.TypeOf(new(string))
   392  		countType     = reflect.TypeOf(CountFlag(0))
   393  		funcType      = reflect.TypeOf((func(string))(nil))
   394  	)
   395  
   396  	v := reflect.ValueOf(&Flag).Elem()
   397  	t := v.Type()
   398  	for i := 0; i < t.NumField(); i++ {
   399  		f := t.Field(i)
   400  		if f.Name == "Cfg" {
   401  			continue
   402  		}
   403  
   404  		var name string
   405  		if len(f.Name) == 1 {
   406  			name = f.Name
   407  		} else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
   408  			name = string(rune(f.Name[5] + 'a' - 'A'))
   409  		} else {
   410  			name = strings.ToLower(f.Name)
   411  		}
   412  		if tag := f.Tag.Get("flag"); tag != "" {
   413  			name = tag
   414  		}
   415  
   416  		help := f.Tag.Get("help")
   417  		if help == "" {
   418  			panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
   419  		}
   420  
   421  		if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
   422  			panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
   423  		}
   424  
   425  		switch f.Type {
   426  		case boolType:
   427  			p := v.Field(i).Addr().Interface().(*bool)
   428  			flag.BoolVar(p, name, *p, help)
   429  		case intType:
   430  			p := v.Field(i).Addr().Interface().(*int)
   431  			flag.IntVar(p, name, *p, help)
   432  		case stringType:
   433  			p := v.Field(i).Addr().Interface().(*string)
   434  			flag.StringVar(p, name, *p, help)
   435  		case ptrBoolType:
   436  			p := v.Field(i).Interface().(*bool)
   437  			flag.BoolVar(p, name, *p, help)
   438  		case ptrIntType:
   439  			p := v.Field(i).Interface().(*int)
   440  			flag.IntVar(p, name, *p, help)
   441  		case ptrStringType:
   442  			p := v.Field(i).Interface().(*string)
   443  			flag.StringVar(p, name, *p, help)
   444  		case countType:
   445  			p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
   446  			objabi.Flagcount(name, help, p)
   447  		case funcType:
   448  			f := v.Field(i).Interface().(func(string))
   449  			objabi.Flagfn1(name, help, f)
   450  		default:
   451  			if val, ok := v.Field(i).Interface().(flag.Value); ok {
   452  				flag.Var(val, name, help)
   453  			} else {
   454  				panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
   455  			}
   456  		}
   457  	}
   458  }
   459  
   460  
   461  
   462  func concurrentFlagOk() bool {
   463  	
   464  	return Flag.Percent == 0 &&
   465  		Flag.E == 0 &&
   466  		Flag.K == 0 &&
   467  		Flag.L == 0 &&
   468  		Flag.LowerH == 0 &&
   469  		Flag.LowerJ == 0 &&
   470  		Flag.LowerM == 0 &&
   471  		Flag.LowerR == 0
   472  }
   473  
   474  func concurrentBackendAllowed() bool {
   475  	if !concurrentFlagOk() {
   476  		return false
   477  	}
   478  
   479  	
   480  	
   481  	
   482  	
   483  	if Ctxt.Debugvlog || !Debug.ConcurrentOk || Flag.Live > 0 {
   484  		return false
   485  	}
   486  	
   487  	if buildcfg.Experiment.FieldTrack {
   488  		return false
   489  	}
   490  	
   491  	if Ctxt.Flag_dynlink || Flag.Race {
   492  		return false
   493  	}
   494  	return true
   495  }
   496  
   497  func addImportDir(dir string) {
   498  	if dir != "" {
   499  		Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
   500  	}
   501  }
   502  
   503  func readImportCfg(file string) {
   504  	if Flag.Cfg.ImportMap == nil {
   505  		Flag.Cfg.ImportMap = make(map[string]string)
   506  	}
   507  	Flag.Cfg.PackageFile = map[string]string{}
   508  	data, err := os.ReadFile(file)
   509  	if err != nil {
   510  		log.Fatalf("-importcfg: %v", err)
   511  	}
   512  
   513  	for lineNum, line := range strings.Split(string(data), "\n") {
   514  		lineNum++ 
   515  		line = strings.TrimSpace(line)
   516  		if line == "" || strings.HasPrefix(line, "#") {
   517  			continue
   518  		}
   519  
   520  		verb, args, found := strings.Cut(line, " ")
   521  		if found {
   522  			args = strings.TrimSpace(args)
   523  		}
   524  		before, after, hasEq := strings.Cut(args, "=")
   525  
   526  		switch verb {
   527  		default:
   528  			log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
   529  		case "importmap":
   530  			if !hasEq || before == "" || after == "" {
   531  				log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
   532  			}
   533  			Flag.Cfg.ImportMap[before] = after
   534  		case "packagefile":
   535  			if !hasEq || before == "" || after == "" {
   536  				log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
   537  			}
   538  			Flag.Cfg.PackageFile[before] = after
   539  		}
   540  	}
   541  }
   542  
   543  func readCoverageCfg(file string) {
   544  	var cfg covcmd.CoverFixupConfig
   545  	data, err := os.ReadFile(file)
   546  	if err != nil {
   547  		log.Fatalf("-coveragecfg: %v", err)
   548  	}
   549  	if err := json.Unmarshal(data, &cfg); err != nil {
   550  		log.Fatalf("error reading -coveragecfg file %q: %v", file, err)
   551  	}
   552  	Flag.Cfg.CoverageInfo = &cfg
   553  }
   554  
   555  func readEmbedCfg(file string) {
   556  	data, err := os.ReadFile(file)
   557  	if err != nil {
   558  		log.Fatalf("-embedcfg: %v", err)
   559  	}
   560  	if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
   561  		log.Fatalf("%s: %v", file, err)
   562  	}
   563  	if Flag.Cfg.Embed.Patterns == nil {
   564  		log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
   565  	}
   566  	if Flag.Cfg.Embed.Files == nil {
   567  		log.Fatalf("%s: invalid embedcfg: missing Files", file)
   568  	}
   569  }
   570  
   571  
   572  func parseSpectre(s string) {
   573  	for _, f := range strings.Split(s, ",") {
   574  		f = strings.TrimSpace(f)
   575  		switch f {
   576  		default:
   577  			log.Fatalf("unknown setting -spectre=%s", f)
   578  		case "":
   579  			
   580  		case "all":
   581  			Flag.Cfg.SpectreIndex = true
   582  			Ctxt.Retpoline = true
   583  		case "index":
   584  			Flag.Cfg.SpectreIndex = true
   585  		case "ret":
   586  			Ctxt.Retpoline = true
   587  		}
   588  	}
   589  
   590  	if Flag.Cfg.SpectreIndex {
   591  		switch buildcfg.GOARCH {
   592  		case "amd64":
   593  			
   594  		default:
   595  			log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH)
   596  		}
   597  	}
   598  }
   599  
View as plain text