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

297 lines
7.1 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.
GOEXPERIMENT = expList()
}
// Note: must agree with runtime.framepointer_enabled.
var Framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64"
func addexp(s string) {
// Could do general integer parsing here, but the runtime.haveexperiment doesn't 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)
}
var (
runtime: static lock ranking for the runtime (enabled by GOEXPERIMENT) I took some of the infrastructure from Austin's lock logging CR https://go-review.googlesource.com/c/go/+/192704 (with deadlock detection from the logs), and developed a setup to give static lock ranking for runtime locks. Static lock ranking establishes a documented total ordering among locks, and then reports an error if the total order is violated. This can happen if a deadlock happens (by acquiring a sequence of locks in different orders), or if just one side of a possible deadlock happens. Lock ordering deadlocks cannot happen as long as the lock ordering is followed. Along the way, I found a deadlock involving the new timer code, which Ian fixed via https://go-review.googlesource.com/c/go/+/207348, as well as two other potential deadlocks. See the constants at the top of runtime/lockrank.go to show the static lock ranking that I ended up with, along with some comments. This is great documentation of the current intended lock ordering when acquiring multiple locks in the runtime. I also added an array lockPartialOrder[] which shows and enforces the current partial ordering among locks (which is embedded within the total ordering). This is more specific about the dependencies among locks. I don't try to check the ranking within a lock class with multiple locks that can be acquired at the same time (i.e. check the ranking when multiple hchan locks are acquired). Currently, I am doing a lockInit() call to set the lock rank of most locks. Any lock that is not otherwise initialized is assumed to be a leaf lock (a very high rank lock), so that eliminates the need to do anything for a bunch of locks (including all architecture-dependent locks). For two locks, root.lock and notifyList.lock (only in the runtime/sema.go file), it is not as easy to do lock initialization, so instead, I am passing the lock rank with the lock calls. For Windows compilation, I needed to increase the StackGuard size from 896 to 928 because of the new lock-rank checking functions. Checking of the static lock ranking is enabled by setting GOEXPERIMENT=staticlockranking before doing a run. To make sure that the static lock ranking code has no overhead in memory or CPU when not enabled by GOEXPERIMENT, I changed 'go build/install' so that it defines a build tag (with the same name) whenever any experiment has been baked into the toolchain (by checking Expstring()). This allows me to avoid increasing the size of the 'mutex' type when static lock ranking is not enabled. Fixes #38029 Change-Id: I154217ff307c47051f8dae9c2a03b53081acd83a Reviewed-on: https://go-review.googlesource.com/c/go/+/207619 Reviewed-by: Dan Scales <danscales@google.com> Reviewed-by: Keith Randall <khr@golang.org> Run-TryBot: Dan Scales <danscales@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
2019-11-13 17:34:47 -08:00
Fieldtrack_enabled int
Preemptibleloops_enabled int
Staticlockranking_enabled int
)
// Experiment contains flags for GOEXPERIMENTs.
//
// TODO(austin): Move the package-level experiment flags into this.
var Experiment ExpFlags
type ExpFlags struct {
// 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", &Fieldtrack_enabled},
{"preemptibleloops", &Preemptibleloops_enabled},
runtime: static lock ranking for the runtime (enabled by GOEXPERIMENT) I took some of the infrastructure from Austin's lock logging CR https://go-review.googlesource.com/c/go/+/192704 (with deadlock detection from the logs), and developed a setup to give static lock ranking for runtime locks. Static lock ranking establishes a documented total ordering among locks, and then reports an error if the total order is violated. This can happen if a deadlock happens (by acquiring a sequence of locks in different orders), or if just one side of a possible deadlock happens. Lock ordering deadlocks cannot happen as long as the lock ordering is followed. Along the way, I found a deadlock involving the new timer code, which Ian fixed via https://go-review.googlesource.com/c/go/+/207348, as well as two other potential deadlocks. See the constants at the top of runtime/lockrank.go to show the static lock ranking that I ended up with, along with some comments. This is great documentation of the current intended lock ordering when acquiring multiple locks in the runtime. I also added an array lockPartialOrder[] which shows and enforces the current partial ordering among locks (which is embedded within the total ordering). This is more specific about the dependencies among locks. I don't try to check the ranking within a lock class with multiple locks that can be acquired at the same time (i.e. check the ranking when multiple hchan locks are acquired). Currently, I am doing a lockInit() call to set the lock rank of most locks. Any lock that is not otherwise initialized is assumed to be a leaf lock (a very high rank lock), so that eliminates the need to do anything for a bunch of locks (including all architecture-dependent locks). For two locks, root.lock and notifyList.lock (only in the runtime/sema.go file), it is not as easy to do lock initialization, so instead, I am passing the lock rank with the lock calls. For Windows compilation, I needed to increase the StackGuard size from 896 to 928 because of the new lock-rank checking functions. Checking of the static lock ranking is enabled by setting GOEXPERIMENT=staticlockranking before doing a run. To make sure that the static lock ranking code has no overhead in memory or CPU when not enabled by GOEXPERIMENT, I changed 'go build/install' so that it defines a build tag (with the same name) whenever any experiment has been baked into the toolchain (by checking Expstring()). This allows me to avoid increasing the size of the 'mutex' type when static lock ranking is not enabled. Fixes #38029 Change-Id: I154217ff307c47051f8dae9c2a03b53081acd83a Reviewed-on: https://go-review.googlesource.com/c/go/+/207619 Reviewed-by: Dan Scales <danscales@google.com> Reviewed-by: Keith Randall <khr@golang.org> Run-TryBot: Dan Scales <danscales@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
2019-11-13 17:34:47 -08:00
{"staticlockranking", &Staticlockranking_enabled},
{"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
}