diff --git a/doc/go1.15.html b/doc/go1.15.html index af0b3ba6ac1..79b18a37206 100644 --- a/doc/go1.15.html +++ b/doc/go1.15.html @@ -33,6 +33,12 @@ TODO

Darwin

+

+ As announced in the Go 1.14 release notes, + Go 1.15 now requires macOS 10.12 Sierra or later; + support for previous versions has been discontinued. +

+

As announced in the Go 1.14 release notes, Go 1.15 drops support for 32-bit binaries on macOS, iOS, @@ -116,6 +122,73 @@ TODO GODEBUG=modcacheunzipinplace=1.

+

Vet

+ +

New warning for string(x)

+ +

+ The vet tool now warns about conversions of the + form string(x) where x has an integer type + other than rune or byte. + Experience with Go has shown that many conversions of this form + erroneously assume that string(x) evaluates to the + string representation of the integer x. + It actually evaluates to a string containing the UTF-8 encoding of + the value of x. + For example, string(9786) does not evaluate to the + string "9786"; it evaluates to the + string "\xe2\x98\xba", or "☺". +

+ +

+ Code that is using string(x) correctly can be rewritten + to string(rune(x)). + Or, in some cases, calling utf8.EncodeRune(buf, x) with + a suitable byte slice buf may be the right solution. + Other code should most likely use strconv.Itoa + or fmt.Sprint. +

+ +

+ This new vet check is enabled by default when using go test. +

+ +

+ We are considering prohibiting the conversion in a future release of Go. + That is, the language would change to only + permit string(x) for integer x when the + type of x is rune or byte. + Such a language change would not be backward compatible. + We are using this vet check as a first trial step toward changing + the language. +

+ +

New warning for impossible interface conversions

+ +

+ The vet tool now warns about type assertions from one interface type + to another interface type when the type assertion will always fail. + This will happen if both interface types implement a method with the + same name but with a different type signature. +

+ +

+ There is no reason to write a type assertion that always fails, so + any code that triggers this vet check should be rewritten. +

+ +

+ This new vet check is enabled by default when using go test. +

+ +

+ We are considering prohibiting impossible interface type assertions + in a future release of Go. + Such a language change would not be backward compatible. + We are using this vet check as a first trial step toward changing + the language. +

+

Runtime

@@ -155,22 +228,6 @@ TODO TODO

-
testing
-
-

- The testing.T type now has a Deadline method - that reports the time at which the test binary will have exceeded its - timeout. -

-

- A TestMain function is no longer required to call - os.Exit. If a TestMain function returns, - the test binary will call os.Exit with the value returned - by m.Run. -

-
-
-

Minor changes to the library

@@ -375,6 +432,20 @@ TODO

testing
+

+ The testing.T type now has a + Deadline method + that reports the time at which the test binary will have exceeded its + timeout. +

+ +

+ A TestMain function is no longer required to call + os.Exit. If a TestMain function returns, + the test binary will call os.Exit with the value returned + by m.Run. +

+

The new methods T.TempDir and diff --git a/doc/install-source.html b/doc/install-source.html index 8f0d3a9d14c..b5b422ea111 100644 --- a/doc/install-source.html +++ b/doc/install-source.html @@ -501,7 +501,7 @@ These default to the values of $GOHOSTOS and

