go/src/cmd/internal/objabi/util.go

300 lines
7.2 KiB
Go
Raw Normal View History

// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package objabi
import (
"fmt"
"log"
"os"
"strings"
)
func envOr(key, value string) string {
if x := os.Getenv(key); x != "" {
return x
}
return value
}
var (
cmd/link: set runtime.GOROOT default during link Suppose you build the Go toolchain in directory A, move the whole thing to directory B, and then use it from B to build a new program hello.exe, and then run hello.exe, and hello.exe crashes with a stack trace into the standard library. Long ago, you'd have seen hello.exe print file names in the A directory tree, even though the files had moved to the B directory tree. About two years ago we changed the compiler to write down these files with the name "$GOROOT" (that literal string) instead of A, so that the final link from B could replace "$GOROOT" with B, so that hello.exe's crash would show the correct source file paths in the stack trace. (golang.org/cl/18200) Now suppose that you do the same thing but hello.exe doesn't crash: it prints fmt.Println(runtime.GOROOT()). And you run hello.exe after clearing $GOROOT from the environment. Long ago, you'd have seen hello.exe print A instead of B. Before this CL, you'd still see hello.exe print A instead of B. This case is the one instance where a moved toolchain still divulges its origin. Not anymore. After this CL, hello.exe will print B, because the linker sets runtime/internal/sys.DefaultGoroot with the effective GOROOT from link time. This makes the default result of runtime.GOROOT once again match the file names recorded in the binary, after two years of divergence. With that cleared up, we can reintroduce GOROOT into the link action ID and also reenable TestExecutableGOROOT/RelocatedExe. When $GOROOT_FINAL is set during link, it is used in preference to $GOROOT, as always, but it was easier to explain the behavior above without introducing that complication. Fixes #22155. Fixes #20284. Fixes #22475. Change-Id: Ifdaeb77fd4678fdb337cf59ee25b2cd873ec1016 Reviewed-on: https://go-review.googlesource.com/86835 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-01-08 11:59:29 -05:00
defaultGOROOT string // set by linker
GOROOT = envOr("GOROOT", defaultGOROOT)
GOARCH = envOr("GOARCH", defaultGOARCH)
GOOS = envOr("GOOS", defaultGOOS)
GO386 = envOr("GO386", defaultGO386)
GOARM = goarm()
GOMIPS = gomips()
GOMIPS64 = gomips64()
GOPPC64 = goppc64()
GOWASM = gowasm()
GO_LDSO = defaultGO_LDSO
Version = version
// GOEXPERIMENT is a comma-separated list of enabled
// experiments. This is derived from the GOEXPERIMENT
// environment variable if set, or the value of GOEXPERIMENT
// when make.bash was run if not.
GOEXPERIMENT string // Set by package init
)
const (
ElfRelocOffset = 256
MachoRelocOffset = 2048 // reserve enough space for ELF relocations
)
func goarm() int {
def := defaultGOARM
if GOOS == "android" && GOARCH == "arm" {
// Android arm devices always support GOARM=7.
def = "7"
}
switch v := envOr("GOARM", def); v {
case "5":
return 5
case "6":
return 6
case "7":
return 7
}
// Fail here, rather than validate at multiple call sites.
log.Fatalf("Invalid GOARM value. Must be 5, 6, or 7.")
panic("unreachable")
}
func gomips() string {
switch v := envOr("GOMIPS", defaultGOMIPS); v {
case "hardfloat", "softfloat":
return v
}
log.Fatalf("Invalid GOMIPS value. Must be hardfloat or softfloat.")
panic("unreachable")
}
func gomips64() string {
switch v := envOr("GOMIPS64", defaultGOMIPS64); v {
case "hardfloat", "softfloat":
return v
}
log.Fatalf("Invalid GOMIPS64 value. Must be hardfloat or softfloat.")
panic("unreachable")
}
func goppc64() int {
switch v := envOr("GOPPC64", defaultGOPPC64); v {
case "power8":
return 8
case "power9":
return 9
}
log.Fatalf("Invalid GOPPC64 value. Must be power8 or power9.")
panic("unreachable")
}
type gowasmFeatures struct {
SignExt bool
SatConv bool
}
func (f gowasmFeatures) String() string {
var flags []string
if f.SatConv {
flags = append(flags, "satconv")
}
if f.SignExt {
flags = append(flags, "signext")
}
return strings.Join(flags, ",")
}
func gowasm() (f gowasmFeatures) {
for _, opt := range strings.Split(envOr("GOWASM", ""), ",") {
switch opt {
case "satconv":
f.SatConv = true
case "signext":
f.SignExt = true
case "":
// ignore
default:
log.Fatalf("Invalid GOWASM value. No such feature: " + opt)
}
}
return
}
func Getgoextlinkenabled() string {
return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
}
func init() {
// Capture "default" experiments.
defaultExpstring = Expstring()
goexperiment := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)
cmd/dist: build bootstrap without GOEXPERIMENT Currently, dist attempts to build the bootstrap with the GOEXPERIMENT set in the environment. However, the logic is incomplete and notably requires a hack to enable the appropriate build tags for GOEXPERIMENT=regabi. Without this hack, the build becomes skewed between a compiler that uses regabi and a runtime that doesn't when building toolchain2. We could try to improve the GOEXPERIMENT processing in cmd/dist, but it will always chase cmd/internal/objabi and it's quite difficult to share the logic with objabi because of the constraints on building cmd/dist. Instead, we switch to building go_bootstrap without any GOEXPERIMENT and only start using GOEXPERIMENT once we have a working, modern cmd/go (which has all the GOEXPERIMENT logic in it). We also build toolchain1 without any GOEXPERIMENT set, in case the bootstrap toolchain is recent enough to understand build-time GOEXPERIMENT settings. As part of this, we make GOEXPERIMENT=none mean "no experiments". This is necessary since, now that we support setting GOEXPERIMENT at build time, we need an explicit way to say "ignore all baked-in experiments". For #40724. Change-Id: I115399579b766a7a8b2f352f7e5efea5305666cd Reviewed-on: https://go-review.googlesource.com/c/go/+/302050 Trust: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Cherry Zhang <cherryyz@google.com>
2021-03-15 15:53:21 -04:00
// GOEXPERIMENT=none overrides all experiments enabled at dist time.
if goexperiment != "none" {
for _, f := range strings.Split(goexperiment, ",") {
if f != "" {
addexp(f)
}
}
}
// regabi is only supported on amd64.
if GOARCH != "amd64" {
Experiment.regabi = false
Experiment.RegabiWrappers = false
Experiment.RegabiG = false
Experiment.RegabiReflect = false
Experiment.RegabiDefer = false
Experiment.RegabiArgs = false
}
// Setting regabi sets working sub-experiments.
if Experiment.regabi {
Experiment.RegabiWrappers = true
Experiment.RegabiG = true
Experiment.RegabiReflect = true
// Not ready yet:
//Experiment.RegabiDefer = true
//Experiment.RegabiArgs = true
}
// Check regabi dependencies.
if Experiment.RegabiG && !Experiment.RegabiWrappers {
panic("GOEXPERIMENT regabig requires regabiwrappers")
}
if Experiment.RegabiArgs && !(Experiment.RegabiWrappers && Experiment.RegabiReflect && Experiment.RegabiDefer) {
panic("GOEXPERIMENT regabiargs requires regabiwrappers,regabireflect,regabidefer")
}
// Set GOEXPERIMENT to the parsed and canonicalized set of experiments.
// This format must be parseable by runtime.haveexperiment.
GOEXPERIMENT = expList()
}
// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"
func addexp(s string) {
// We could do general integer parsing here, but there's no need yet.
v, vb := 1, true
name := s
if len(name) > 2 && name[:2] == "no" {
v, vb = 0, false
name = name[2:]
}
for i := 0; i < len(exper); i++ {
if exper[i].name == name {
switch val := exper[i].val.(type) {
case *int:
*val = v
case *bool:
*val = vb
default:
panic("bad GOEXPERIMENT type for " + s)
}
return
}
}
fmt.Printf("unknown experiment %s\n", s)
os.Exit(2)
}
// Experiment contains flags for GOEXPERIMENTs.
var Experiment = ExpFlags{}
type ExpFlags struct {
FieldTrack bool
PreemptibleLoops bool
StaticLockRanking bool
// regabi is split into several sub-experiments that can be
// enabled individually. GOEXPERIMENT=regabi implies the
// subset that are currently "working". Not all combinations work.
regabi bool
// RegabiWrappers enables ABI wrappers for calling between
// ABI0 and ABIInternal functions. Without this, the ABIs are
// assumed to be identical so cross-ABI calls are direct.
RegabiWrappers bool
// RegabiG enables dedicated G and zero registers in
// ABIInternal.
//
// Requires wrappers because it makes the ABIs incompatible.
RegabiG bool
// RegabiReflect enables the register-passing paths in
// reflection calls. This is also gated by intArgRegs in
// reflect and runtime (which are disabled by default) so it
// can be used in targeted tests.
RegabiReflect bool
// RegabiDefer enables desugaring defer and go calls
// into argument-less closures.
RegabiDefer bool
// RegabiArgs enables register arguments/results in all
// compiled Go functions.
//
// Requires wrappers, reflect, defer.
RegabiArgs bool
}
// Toolchain experiments.
// These are controlled by the GOEXPERIMENT environment
// variable recorded when the toolchain is built.
var exper = []struct {
name string
val interface{} // Must be *int or *bool
}{
{"fieldtrack", &Experiment.FieldTrack},
{"preemptibleloops", &Experiment.PreemptibleLoops},
{"staticlockranking", &Experiment.StaticLockRanking},
{"regabi", &Experiment.regabi},
{"regabiwrappers", &Experiment.RegabiWrappers},
{"regabig", &Experiment.RegabiG},
{"regabireflect", &Experiment.RegabiReflect},
{"regabidefer", &Experiment.RegabiDefer},
{"regabiargs", &Experiment.RegabiArgs},
}
var defaultExpstring string
// expList returns the list of enabled GOEXPERIMENTS as a
// commas-separated list.
func expList() string {
buf := ""
for i := range exper {
switch val := exper[i].val.(type) {
case *int:
if *val != 0 {
buf += "," + exper[i].name
}
case *bool:
if *val {
buf += "," + exper[i].name
}
}
}
if len(buf) == 0 {
return ""
}
return buf[1:]
}
// Expstring returns the GOEXPERIMENT string that should appear in Go
// version signatures. This always starts with "X:".
func Expstring() string {
list := expList()
if list == "" {
return "X:none"
}
return "X:" + list
}