Choices for $GOOS are -android, darwin (macOS 10.11 and above and iOS), +android, darwin (macOS/iOS), dragonfly, freebsd, illumos, js, linux, netbsd, openbsd, plan9, solaris and windows. diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 6ccd0b8d943..74654c86bc0 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -271,7 +271,7 @@ func compile(fn *Node) { } } - if compilenow() { + if compilenow(fn) { compileSSA(fn, 0) } else { compilequeue = append(compilequeue, fn) @@ -282,10 +282,31 @@ func compile(fn *Node) { // If functions are not compiled immediately, // they are enqueued in compilequeue, // which is drained by compileFunctions. -func compilenow() bool { +func compilenow(fn *Node) bool { + // Issue 38068: if this function is a method AND an inline + // candidate AND was not inlined (yet), put it onto the compile + // queue instead of compiling it immediately. This is in case we + // wind up inlining it into a method wrapper that is generated by + // compiling a function later on in the xtop list. + if fn.IsMethod() && isInlinableButNotInlined(fn) { + return false + } return nBackendWorkers == 1 && Debug_compilelater == 0 } +// isInlinableButNotInlined returns true if 'fn' was marked as an +// inline candidate but then never inlined (presumably because we +// found no call sites). +func isInlinableButNotInlined(fn *Node) bool { + if fn.Func.Nname.Func.Inl == nil { + return false + } + if fn.Sym == nil { + return true + } + return !fn.Sym.Linksym().WasInlined() +} + const maxStackSize = 1 << 30 // compileSSA builds an SSA backend function, diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index d22ee1d361d..9e2b4f33b86 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -31,7 +31,6 @@ var ( goos string goarm string go386 string - goamd64 string gomips string gomips64 string goppc64 string @@ -152,12 +151,6 @@ func xinit() { } go386 = b - b = os.Getenv("GOAMD64") - if b == "" { - b = "alignedjumps" - } - goamd64 = b - b = os.Getenv("GOMIPS") if b == "" { b = "hardfloat" @@ -230,7 +223,6 @@ func xinit() { // For tools being invoked but also for os.ExpandEnv. os.Setenv("GO386", go386) - os.Setenv("GOAMD64", goamd64) os.Setenv("GOARCH", goarch) os.Setenv("GOARM", goarm) os.Setenv("GOHOSTARCH", gohostarch) @@ -1171,9 +1163,6 @@ func cmdenv() { if goarch == "386" { xprintf(format, "GO386", go386) } - if goarch == "amd64" { - xprintf(format, "GOAMD64", goamd64) - } if goarch == "mips" || goarch == "mipsle" { xprintf(format, "GOMIPS", gomips) } diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go index f11933c9258..27449515976 100644 --- a/src/cmd/dist/buildruntime.go +++ b/src/cmd/dist/buildruntime.go @@ -42,7 +42,6 @@ func mkzversion(dir, file string) { // // const defaultGOROOT = // const defaultGO386 = -// const defaultGOAMD64 = // const defaultGOARM = // const defaultGOMIPS = // const defaultGOMIPS64 = @@ -72,7 +71,6 @@ func mkzbootstrap(file string) { fmt.Fprintf(&buf, "import \"runtime\"\n") fmt.Fprintln(&buf) fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386) - fmt.Fprintf(&buf, "const defaultGOAMD64 = `%s`\n", goamd64) fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm) fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips) fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64) diff --git a/src/cmd/go.mod b/src/cmd/go.mod index 9c78cd14e6d..d56dde8a2ac 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -7,7 +7,7 @@ require ( github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect golang.org/x/arch v0.0.0-20200511175325-f7c78586839d golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 - golang.org/x/mod v0.2.1-0.20200429172858-859b3ef565e2 + golang.org/x/mod v0.3.0 golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect golang.org/x/tools v0.0.0-20200504152539-33427f1b0364 ) diff --git a/src/cmd/go.sum b/src/cmd/go.sum index f1b3754aad0..922df777bed 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -14,8 +14,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.2.1-0.20200429172858-859b3ef565e2 h1:VUsRDZIYpMs3R7PyYeN7BSbDfYjhxaX6HlWvM5iAEqs= -golang.org/x/mod v0.2.1-0.20200429172858-859b3ef565e2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 5c1f7254bf7..fdeef651c76 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1754,9 +1754,6 @@ // GO386 // For GOARCH=386, the floating point instruction set. // Valid values are 387, sse2. -// GOAMD64 -// For GOARCH=amd64, jumps can be optionally be aligned such that they do not end on -// or cross 32 byte boundaries. Valid values are alignedjumps (default), normaljumps. // GOMIPS // For GOARCH=mips{,le}, whether to use floating point instructions. // Valid values are hardfloat (default), softfloat. diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index 21f55e852f6..7f8f8e92be3 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -241,7 +241,6 @@ var ( // Used in envcmd.MkEnv and build ID computations. GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM)) GO386 = envOr("GO386", objabi.GO386) - GOAMD64 = envOr("GOAMD64", objabi.GOAMD64) GOMIPS = envOr("GOMIPS", objabi.GOMIPS) GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64) GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64)) @@ -267,8 +266,6 @@ func GetArchEnv() (key, val string) { return "GOARM", GOARM case "386": return "GO386", GO386 - case "amd64": - return "GOAMD64", GOAMD64 case "mips", "mipsle": return "GOMIPS", GOMIPS case "mips64", "mips64le": diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 9583b3f3271..693de8ff49d 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -582,9 +582,6 @@ Architecture-specific environment variables: GO386 For GOARCH=386, the floating point instruction set. Valid values are 387, sse2. - GOAMD64 - For GOARCH=amd64, jumps can be optionally be aligned such that they do not end on - or cross 32 byte boundaries. Valid values are alignedjumps (default), normaljumps. GOMIPS For GOARCH=mips{,le}, whether to use floating point instructions. Valid values are hardfloat (default), softfloat. diff --git a/src/cmd/go/internal/modfetch/proxy.go b/src/cmd/go/internal/modfetch/proxy.go index 0ca43d4c4a4..3971598733f 100644 --- a/src/cmd/go/internal/modfetch/proxy.go +++ b/src/cmd/go/internal/modfetch/proxy.go @@ -196,8 +196,12 @@ func TryProxies(f func(proxy string) error) error { // We try to report the most helpful error to the user. "direct" and "noproxy" // errors are best, followed by proxy errors other than ErrNotExist, followed - // by ErrNotExist. Note that errProxyOff, errNoproxy, and errUseProxy are - // equivalent to ErrNotExist. + // by ErrNotExist. + // + // Note that errProxyOff, errNoproxy, and errUseProxy are equivalent to + // ErrNotExist. errUseProxy should only be returned if "noproxy" is the only + // proxy. errNoproxy should never be returned, since there should always be a + // more useful error from "noproxy" first. const ( notExistRank = iota proxyRank @@ -212,7 +216,7 @@ func TryProxies(f func(proxy string) error) error { } isNotExistErr := errors.Is(err, os.ErrNotExist) - if proxy.url == "direct" || proxy.url == "noproxy" { + if proxy.url == "direct" || (proxy.url == "noproxy" && err != errUseProxy) { bestErr = err bestErrRank = directRank } else if bestErrRank <= proxyRank && !isNotExistErr { diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 6ab3498c3eb..071c9d2db98 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -2434,13 +2434,25 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool { if b.flagCache == nil { b.flagCache = make(map[[2]string]bool) } + + tmp := os.DevNull + if runtime.GOOS == "windows" { + f, err := ioutil.TempFile(b.WorkDir, "") + if err != nil { + return false + } + f.Close() + tmp = f.Name() + defer os.Remove(tmp) + } + // We used to write an empty C file, but that gets complicated with // go build -n. We tried using a file that does not exist, but that // fails on systems with GCC version 4.2.1; that is the last GPLv2 // version of GCC, so some systems have frozen on it. // Now we pass an empty file on stdin, which should work at least for // GCC and clang. - cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-", "-o", os.DevNull) + cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-", "-o", tmp) if cfg.BuildN || cfg.BuildX { b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs)) if cfg.BuildN { diff --git a/src/cmd/go/note_test.go b/src/cmd/go/note_test.go index 089e2f33760..659366dcf97 100644 --- a/src/cmd/go/note_test.go +++ b/src/cmd/go/note_test.go @@ -53,7 +53,7 @@ func TestNoteReading(t *testing.T) { // we've had trouble reading the notes generated by gold. err := tg.doRun([]string{"build", "-ldflags", "-buildid=" + buildID + " -linkmode=external -extldflags=-fuse-ld=gold", "-o", tg.path("hello3.exe"), tg.path("hello.go")}) if err != nil { - if tg.grepCountBoth("(invalid linker|gold|cannot find 'ld')") > 0 { + if tg.grepCountBoth("(invalid linker|gold|cannot find [‘']ld[’'])") > 0 { // It's not an error if gold isn't there. gcc claims it "cannot find 'ld'" if // ld.gold is missing, see issue #22340. t.Log("skipping gold test") diff --git a/src/cmd/go/testdata/script/issue36000.txt b/src/cmd/go/testdata/script/issue36000.txt new file mode 100644 index 00000000000..41732751ac0 --- /dev/null +++ b/src/cmd/go/testdata/script/issue36000.txt @@ -0,0 +1,6 @@ +# Tests golang.org/issue/36000 + +[!cgo] skip + +# go env with CGO flags should not make NUL file +go env CGO_CFLAGS diff --git a/src/cmd/go/testdata/script/mod_gonoproxy.txt b/src/cmd/go/testdata/script/mod_gonoproxy.txt index 2bd94cdee04..d7848c7d263 100644 --- a/src/cmd/go/testdata/script/mod_gonoproxy.txt +++ b/src/cmd/go/testdata/script/mod_gonoproxy.txt @@ -10,7 +10,7 @@ env GOSUMDB=$sumdb' '$proxy/sumdb-wrong ! go get rsc.io/quote stderr 'SECURITY ERROR' -# but GONOSUMDB bypasses sumdb, for rsc.io/quote, rsc.io/sampler, golang.org/x/text +# GONOSUMDB bypasses sumdb, for rsc.io/quote, rsc.io/sampler, golang.org/x/text env GONOSUMDB='*/quote,*/*mple*,golang.org/x' go get rsc.io/quote rm go.sum @@ -18,7 +18,13 @@ env GOPRIVATE='*/quote,*/*mple*,golang.org/x' env GONOPROXY=none # that is, proxy all despite GOPRIVATE go get rsc.io/quote -# and GONOPROXY bypasses proxy +# When GOPROXY=off, fetching modules not matched by GONOPROXY fails. +env GONOPROXY=*/fortune +env GOPROXY=off +! go get golang.org/x/text +stderr '^go get golang.org/x/text: module lookup disabled by GOPROXY=off$' + +# GONOPROXY bypasses proxy [!net] skip [!exec:git] skip env GOPRIVATE=none diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 6701b9bfba8..5e7018eb6ed 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -425,6 +425,14 @@ func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) { absfn.Type = objabi.SDWARFINFO ft.ctxt.Data = append(ft.ctxt.Data, absfn) + // In the case of "late" inlining (inlines that happen during + // wrapper generation as opposed to the main inlining phase) it's + // possible that we didn't cache the abstract function sym for the + // text symbol -- do so now if needed. See issue 38068. + if s.Func != nil && s.Func.dwarfAbsFnSym == nil { + s.Func.dwarfAbsFnSym = absfn + } + ft.precursor[s] = fnState{precursor: fn, absfn: absfn} } diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go index 72dd5856f8c..2f94ec6a67d 100644 --- a/src/cmd/internal/objabi/util.go +++ b/src/cmd/internal/objabi/util.go @@ -37,16 +37,13 @@ var ( const ( ElfRelocOffset = 256 - MachoRelocOffset = 2048 // reserve enough space for ELF relocations + MachoRelocOffset = 2048 // reserve enough space for ELF relocations + Go115AMD64 = "alignedjumps" // Should be "alignedjumps" or "normaljumps"; this replaces environment variable introduced in CL 219357. ) +// TODO(1.16): assuming no issues in 1.15 release, remove this and related constant. func goamd64() string { - switch v := envOr("GOAMD64", defaultGOAMD64); v { - case "normaljumps", "alignedjumps": - return v - } - log.Fatalf("Invalid GOAMD64 value. Must be normaljumps or alignedjumps.") - panic("unreachable") + return Go115AMD64 } func goarm() int { diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index 08f6c765c66..357a22b8794 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -385,12 +385,16 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) { offset := (signext24(r.Add()&0xffffff) + 2) * 4 var tramp loader.Sym for i := 0; ; i++ { - name := ldr.SymName(rs) + fmt.Sprintf("%+d-tramp%d", offset, i) + oName := ldr.SymName(rs) + name := oName + fmt.Sprintf("%+d-tramp%d", offset, i) tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs))) if ldr.SymType(tramp) == sym.SDYNIMPORT { // don't reuse trampoline defined in other module continue } + if oName == "runtime.deferreturn" { + ldr.SetIsDeferReturnTramp(tramp, true) + } if ldr.SymValue(tramp) == 0 { // either the trampoline does not exist -- we need to create one, // or found one the address which is not assigned -- this will be diff --git a/src/cmd/link/internal/ld/fallocate_test.go b/src/cmd/link/internal/ld/fallocate_test.go new file mode 100644 index 00000000000..a064bea23d0 --- /dev/null +++ b/src/cmd/link/internal/ld/fallocate_test.go @@ -0,0 +1,50 @@ +// Copyright 2020 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. + +// +build darwin linux + +package ld + +import ( + "io/ioutil" + "os" + "path/filepath" + "syscall" + "testing" +) + +func TestFallocate(t *testing.T) { + dir, err := ioutil.TempDir("", "TestFallocate") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + filename := filepath.Join(dir, "a.out") + out := NewOutBuf(nil) + err = out.Open(filename) + if err != nil { + t.Fatalf("Open file failed: %v", err) + } + defer out.Close() + + // Mmap 1 MiB initially, and grow to 2 and 3 MiB. + // Check if the file size and disk usage is expected. + for _, sz := range []int64{1 << 20, 2 << 20, 3 << 20} { + err = out.Mmap(uint64(sz)) + if err != nil { + t.Fatalf("Mmap failed: %v", err) + } + stat, err := os.Stat(filename) + if err != nil { + t.Fatalf("Stat failed: %v", err) + } + if got := stat.Size(); got != sz { + t.Errorf("unexpected file size: got %d, want %d", got, sz) + } + if got, want := stat.Sys().(*syscall.Stat_t).Blocks, (sz+511)/512; got != want { + t.Errorf("unexpected disk usage: got %d blocks, want %d", got, want) + } + out.munmap() + } +} diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go index 299902ec626..9a74ba875e2 100644 --- a/src/cmd/link/internal/ld/outbuf_darwin.go +++ b/src/cmd/link/internal/ld/outbuf_darwin.go @@ -10,16 +10,25 @@ import ( ) func (out *OutBuf) fallocate(size uint64) error { + stat, err := out.f.Stat() + if err != nil { + return err + } + cursize := uint64(stat.Size()) + if size <= cursize { + return nil + } + store := &syscall.Fstore_t{ Flags: syscall.F_ALLOCATEALL, Posmode: syscall.F_PEOFPOSMODE, Offset: 0, - Length: int64(size), + Length: int64(size - cursize), // F_PEOFPOSMODE allocates from the end of the file, so we want the size difference here } - _, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store))) - if err != 0 { - return err + _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store))) + if errno != 0 { + return errno } return nil diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index dc2fb70175c..a5f776ebffd 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -161,7 +161,7 @@ func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 // set the resumption point to PC_B. lastWasmAddr = uint32(r.Add()) } - if r.Type().IsDirectCall() && r.Sym() == state.deferReturnSym { + if r.Type().IsDirectCall() && (r.Sym() == state.deferReturnSym || state.ldr.IsDeferReturnTramp(r.Sym())) { if target.IsWasm() { deferreturn = lastWasmAddr - 1 } else { diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index b9e063288a8..e3405069784 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -229,7 +229,8 @@ type Loader struct { outdata [][]byte // symbol's data in the output buffer extRelocs [][]ExtReloc // symbol's external relocations - itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.* + itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.* + deferReturnTramp map[Sym]bool // whether the symbol is a trampoline of a deferreturn call objByPkg map[string]*oReader // map package path to its Go object reader @@ -354,6 +355,7 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor attrCgoExportDynamic: make(map[Sym]struct{}), attrCgoExportStatic: make(map[Sym]struct{}), itablink: make(map[Sym]struct{}), + deferReturnTramp: make(map[Sym]bool), extStaticSyms: make(map[nameVer]Sym), builtinSyms: make([]Sym, nbuiltin), flags: flags, @@ -1050,6 +1052,16 @@ func (l *Loader) IsItabLink(i Sym) bool { return false } +// Return whether this is a trampoline of a deferreturn call. +func (l *Loader) IsDeferReturnTramp(i Sym) bool { + return l.deferReturnTramp[i] +} + +// Set that i is a trampoline of a deferreturn call. +func (l *Loader) SetIsDeferReturnTramp(i Sym, v bool) { + l.deferReturnTramp[i] = v +} + // growValues grows the slice used to store symbol values. func (l *Loader) growValues(reqLen int) { curLen := len(l.values) diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index ee14601caa5..aae42c0bbce 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -686,16 +686,20 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) { // target is at some offset within the function. Calls to duff+8 and duff+256 must appear as // distinct trampolines. - name := ldr.SymName(rs) + oName := ldr.SymName(rs) + name := oName if r.Add() == 0 { - name = name + fmt.Sprintf("-tramp%d", i) + name += fmt.Sprintf("-tramp%d", i) } else { - name = name + fmt.Sprintf("%+x-tramp%d", r.Add(), i) + name += fmt.Sprintf("%+x-tramp%d", r.Add(), i) } // Look up the trampoline in case it already exists tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs))) + if oName == "runtime.deferreturn" { + ldr.SetIsDeferReturnTramp(tramp, true) + } if ldr.SymValue(tramp) == 0 { break } diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index 145eb3c4ae9..635c55dce69 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -605,10 +605,23 @@ func TestFuncAlign(t *testing.T) { } } -const helloSrc = ` +const testTrampSrc = ` package main import "fmt" -func main() { fmt.Println("hello") } +func main() { + fmt.Println("hello") + + defer func(){ + if e := recover(); e == nil { + panic("did not panic") + } + }() + f1() +} + +// Test deferreturn trampolines. See issue #39049. +func f1() { defer f2() } +func f2() { panic("XXX") } ` func TestTrampoline(t *testing.T) { @@ -631,7 +644,7 @@ func TestTrampoline(t *testing.T) { defer os.RemoveAll(tmpdir) src := filepath.Join(tmpdir, "hello.go") - err = ioutil.WriteFile(src, []byte(helloSrc), 0666) + err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666) if err != nil { t.Fatal(err) } diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 0a3ea66ffd9..8a7976a4bfb 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -29,7 +29,7 @@ golang.org/x/arch/x86/x86asm golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519/internal/edwards25519 golang.org/x/crypto/ssh/terminal -# golang.org/x/mod v0.2.1-0.20200429172858-859b3ef565e2 +# golang.org/x/mod v0.3.0 ## explicit golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go index 313872ca76c..de93e1b63f3 100644 --- a/src/crypto/tls/handshake_client_test.go +++ b/src/crypto/tls/handshake_client_test.go @@ -980,7 +980,28 @@ func testResumption(t *testing.T, version uint16) { if bytes.Equal(ticket, getTicket()) { t.Fatal("new ticket wasn't provided after old ticket expired") } - testResumeState("FreshSessionTicket", true) + + // Age the session ticket a bit at a time, but don't expire it. + d := 0 * time.Hour + for i := 0; i < 13; i++ { + d += 12 * time.Hour + serverConfig.Time = func() time.Time { return time.Now().Add(d) } + testResumeState("OldSessionTicket", true) + } + // Expire it (now a little more than 7 days) and make sure a full + // handshake occurs for TLS 1.2. Resumption should still occur for + // TLS 1.3 since the client should be using a fresh ticket sent over + // by the server. + d += 12 * time.Hour + serverConfig.Time = func() time.Time { return time.Now().Add(d) } + if version == VersionTLS13 { + testResumeState("ExpiredSessionTicket", true) + } else { + testResumeState("ExpiredSessionTicket", false) + } + if bytes.Equal(ticket, getTicket()) { + t.Fatal("new ticket wasn't provided after old ticket expired") + } // Reset serverConfig to ensure that calling SetSessionTicketKeys // before the serverConfig is used works. diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 6aacfa1ff66..57fba108a72 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -75,13 +75,8 @@ func (hs *serverHandshakeState) handshake() error { if err := hs.establishKeys(); err != nil { return err } - // ticketSupported is set in a resumption handshake if the - // ticket from the client was encrypted with an old session - // ticket key and thus a refreshed ticket should be sent. - if hs.hello.ticketSupported { - if err := hs.sendSessionTicket(); err != nil { - return err - } + if err := hs.sendSessionTicket(); err != nil { + return err } if err := hs.sendFinished(c.serverFinished[:]); err != nil { return err @@ -688,6 +683,9 @@ func (hs *serverHandshakeState) readFinished(out []byte) error { } func (hs *serverHandshakeState) sendSessionTicket() error { + // ticketSupported is set in a resumption handshake if the + // ticket from the client was encrypted with an old session + // ticket key and thus a refreshed ticket should be sent. if !hs.hello.ticketSupported { return nil } @@ -695,6 +693,13 @@ func (hs *serverHandshakeState) sendSessionTicket() error { c := hs.c m := new(newSessionTicketMsg) + createdAt := uint64(c.config.time().Unix()) + if hs.sessionState != nil { + // If this is re-wrapping an old key, then keep + // the original time it was created. + createdAt = hs.sessionState.createdAt + } + var certsFromClient [][]byte for _, cert := range c.peerCertificates { certsFromClient = append(certsFromClient, cert.Raw) @@ -702,7 +707,7 @@ func (hs *serverHandshakeState) sendSessionTicket() error { state := sessionState{ vers: c.vers, cipherSuite: hs.suite.id, - createdAt: uint64(c.config.time().Unix()), + createdAt: createdAt, masterSecret: hs.masterSecret, certificates: certsFromClient, } diff --git a/src/crypto/tls/ticket.go b/src/crypto/tls/ticket.go index 38b01fc25ca..6c1d20da206 100644 --- a/src/crypto/tls/ticket.go +++ b/src/crypto/tls/ticket.go @@ -54,7 +54,6 @@ func (m *sessionState) unmarshal(data []byte) bool { *m = sessionState{usedOldKey: m.usedOldKey} s := cryptobyte.String(data) if ok := s.ReadUint16(&m.vers) && - m.vers != VersionTLS13 && s.ReadUint16(&m.cipherSuite) && readUint64(&s, &m.createdAt) && readUint16LengthPrefixed(&s, &m.masterSecret) && diff --git a/src/crypto/x509/root_unix_test.go b/src/crypto/x509/root_unix_test.go index 39556ae60d3..5a8015429c0 100644 --- a/src/crypto/x509/root_unix_test.go +++ b/src/crypto/x509/root_unix_test.go @@ -204,7 +204,8 @@ func TestLoadSystemCertsLoadColonSeparatedDirs(t *testing.T) { } func TestReadUniqueDirectoryEntries(t *testing.T) { - temp := func(base string) string { return filepath.Join(t.TempDir(), base) } + tmp := t.TempDir() + temp := func(base string) string { return filepath.Join(tmp, base) } if f, err := os.Create(temp("file")); err != nil { t.Fatal(err) } else { @@ -216,7 +217,7 @@ func TestReadUniqueDirectoryEntries(t *testing.T) { if err := os.Symlink("../target-out", temp("link-out")); err != nil { t.Fatal(err) } - got, err := readUniqueDirectoryEntries(t.TempDir()) + got, err := readUniqueDirectoryEntries(tmp) if err != nil { t.Fatal(err) } diff --git a/src/internal/cfg/cfg.go b/src/internal/cfg/cfg.go index e40b7b4d1aa..bdbe9df3e75 100644 --- a/src/internal/cfg/cfg.go +++ b/src/internal/cfg/cfg.go @@ -33,7 +33,6 @@ const KnownEnv = ` GCCGO GO111MODULE GO386 - GOAMD64 GOARCH GOARM GOBIN diff --git a/src/math/big/link_test.go b/src/math/big/link_test.go index ad4359cee0c..2212bd444ff 100644 --- a/src/math/big/link_test.go +++ b/src/math/big/link_test.go @@ -20,8 +20,9 @@ func TestLinkerGC(t *testing.T) { t.Skip("skipping in short mode") } t.Parallel() + tmp := t.TempDir() goBin := testenv.GoToolPath(t) - goFile := filepath.Join(t.TempDir(), "x.go") + goFile := filepath.Join(tmp, "x.go") file := []byte(`package main import _ "math/big" func main() {} @@ -30,13 +31,13 @@ func main() {} t.Fatal(err) } cmd := exec.Command(goBin, "build", "-o", "x.exe", "x.go") - cmd.Dir = t.TempDir() + cmd.Dir = tmp if out, err := cmd.CombinedOutput(); err != nil { t.Fatalf("compile: %v, %s", err, out) } cmd = exec.Command(goBin, "tool", "nm", "x.exe") - cmd.Dir = t.TempDir() + cmd.Dir = tmp nm, err := cmd.CombinedOutput() if err != nil { t.Fatalf("nm: %v, %s", err, nm) diff --git a/src/os/os_test.go b/src/os/os_test.go index f86428b7b92..e8c64510f50 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -2539,3 +2539,34 @@ func isDeadlineExceeded(err error) bool { } return true } + +// Test that opening a file does not change its permissions. Issue 38225. +func TestOpenFileKeepsPermissions(t *testing.T) { + t.Parallel() + dir := t.TempDir() + name := filepath.Join(dir, "x") + f, err := Create(name) + if err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Error(err) + } + f, err = OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, 0) + if err != nil { + t.Fatal(err) + } + if fi, err := f.Stat(); err != nil { + t.Error(err) + } else if fi.Mode()&0222 == 0 { + t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode()) + } + if err := f.Close(); err != nil { + t.Error(err) + } + if fi, err := Stat(name); err != nil { + t.Error(err) + } else if fi.Mode()&0222 == 0 { + t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode()) + } +} diff --git a/src/os/readfrom_linux_test.go b/src/os/readfrom_linux_test.go index cecaed52146..b6f5cb70342 100644 --- a/src/os/readfrom_linux_test.go +++ b/src/os/readfrom_linux_test.go @@ -249,14 +249,15 @@ func newCopyFileRangeTest(t *testing.T, size int64) (dst, src *File, data []byte t.Helper() hook = hookCopyFileRange(t) + tmp := t.TempDir() - src, err := Create(filepath.Join(t.TempDir(), "src")) + src, err := Create(filepath.Join(tmp, "src")) if err != nil { t.Fatal(err) } t.Cleanup(func() { src.Close() }) - dst, err = Create(filepath.Join(t.TempDir(), "dst")) + dst, err = Create(filepath.Join(tmp, "dst")) if err != nil { t.Fatal(err) } diff --git a/src/runtime/lockrank.go b/src/runtime/lockrank.go index 899c4e2e852..a4fe9215575 100644 --- a/src/runtime/lockrank.go +++ b/src/runtime/lockrank.go @@ -33,6 +33,7 @@ const ( lockRankDummy lockRank = iota // Locks held above sched + lockRankSysmon lockRankScavenge lockRankForcegc lockRankSweepWaiters @@ -113,6 +114,7 @@ const lockRankLeafRank lockRank = 1000 var lockNames = []string{ lockRankDummy: "", + lockRankSysmon: "sysmon", lockRankScavenge: "scavenge", lockRankForcegc: "forcegc", lockRankSweepWaiters: "sweepWaiters", @@ -194,48 +196,49 @@ func (rank lockRank) String() string { // hchan lock can be held immediately above it when it is acquired. var lockPartialOrder [][]lockRank = [][]lockRank{ lockRankDummy: {}, - lockRankScavenge: {}, - lockRankForcegc: {}, + lockRankSysmon: {}, + lockRankScavenge: {lockRankSysmon}, + lockRankForcegc: {lockRankSysmon}, lockRankSweepWaiters: {}, lockRankAssistQueue: {}, lockRankCpuprof: {}, lockRankSweep: {}, - lockRankSched: {lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep}, + lockRankSched: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep}, lockRankDeadlock: {lockRankDeadlock}, lockRankPanic: {lockRankDeadlock}, - lockRankAllg: {lockRankSched, lockRankPanic}, - lockRankAllp: {lockRankSched}, + lockRankAllg: {lockRankSysmon, lockRankSched, lockRankPanic}, + lockRankAllp: {lockRankSysmon, lockRankSched}, lockRankPollDesc: {}, - lockRankTimers: {lockRankScavenge, lockRankSched, lockRankAllp, lockRankPollDesc, lockRankTimers}, + lockRankTimers: {lockRankSysmon, lockRankScavenge, lockRankSched, lockRankAllp, lockRankPollDesc, lockRankTimers}, lockRankItab: {}, lockRankReflectOffs: {lockRankItab}, lockRankHchan: {lockRankScavenge, lockRankSweep, lockRankHchan}, lockRankFin: {lockRankSched, lockRankAllg, lockRankTimers, lockRankHchan}, lockRankNotifyList: {}, - lockRankTraceBuf: {lockRankScavenge}, + lockRankTraceBuf: {lockRankSysmon, lockRankScavenge}, lockRankTraceStrings: {lockRankTraceBuf}, - lockRankMspanSpecial: {lockRankScavenge, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings}, - lockRankProf: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, - lockRankGcBitsArenas: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, + lockRankMspanSpecial: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings}, + lockRankProf: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, + lockRankGcBitsArenas: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, lockRankRoot: {}, - lockRankTrace: {lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankSched, lockRankHchan, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankSweep}, + lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankSched, lockRankHchan, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankSweep}, lockRankTraceStackTab: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankSched, lockRankAllg, lockRankTimers, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankTrace}, lockRankNetpollInit: {lockRankTimers}, lockRankRwmutexW: {}, lockRankRwmutexR: {lockRankRwmutexW}, - lockRankMcentral: {lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, - lockRankSpine: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, - lockRankSpanSetSpine: {lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, - lockRankGscan: {lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankNotifyList, lockRankProf, lockRankGcBitsArenas, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankMcentral, lockRankSpine, lockRankSpanSetSpine}, - lockRankStackpool: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankRwmutexR, lockRankMcentral, lockRankSpine, lockRankSpanSetSpine, lockRankGscan}, - lockRankStackLarge: {lockRankAssistQueue, lockRankSched, lockRankItab, lockRankHchan, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankSpanSetSpine, lockRankGscan}, + lockRankMcentral: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, + lockRankSpine: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, + lockRankSpanSetSpine: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, + lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankNotifyList, lockRankProf, lockRankGcBitsArenas, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankMcentral, lockRankSpine, lockRankSpanSetSpine}, + lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankRwmutexR, lockRankMcentral, lockRankSpine, lockRankSpanSetSpine, lockRankGscan}, + lockRankStackLarge: {lockRankSysmon, lockRankAssistQueue, lockRankSched, lockRankItab, lockRankHchan, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankSpanSetSpine, lockRankGscan}, lockRankDefer: {}, lockRankSudog: {lockRankNotifyList, lockRankHchan}, lockRankWbufSpans: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankSched, lockRankAllg, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceStrings, lockRankMspanSpecial, lockRankProf, lockRankRoot, lockRankGscan, lockRankDefer, lockRankSudog}, - lockRankMheap: {lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan, lockRankMspanSpecial, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankDefer, lockRankSudog, lockRankWbufSpans, lockRankSpanSetSpine}, - lockRankMheapSpecial: {lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, + lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankPollDesc, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan, lockRankMspanSpecial, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankMcentral, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankDefer, lockRankSudog, lockRankWbufSpans, lockRankSpanSetSpine}, + lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankHchan}, lockRankGlobalAlloc: {lockRankProf, lockRankSpine, lockRankSpanSetSpine, lockRankMheap, lockRankMheapSpecial}, lockRankGFree: {lockRankSched}, diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index 6f7dc6eaa66..2c7bfd8a59e 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -1283,9 +1283,10 @@ HaveSpan: // Publish the span in various locations. // This is safe to call without the lock held because the slots - // related to this span will only every be read or modified by - // this thread until pointers into the span are published or - // pageInUse is updated. + // related to this span will only ever be read or modified by + // this thread until pointers into the span are published (and + // we execute a publication barrier at the end of this function + // before that happens) or pageInUse is updated. h.setSpans(s.base(), npages, s) if !manual { @@ -1315,6 +1316,11 @@ HaveSpan: traceHeapAlloc() } } + + // Make sure the newly allocated span will be observed + // by the GC before pointers into the span are published. + publicationBarrier() + return s } diff --git a/src/runtime/mranges.go b/src/runtime/mranges.go index c2b8e7161c4..e23d0778eb9 100644 --- a/src/runtime/mranges.go +++ b/src/runtime/mranges.go @@ -69,6 +69,18 @@ func (a addrRange) subtract(b addrRange) addrRange { return a } +// removeGreaterEqual removes all addresses in a greater than or equal +// to addr and returns the new range. +func (a addrRange) removeGreaterEqual(addr uintptr) addrRange { + if (offAddr{addr}).lessEqual(a.base) { + return addrRange{} + } + if a.limit.lessEqual(offAddr{addr}) { + return a + } + return makeAddrRange(a.base.addr(), addr) +} + var ( // minOffAddr is the minimum address in the offset space, and // it corresponds to the virtual address arenaBaseOffset. @@ -281,7 +293,7 @@ func (a *addrRanges) removeGreaterEqual(addr uintptr) { } if r := a.ranges[pivot-1]; r.contains(addr) { removed += r.size() - r = r.subtract(makeAddrRange(addr, maxOffAddr.addr())) + r = r.removeGreaterEqual(addr) if r.size() == 0 { pivot-- } else { diff --git a/src/runtime/os_plan9.go b/src/runtime/os_plan9.go index b534cdba5d3..2bea1058f2d 100644 --- a/src/runtime/os_plan9.go +++ b/src/runtime/os_plan9.go @@ -293,7 +293,6 @@ func osinit() { ncpu = getproccount() physPageSize = getPageSize() getg().m.procid = getpid() - notify(unsafe.Pointer(funcPC(sigtramp))) } //go:nosplit @@ -311,6 +310,9 @@ func goenvs() { } func initsig(preinit bool) { + if !preinit { + notify(unsafe.Pointer(funcPC(sigtramp))) + } } //go:nosplit diff --git a/src/runtime/proc.go b/src/runtime/proc.go index ca998702245..b423026c0e5 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -533,6 +533,7 @@ func cpuinit() { // The new G calls runtime·main. func schedinit() { lockInit(&sched.lock, lockRankSched) + lockInit(&sched.sysmonlock, lockRankSysmon) lockInit(&sched.deferlock, lockRankDefer) lockInit(&sched.sudoglock, lockRankSudog) lockInit(&deadlock, lockRankDeadlock) @@ -4613,6 +4614,18 @@ func sysmon() { } unlock(&sched.lock) } + lock(&sched.sysmonlock) + { + // If we spent a long time blocked on sysmonlock + // then we want to update now and next since it's + // likely stale. + now1 := nanotime() + if now1-now > 50*1000 /* 50µs */ { + next, _ = timeSleepUntil() + } + now = now1 + } + // trigger libc interceptors if needed if *cgo_yield != nil { asmcgocall(*cgo_yield, nil) @@ -4665,6 +4678,7 @@ func sysmon() { lasttrace = now schedtrace(debug.scheddetail > 0) } + unlock(&sched.sysmonlock) } } diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 2dfa4735143..bb625aa406a 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -108,7 +108,6 @@ import "fmt" import "runtime" var gslice []string func main() { - go func() { select{} }() // ensure a second goroutine is running mapvar := make(map[string]string, 13) mapvar["abc"] = "def" mapvar["ghi"] = "jkl" @@ -231,9 +230,6 @@ func testGdbPython(t *testing.T, cgo bool) { "-ex", "echo BEGIN goroutine 1 bt\n", "-ex", "goroutine 1 bt", "-ex", "echo END\n", - "-ex", "echo BEGIN goroutine 2 bt\n", - "-ex", "goroutine 2 bt", - "-ex", "echo END\n", "-ex", "echo BEGIN goroutine all bt\n", "-ex", "goroutine all bt", "-ex", "echo END\n", @@ -310,7 +306,6 @@ func testGdbPython(t *testing.T, cgo bool) { // Check that the backtraces are well formed. checkCleanBacktrace(t, blocks["goroutine 1 bt"]) - checkCleanBacktrace(t, blocks["goroutine 2 bt"]) checkCleanBacktrace(t, blocks["goroutine 1 bt at the end"]) btGoroutine1Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?main\.main.+at`) @@ -318,12 +313,7 @@ func testGdbPython(t *testing.T, cgo bool) { t.Fatalf("goroutine 1 bt failed: %s", bl) } - btGoroutine2Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?runtime.+at`) - if bl := blocks["goroutine 2 bt"]; !btGoroutine2Re.MatchString(bl) { - t.Fatalf("goroutine 2 bt failed: %s", bl) - } - - if bl := blocks["goroutine all bt"]; !btGoroutine1Re.MatchString(bl) || !btGoroutine2Re.MatchString(bl) { + if bl := blocks["goroutine all bt"]; !btGoroutine1Re.MatchString(bl) { t.Fatalf("goroutine all bt failed: %s", bl) } diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 1fe41cf5b29..cffdb0bf277 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -349,9 +349,9 @@ type sudog struct { g *g - next *sudog - prev *sudog - elem unsafe.Pointer // data element (may point to stack) + next *sudog + prev *sudog + elem unsafe.Pointer // data element (may point to stack) // The following fields are never accessed concurrently. // For channels, waitlink is only accessed by g. @@ -366,10 +366,10 @@ type sudog struct { // g.selectDone must be CAS'd to win the wake-up race. isSelect bool - parent *sudog // semaRoot binary tree - waitlink *sudog // g.waiting list or semaRoot - waittail *sudog // semaRoot - c *hchan // channel + parent *sudog // semaRoot binary tree + waitlink *sudog // g.waiting list or semaRoot + waittail *sudog // semaRoot + c *hchan // channel } type libcall struct { @@ -768,6 +768,12 @@ type schedt struct { procresizetime int64 // nanotime() of last change to gomaxprocs totaltime int64 // ∫gomaxprocs dt up to procresizetime + + // sysmonlock protects sysmon's actions on the runtime. + // + // Acquire and hold this mutex to block sysmon from interacting + // with the rest of the runtime. + sysmonlock mutex } // Values for the flags field of a sigTabT. diff --git a/src/runtime/testdata/testprogcgo/eintr.go b/src/runtime/testdata/testprogcgo/eintr.go index 58f0dd2ca3d..9d9435d9a6c 100644 --- a/src/runtime/testdata/testprogcgo/eintr.go +++ b/src/runtime/testdata/testprogcgo/eintr.go @@ -32,11 +32,11 @@ import ( "errors" "fmt" "io" + "io/ioutil" "log" "net" "os" "os/exec" - "os/signal" "sync" "syscall" "time" @@ -71,14 +71,15 @@ func EINTR() { // spin does CPU bound spinning and allocating for a millisecond, // to get a SIGURG. //go:noinline -func spin() (float64, [][]byte) { +func spin() (float64, []byte) { stop := time.Now().Add(time.Millisecond) r1 := 0.0 - var r2 [][]byte + r2 := make([]byte, 200) for time.Now().Before(stop) { for i := 1; i < 1e6; i++ { r1 += r1 / float64(i) - r2 = append(r2, bytes.Repeat([]byte{byte(i)}, 100)) + r2 = append(r2, bytes.Repeat([]byte{byte(i)}, 100)...) + r2 = r2[100:] } } return r1, r2 @@ -96,8 +97,13 @@ func winch() { // sendSomeSignals triggers a few SIGURG and SIGWINCH signals. func sendSomeSignals() { - spin() + done := make(chan struct{}) + go func() { + spin() + close(done) + }() winch() + <-done } // testPipe tests pipe operations. @@ -212,6 +218,10 @@ func testExec(wg *sync.WaitGroup) { go func() { defer wg.Done() cmd := exec.Command(os.Args[0], "Block") + stdin, err := cmd.StdinPipe() + if err != nil { + log.Fatal(err) + } cmd.Stderr = new(bytes.Buffer) cmd.Stdout = cmd.Stderr if err := cmd.Start(); err != nil { @@ -220,9 +230,7 @@ func testExec(wg *sync.WaitGroup) { go func() { sendSomeSignals() - if err := cmd.Process.Signal(os.Interrupt); err != nil { - panic(err) - } + stdin.Close() }() if err := cmd.Wait(); err != nil { @@ -231,10 +239,7 @@ func testExec(wg *sync.WaitGroup) { }() } -// Block blocks until the process receives os.Interrupt. +// Block blocks until stdin is closed. func Block() { - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt) - defer signal.Stop(c) - <-c + io.Copy(ioutil.Discard, os.Stdin) } diff --git a/src/runtime/trace.go b/src/runtime/trace.go index 33062daa466..169b650eb42 100644 --- a/src/runtime/trace.go +++ b/src/runtime/trace.go @@ -187,6 +187,9 @@ func StartTrace() error { // paired with an end). stopTheWorldGC("start tracing") + // Prevent sysmon from running any code that could generate events. + lock(&sched.sysmonlock) + // We are in stop-the-world, but syscalls can finish and write to trace concurrently. // Exitsyscall could check trace.enabled long before and then suddenly wake up // and decide to write to trace at a random point in time. @@ -196,6 +199,7 @@ func StartTrace() error { if trace.enabled || trace.shutdown { unlock(&trace.bufLock) + unlock(&sched.sysmonlock) startTheWorldGC() return errorString("tracing is already enabled") } @@ -267,6 +271,8 @@ func StartTrace() error { unlock(&trace.bufLock) + unlock(&sched.sysmonlock) + startTheWorldGC() return nil } @@ -278,11 +284,15 @@ func StopTrace() { // and also to avoid races with traceEvent. stopTheWorldGC("stop tracing") + // See the comment in StartTrace. + lock(&sched.sysmonlock) + // See the comment in StartTrace. lock(&trace.bufLock) if !trace.enabled { unlock(&trace.bufLock) + unlock(&sched.sysmonlock) startTheWorldGC() return } @@ -320,6 +330,8 @@ func StopTrace() { trace.shutdown = true unlock(&trace.bufLock) + unlock(&sched.sysmonlock) + startTheWorldGC() // The world is started but we've set trace.shutdown, so new tracing can't start. diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 89c0a930cb6..f62c00d72fe 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -339,6 +339,26 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) { var attrs uint32 = FILE_ATTRIBUTE_NORMAL if perm&S_IWRITE == 0 { attrs = FILE_ATTRIBUTE_READONLY + if createmode == CREATE_ALWAYS { + // We have been asked to create a read-only file. + // If the file already exists, the semantics of + // the Unix open system call is to preserve the + // existing permissions. If we pass CREATE_ALWAYS + // and FILE_ATTRIBUTE_READONLY to CreateFile, + // and the file already exists, CreateFile will + // change the file permissions. + // Avoid that to preserve the Unix semantics. + h, e := CreateFile(pathp, access, sharemode, sa, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) + switch e { + case ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, ERROR_PATH_NOT_FOUND: + // File does not exist. These are the same + // errors as Errno.Is checks for ErrNotExist. + // Carry on to create the file. + default: + // Success or some different error. + return h, e + } + } } h, e := CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0) return h, e diff --git a/src/testing/testing.go b/src/testing/testing.go index 216e46ee816..608bb39671c 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -372,6 +372,7 @@ type common struct { tempDirOnce sync.Once tempDir string tempDirErr error + tempDirSeq int32 } // Short reports whether the -test.short flag is set. @@ -821,12 +822,13 @@ var tempDirReplacer struct { } // TempDir returns a temporary directory for the test to use. -// It is lazily created on first access, and calls t.Fatal if the directory -// creation fails. -// Subsequent calls to t.TempDir return the same directory. // The directory is automatically removed by Cleanup when the test and // all its subtests complete. +// Each subsequent call to t.TempDir returns a unique directory; +// if the directory creation fails, TempDir terminates the test by calling Fatal. func (c *common) TempDir() string { + // Use a single parent directory for all the temporary directories + // created by a test, each numbered sequentially. c.tempDirOnce.Do(func() { c.Helper() @@ -849,7 +851,12 @@ func (c *common) TempDir() string { if c.tempDirErr != nil { c.Fatalf("TempDir: %v", c.tempDirErr) } - return c.tempDir + seq := atomic.AddInt32(&c.tempDirSeq, 1) + dir := fmt.Sprintf("%s%c%03d", c.tempDir, os.PathSeparator, seq) + if err := os.Mkdir(dir, 0777); err != nil { + c.Fatalf("TempDir: %v", err) + } + return dir } // panicHanding is an argument to runCleanup. diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go index 1340dae5c4d..dbef7066e0e 100644 --- a/src/testing/testing_test.go +++ b/src/testing/testing_test.go @@ -7,6 +7,7 @@ package testing_test import ( "io/ioutil" "os" + "path/filepath" "testing" ) @@ -55,8 +56,11 @@ func testTempDir(t *testing.T) { t.Fatal("expected dir") } dir2 := t.TempDir() - if dir != dir2 { - t.Fatal("directory changed between calls") + if dir == dir2 { + t.Fatal("subsequent calls to TempDir returned the same directory") + } + if filepath.Dir(dir) != filepath.Dir(dir2) { + t.Fatalf("calls to TempDir do not share a parent; got %q, %q", dir, dir2) } dirCh <- dir fi, err := os.Stat(dir)