mirror of
https://github.com/golang/go.git
synced 2026-01-30 07:32:05 +03:00
Compare commits
47 Commits
dev.corety
...
go1.19.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d5ed57b13 | ||
|
|
2a7adf4ccd | ||
|
|
0618956b31 | ||
|
|
e73130cf45 | ||
|
|
0cc20ecc28 | ||
|
|
8d10cc0261 | ||
|
|
895664482c | ||
|
|
645abfe529 | ||
|
|
4fa773cdef | ||
|
|
f6d844510d | ||
|
|
2614985ef7 | ||
|
|
3747bb2482 | ||
|
|
11728b38dc | ||
|
|
064f34f048 | ||
|
|
a366ed5982 | ||
|
|
4b0e03da0e | ||
|
|
225bcec98b | ||
|
|
19d792c1eb | ||
|
|
00ece11be1 | ||
|
|
4a4127bccc | ||
|
|
9cfe4e258b | ||
|
|
86e9e0ea87 | ||
|
|
0bba4d2fe6 | ||
|
|
823e5d8afa | ||
|
|
11033eac90 | ||
|
|
4580d6dc6d | ||
|
|
62aa93010d | ||
|
|
15b234b830 | ||
|
|
4da9d6aa10 | ||
|
|
4b1c16cc45 | ||
|
|
2553a09e31 | ||
|
|
2833550891 | ||
|
|
d2bcb22ce0 | ||
|
|
d39fd4f2b7 | ||
|
|
f1b38e8b41 | ||
|
|
2b59f4dd19 | ||
|
|
2375ef8df6 | ||
|
|
16c2b3615a | ||
|
|
3c200d6c81 | ||
|
|
43456202a1 | ||
|
|
56ec1163e3 | ||
|
|
ec7bf2007e | ||
|
|
46ab46c7c5 | ||
|
|
ad672e7ce1 | ||
|
|
392548f147 | ||
|
|
bac4eb53d6 | ||
|
|
18033a0b56 |
@@ -1 +1,2 @@
|
||||
branch: master
|
||||
branch: release-branch.go1.19
|
||||
parent-branch: master
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -519,38 +520,13 @@ func TestEarlySignalHandler(t *testing.T) {
|
||||
|
||||
func TestSignalForwarding(t *testing.T) {
|
||||
checkSignalForwardingTest(t)
|
||||
buildSignalForwardingTest(t)
|
||||
|
||||
if !testWork {
|
||||
defer func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
}()
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd = exec.Command(bin[0], append(bin[1:], "1")...)
|
||||
cmd := exec.Command(bin[0], append(bin[1:], "1")...)
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
t.Logf("%v\n%s", cmd.Args, out)
|
||||
expectSignal(t, err, syscall.SIGSEGV)
|
||||
expectSignal(t, err, syscall.SIGSEGV, 0)
|
||||
|
||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
||||
@@ -561,7 +537,7 @@ func TestSignalForwarding(t *testing.T) {
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
expectSignal(t, err, syscall.SIGPIPE)
|
||||
expectSignal(t, err, syscall.SIGPIPE, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,32 +548,7 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH)
|
||||
}
|
||||
checkSignalForwardingTest(t)
|
||||
|
||||
if !testWork {
|
||||
defer func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
}()
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
buildSignalForwardingTest(t)
|
||||
|
||||
// We want to send the process a signal and see if it dies.
|
||||
// Normally the signal goes to the C thread, the Go signal
|
||||
@@ -610,42 +561,27 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
// fail.
|
||||
const tries = 20
|
||||
for i := 0; i < tries; i++ {
|
||||
cmd = exec.Command(bin[0], append(bin[1:], "2")...)
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer stderr.Close()
|
||||
|
||||
r := bufio.NewReader(stderr)
|
||||
|
||||
err = cmd.Start()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for trigger to ensure that the process is started.
|
||||
ok, err := r.ReadString('\n')
|
||||
|
||||
// Verify trigger.
|
||||
if err != nil || ok != "OK\n" {
|
||||
t.Fatalf("Did not receive OK signal")
|
||||
}
|
||||
|
||||
// Give the program a chance to enter the sleep function.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
cmd.Process.Signal(syscall.SIGSEGV)
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
err := runSignalForwardingTest(t, "2")
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if expectSignal(t, err, syscall.SIGSEGV) {
|
||||
// If the signal is delivered to a C thread, as expected,
|
||||
// the Go signal handler will disable itself and re-raise
|
||||
// the signal, causing the program to die with SIGSEGV.
|
||||
//
|
||||
// It is also possible that the signal will be
|
||||
// delivered to a Go thread, such as a GC thread.
|
||||
// Currently when the Go runtime sees that a SIGSEGV was
|
||||
// sent from a different program, it first tries to send
|
||||
// the signal to the os/signal API. If nothing is looking
|
||||
// for (or explicitly ignoring) SIGSEGV, then it crashes.
|
||||
// Because the Go runtime is invoked via a c-archive,
|
||||
// it treats this as GOTRACEBACK=crash, meaning that it
|
||||
// dumps a stack trace for all goroutines, which it does
|
||||
// by raising SIGQUIT. The effect is that we will see the
|
||||
// program die with SIGQUIT in that case, not SIGSEGV.
|
||||
if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -653,6 +589,23 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
t.Errorf("program succeeded unexpectedly %d times", tries)
|
||||
}
|
||||
|
||||
func TestSignalForwardingGo(t *testing.T) {
|
||||
// This test fails on darwin-amd64 because of the special
|
||||
// handling of user-generated SIGSEGV signals in fixsigcode in
|
||||
// runtime/signal_darwin_amd64.go.
|
||||
if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
|
||||
t.Skip("not supported on darwin-amd64")
|
||||
}
|
||||
|
||||
checkSignalForwardingTest(t)
|
||||
buildSignalForwardingTest(t)
|
||||
err := runSignalForwardingTest(t, "4")
|
||||
|
||||
// Occasionally the signal will be delivered to a C thread,
|
||||
// and the program will crash with SIGSEGV.
|
||||
expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV)
|
||||
}
|
||||
|
||||
// checkSignalForwardingTest calls t.Skip if the SignalForwarding test
|
||||
// doesn't work on this platform.
|
||||
func checkSignalForwardingTest(t *testing.T) {
|
||||
@@ -667,18 +620,121 @@ func checkSignalForwardingTest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// buildSignalForwardingTest builds the executable used by the various
|
||||
// signal forwarding tests.
|
||||
func buildSignalForwardingTest(t *testing.T) {
|
||||
if !testWork {
|
||||
t.Cleanup(func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
})
|
||||
}
|
||||
|
||||
t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2")
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
t.Log(ccArgs)
|
||||
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runSignalForwardingTest(t *testing.T, arg string) error {
|
||||
t.Logf("%v %s", bin, arg)
|
||||
cmd := exec.Command(bin[0], append(bin[1:], arg)...)
|
||||
|
||||
var out strings.Builder
|
||||
cmd.Stdout = &out
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer stderr.Close()
|
||||
|
||||
r := bufio.NewReader(stderr)
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for trigger to ensure that process is started.
|
||||
ok, err := r.ReadString('\n')
|
||||
|
||||
// Verify trigger.
|
||||
if err != nil || ok != "OK\n" {
|
||||
t.Fatal("Did not receive OK signal")
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
var errsb strings.Builder
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
io.Copy(&errsb, r)
|
||||
}()
|
||||
|
||||
// Give the program a chance to enter the function.
|
||||
// If the program doesn't get there the test will still
|
||||
// pass, although it doesn't quite test what we intended.
|
||||
// This is fine as long as the program normally makes it.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
cmd.Process.Signal(syscall.SIGSEGV)
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
s := out.String()
|
||||
if len(s) > 0 {
|
||||
t.Log(s)
|
||||
}
|
||||
wg.Wait()
|
||||
s = errsb.String()
|
||||
if len(s) > 0 {
|
||||
t.Log(s)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// expectSignal checks that err, the exit status of a test program,
|
||||
// shows a failure due to a specific signal. Returns whether we found
|
||||
// the expected signal.
|
||||
func expectSignal(t *testing.T, err error, sig syscall.Signal) bool {
|
||||
// shows a failure due to a specific signal or two. Returns whether we
|
||||
// found an expected signal.
|
||||
func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool {
|
||||
t.Helper()
|
||||
if err == nil {
|
||||
t.Error("test program succeeded unexpectedly")
|
||||
} else if ee, ok := err.(*exec.ExitError); !ok {
|
||||
t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
|
||||
} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
|
||||
t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
|
||||
} else if !ws.Signaled() || ws.Signal() != sig {
|
||||
t.Errorf("got %v; expected signal %v", ee, sig)
|
||||
} else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) {
|
||||
if sig2 == 0 {
|
||||
t.Errorf("got %q; expected signal %q", ee, sig1)
|
||||
} else {
|
||||
t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2)
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
@@ -1015,14 +1071,14 @@ func TestCompileWithoutShared(t *testing.T) {
|
||||
binArgs := append(cmdToRun(exe), "1")
|
||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
||||
t.Logf("%v\n%s", binArgs, out)
|
||||
expectSignal(t, err, syscall.SIGSEGV)
|
||||
expectSignal(t, err, syscall.SIGSEGV, 0)
|
||||
|
||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
||||
binArgs := append(cmdToRun(exe), "3")
|
||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
||||
t.Logf("%v\n%s", binArgs, out)
|
||||
expectSignal(t, err, syscall.SIGPIPE)
|
||||
expectSignal(t, err, syscall.SIGPIPE, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,12 @@ func RunGoroutines() {
|
||||
}
|
||||
}
|
||||
|
||||
// Block blocks the current thread while running Go code.
|
||||
//export Block
|
||||
func Block() {
|
||||
select {}
|
||||
}
|
||||
|
||||
var P *byte
|
||||
|
||||
// TestSEGV makes sure that an invalid address turns into a run-time Go panic.
|
||||
|
||||
13
misc/cgo/testcarchive/testdata/main5.c
vendored
13
misc/cgo/testcarchive/testdata/main5.c
vendored
@@ -29,10 +29,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
verbose = (argc > 2);
|
||||
|
||||
if (verbose) {
|
||||
printf("calling RunGoroutines\n");
|
||||
}
|
||||
|
||||
Noop();
|
||||
|
||||
switch (test) {
|
||||
@@ -90,6 +86,15 @@ int main(int argc, char** argv) {
|
||||
printf("did not receive SIGPIPE\n");
|
||||
return 0;
|
||||
}
|
||||
case 4: {
|
||||
fprintf(stderr, "OK\n");
|
||||
fflush(stderr);
|
||||
|
||||
if (verbose) {
|
||||
printf("calling Block\n");
|
||||
}
|
||||
Block();
|
||||
}
|
||||
default:
|
||||
printf("Unknown test: %d\n", test);
|
||||
return 0;
|
||||
|
||||
@@ -143,6 +143,10 @@ const (
|
||||
blockSize = 512 // Size of each block in a tar stream
|
||||
nameSize = 100 // Max length of the name field in USTAR format
|
||||
prefixSize = 155 // Max length of the prefix field in USTAR format
|
||||
|
||||
// Max length of a special file (PAX header, GNU long name or link).
|
||||
// This matches the limit used by libarchive.
|
||||
maxSpecialFileSize = 1 << 20
|
||||
)
|
||||
|
||||
// blockPadding computes the number of bytes needed to pad offset up to the
|
||||
|
||||
@@ -103,7 +103,7 @@ func (tr *Reader) next() (*Header, error) {
|
||||
continue // This is a meta header affecting the next header
|
||||
case TypeGNULongName, TypeGNULongLink:
|
||||
format.mayOnlyBe(FormatGNU)
|
||||
realname, err := io.ReadAll(tr)
|
||||
realname, err := readSpecialFile(tr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -293,7 +293,7 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) {
|
||||
// parsePAX parses PAX headers.
|
||||
// If an extended header (type 'x') is invalid, ErrHeader is returned
|
||||
func parsePAX(r io.Reader) (map[string]string, error) {
|
||||
buf, err := io.ReadAll(r)
|
||||
buf, err := readSpecialFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -828,6 +828,16 @@ func tryReadFull(r io.Reader, b []byte) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// readSpecialFile is like io.ReadAll except it returns
|
||||
// ErrFieldTooLong if more than maxSpecialFileSize is read.
|
||||
func readSpecialFile(r io.Reader) ([]byte, error) {
|
||||
buf, err := io.ReadAll(io.LimitReader(r, maxSpecialFileSize+1))
|
||||
if len(buf) > maxSpecialFileSize {
|
||||
return nil, ErrFieldTooLong
|
||||
}
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// discard skips n bytes in r, reporting an error if unable to do so.
|
||||
func discard(r io.Reader, n int64) error {
|
||||
// If possible, Seek to the last byte before the end of the data section.
|
||||
|
||||
@@ -6,6 +6,7 @@ package tar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -243,6 +244,9 @@ func TestReader(t *testing.T) {
|
||||
}, {
|
||||
file: "testdata/pax-bad-hdr-file.tar",
|
||||
err: ErrHeader,
|
||||
}, {
|
||||
file: "testdata/pax-bad-hdr-large.tar.bz2",
|
||||
err: ErrFieldTooLong,
|
||||
}, {
|
||||
file: "testdata/pax-bad-mtime-file.tar",
|
||||
err: ErrHeader,
|
||||
@@ -625,9 +629,14 @@ func TestReader(t *testing.T) {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var fr io.Reader = f
|
||||
if strings.HasSuffix(v.file, ".bz2") {
|
||||
fr = bzip2.NewReader(fr)
|
||||
}
|
||||
|
||||
// Capture all headers and checksums.
|
||||
var (
|
||||
tr = NewReader(f)
|
||||
tr = NewReader(fr)
|
||||
hdrs []*Header
|
||||
chksums []string
|
||||
rdbuf = make([]byte, 8)
|
||||
|
||||
BIN
src/archive/tar/testdata/pax-bad-hdr-large.tar.bz2
vendored
Normal file
BIN
src/archive/tar/testdata/pax-bad-hdr-large.tar.bz2
vendored
Normal file
Binary file not shown.
@@ -199,6 +199,9 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
|
||||
flag = TypeXHeader
|
||||
}
|
||||
data := buf.String()
|
||||
if len(data) > maxSpecialFileSize {
|
||||
return ErrFieldTooLong
|
||||
}
|
||||
if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal {
|
||||
return err // Global headers return here
|
||||
}
|
||||
|
||||
@@ -1004,6 +1004,33 @@ func TestIssue12594(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteLongHeader(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
h *Header
|
||||
}{{
|
||||
name: "name too long",
|
||||
h: &Header{Name: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "linkname too long",
|
||||
h: &Header{Linkname: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "uname too long",
|
||||
h: &Header{Uname: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "gname too long",
|
||||
h: &Header{Gname: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "PAX header too long",
|
||||
h: &Header{PAXRecords: map[string]string{"GOLANG.x": strings.Repeat("a", maxSpecialFileSize)}},
|
||||
}} {
|
||||
w := NewWriter(io.Discard)
|
||||
if err := w.WriteHeader(test.h); err != ErrFieldTooLong {
|
||||
t.Errorf("%v: w.WriteHeader() = %v, want ErrFieldTooLong", test.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testNonEmptyWriter wraps an io.Writer and ensures that
|
||||
// Write is never called with an empty buffer.
|
||||
type testNonEmptyWriter struct{ io.Writer }
|
||||
|
||||
@@ -798,18 +798,18 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
|
||||
// CalleeEffects appends any side effects from evaluating callee to init.
|
||||
func CalleeEffects(init *ir.Nodes, callee ir.Node) {
|
||||
for {
|
||||
init.Append(ir.TakeInit(callee)...)
|
||||
|
||||
switch callee.Op() {
|
||||
case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR:
|
||||
return // done
|
||||
|
||||
case ir.OCONVNOP:
|
||||
conv := callee.(*ir.ConvExpr)
|
||||
init.Append(ir.TakeInit(conv)...)
|
||||
callee = conv.X
|
||||
|
||||
case ir.OINLCALL:
|
||||
ic := callee.(*ir.InlinedCallExpr)
|
||||
init.Append(ir.TakeInit(ic)...)
|
||||
init.Append(ic.Body.Take()...)
|
||||
callee = ic.SingleResult()
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ var Syms struct {
|
||||
GCWriteBarrier *obj.LSym
|
||||
Goschedguarded *obj.LSym
|
||||
Growslice *obj.LSym
|
||||
Memmove *obj.LSym
|
||||
Msanread *obj.LSym
|
||||
Msanwrite *obj.LSym
|
||||
Msanmove *obj.LSym
|
||||
|
||||
@@ -434,7 +434,7 @@ func (lv *liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, loc
|
||||
if node.FrameOffset() < 0 {
|
||||
lv.f.Fatalf("Node %v has frameoffset %d\n", node.Sym().Name, node.FrameOffset())
|
||||
}
|
||||
typebits.Set(node.Type(), node.FrameOffset(), args)
|
||||
typebits.SetNoCheck(node.Type(), node.FrameOffset(), args)
|
||||
break
|
||||
}
|
||||
fallthrough // PPARAMOUT in registers acts memory-allocates like an AUTO
|
||||
@@ -1507,7 +1507,7 @@ func WriteFuncMap(fn *ir.Func, abiInfo *abi.ABIParamResultInfo) {
|
||||
bv := bitvec.New(int32(nptr) * 2)
|
||||
|
||||
for _, p := range abiInfo.InParams() {
|
||||
typebits.Set(p.Type, p.FrameOffset(abiInfo), bv)
|
||||
typebits.SetNoCheck(p.Type, p.FrameOffset(abiInfo), bv)
|
||||
}
|
||||
|
||||
nbitmap := 1
|
||||
@@ -1522,7 +1522,7 @@ func WriteFuncMap(fn *ir.Func, abiInfo *abi.ABIParamResultInfo) {
|
||||
if fn.Type().NumResults() > 0 {
|
||||
for _, p := range abiInfo.OutParams() {
|
||||
if len(p.Registers) == 0 {
|
||||
typebits.Set(p.Type, p.FrameOffset(abiInfo), bv)
|
||||
typebits.SetNoCheck(p.Type, p.FrameOffset(abiInfo), bv)
|
||||
}
|
||||
}
|
||||
off = objw.BitVec(lsym, off, bv)
|
||||
|
||||
@@ -25,6 +25,17 @@ func (s *gcSizes) Alignof(T types2.Type) int64 {
|
||||
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||
return s.Alignof(t.Elem())
|
||||
case *types2.Struct:
|
||||
if t.NumFields() == 0 && types2.IsSyncAtomicAlign64(T) {
|
||||
// Special case: sync/atomic.align64 is an
|
||||
// empty struct we recognize as a signal that
|
||||
// the struct it contains must be
|
||||
// 64-bit-aligned.
|
||||
//
|
||||
// This logic is equivalent to the logic in
|
||||
// cmd/compile/internal/types/size.go:calcStructOffset
|
||||
return 8
|
||||
}
|
||||
|
||||
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
||||
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||
// field f of x, but at least 1."
|
||||
|
||||
@@ -334,10 +334,6 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
} else { // ir.OMETHEXPR or ir.METHVALUE
|
||||
// Method expression T.M where T is a generic type.
|
||||
se := x.(*ir.SelectorExpr)
|
||||
targs := deref(se.X.Type()).RParams()
|
||||
if len(targs) == 0 {
|
||||
panic("bad")
|
||||
}
|
||||
if x.Op() == ir.OMETHVALUE {
|
||||
rcvrValue = se.X
|
||||
}
|
||||
@@ -348,7 +344,8 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
// of se.Selection, since that will be the type that actually has
|
||||
// the method.
|
||||
recv := deref(se.Selection.Type.Recv().Type)
|
||||
if len(recv.RParams()) == 0 {
|
||||
targs := recv.RParams()
|
||||
if len(targs) == 0 {
|
||||
// The embedded type that actually has the method is not
|
||||
// actually generic, so no need to build a closure.
|
||||
return x
|
||||
@@ -1357,6 +1354,9 @@ func (g *genInst) dictPass(info *instInfo) {
|
||||
}
|
||||
case ir.ODOTTYPE, ir.ODOTTYPE2:
|
||||
dt := m.(*ir.TypeAssertExpr)
|
||||
if dt.Type().IsEmptyInterface() || (dt.Type().IsInterface() && !dt.Type().HasShape()) {
|
||||
break
|
||||
}
|
||||
if !dt.Type().HasShape() && !(dt.X.Type().HasShape() && !dt.X.Type().IsEmptyInterface()) {
|
||||
break
|
||||
}
|
||||
@@ -1787,6 +1787,7 @@ func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsub
|
||||
// instantiations have been created.
|
||||
// Also handles writing method expression closures into the dictionaries.
|
||||
func (g *genInst) finalizeSyms() {
|
||||
Outer:
|
||||
for _, d := range g.dictSymsToFinalize {
|
||||
infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
|
||||
|
||||
@@ -1856,7 +1857,30 @@ func (g *genInst) finalizeSyms() {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
base.Fatalf("method %s on %v not found", bf.name, rcvr)
|
||||
// We failed to find a method expression needed for this
|
||||
// dictionary. This may happen because we tried to create a
|
||||
// dictionary for an invalid instantiation.
|
||||
//
|
||||
// For example, in test/typeparam/issue54225.go, we attempt to
|
||||
// construct a dictionary for "Node[struct{}].contentLen",
|
||||
// even though "struct{}" does not implement "Value", so it
|
||||
// cannot actually be used as a type argument to "Node".
|
||||
//
|
||||
// The real issue here is we shouldn't be attempting to create
|
||||
// those dictionaries in the first place (e.g., CL 428356),
|
||||
// but that fix is scarier for backporting to Go 1.19. Too
|
||||
// many backport CLs to this code have fixed one issue while
|
||||
// introducing another.
|
||||
//
|
||||
// So as a hack, instead of calling Fatalf, we simply skip
|
||||
// calling objw.Global below, which prevents us from emitting
|
||||
// the broken dictionary. The linker's dead code elimination
|
||||
// should then naturally prune this invalid, unneeded
|
||||
// dictionary. Worst case, if the dictionary somehow *is*
|
||||
// needed by the final executable, we've just turned an ICE
|
||||
// into a link-time missing symbol failure.
|
||||
infoPrint(" ! abandoning dictionary %v; missing method expression %v.%s\n", d.sym.Name, rcvr, bf.name)
|
||||
continue Outer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1890,14 +1890,14 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
// the TOC to the appropriate value for that module. But if it returns
|
||||
// directly to the wrapper's caller, nothing will reset it to the correct
|
||||
// value for that function.
|
||||
var call *ir.CallExpr
|
||||
if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !generic {
|
||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call.Args = ir.ParamNames(fn.Type())
|
||||
call.IsDDD = fn.Type().IsVariadic()
|
||||
fn.Body.Append(ir.NewTailCallStmt(base.Pos, call))
|
||||
} else {
|
||||
fn.SetWrapper(true) // ignore frame for panic+recover matching
|
||||
var call *ir.CallExpr
|
||||
|
||||
if generic && dot.X != nthis {
|
||||
// If there is embedding involved, then we should do the
|
||||
@@ -1982,7 +1982,22 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
typecheck.Stmts(fn.Body)
|
||||
|
||||
if AfterGlobalEscapeAnalysis {
|
||||
inline.InlineCalls(fn)
|
||||
// Inlining the method may reveal closures, which require walking all function bodies
|
||||
// to decide whether to capture free variables by value or by ref. So we only do inline
|
||||
// if the method do not contain any closures, otherwise, the escape analysis may make
|
||||
// dead variables resurrected, and causing liveness analysis confused, see issue #53702.
|
||||
var canInline bool
|
||||
switch x := call.X.(type) {
|
||||
case *ir.Name:
|
||||
canInline = len(x.Func.Closures) == 0
|
||||
case *ir.SelectorExpr:
|
||||
if x.Op() == ir.OMETHEXPR {
|
||||
canInline = x.FuncName().Func != nil && len(x.FuncName().Func.Closures) == 0
|
||||
}
|
||||
}
|
||||
if canInline {
|
||||
inline.InlineCalls(fn)
|
||||
}
|
||||
escape.Batch([]*ir.Func{fn}, false)
|
||||
}
|
||||
|
||||
|
||||
@@ -2092,7 +2092,13 @@
|
||||
|
||||
// Inline small or disjoint runtime.memmove calls with constant length.
|
||||
// See the comment in op Move in genericOps.go for discussion of the type.
|
||||
|
||||
//
|
||||
// Note that we've lost any knowledge of the type and alignment requirements
|
||||
// of the source and destination. We only know the size, and that the type
|
||||
// contains no pointers.
|
||||
// The type of the move is not necessarily v.Args[0].Type().Elem()!
|
||||
// See issue 55122 for details.
|
||||
//
|
||||
// Because expand calls runs after prove, constants useful to this pattern may not appear.
|
||||
// Both versions need to exist; the memory and register variants.
|
||||
//
|
||||
@@ -2100,31 +2106,28 @@
|
||||
(SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||
&& sz >= 0
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& t.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
||||
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(s1, s2, s3, call)
|
||||
=> (Move {t.Elem()} [int64(sz)] dst src mem)
|
||||
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
|
||||
// Match post-expansion calls, register version.
|
||||
(SelectN [0] call:(StaticCall {sym} dst src (Const(64|32) [sz]) mem))
|
||||
&& sz >= 0
|
||||
&& call.Uses == 1 // this will exclude all calls with results
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(call)
|
||||
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
|
||||
// Match pre-expansion calls.
|
||||
(SelectN [0] call:(StaticLECall {sym} dst src (Const(64|32) [sz]) mem))
|
||||
&& sz >= 0
|
||||
&& call.Uses == 1 // this will exclude all calls with results
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(call)
|
||||
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
|
||||
// De-virtualize late-expanded interface calls into late-expanded static calls.
|
||||
// Note that (ITab (IMake)) doesn't get rewritten until after the first opt pass,
|
||||
|
||||
@@ -355,7 +355,9 @@ var genericOps = []opData{
|
||||
{name: "Load", argLength: 2}, // Load from arg0. arg1=memory
|
||||
{name: "Dereference", argLength: 2}, // Load from arg0. arg1=memory. Helper op for arg/result passing, result is an otherwise not-SSA-able "value".
|
||||
{name: "Store", argLength: 3, typ: "Mem", aux: "Typ"}, // Store arg1 to arg0. arg2=memory, aux=type. Returns memory.
|
||||
// The source and destination of Move may overlap in some cases. See e.g.
|
||||
// Normally we require that the source and destination of Move do not overlap.
|
||||
// There is an exception when we know all the loads will happen before all
|
||||
// the stores. In that case, overlap is ok. See
|
||||
// memmove inlining in generic.rules. When inlineablememmovesize (in ../rewrite.go)
|
||||
// returns true, we must do all loads before all stores, when lowering Move.
|
||||
// The type of Move is used for the write barrier pass to insert write barriers
|
||||
|
||||
@@ -1362,7 +1362,8 @@ func zeroUpper56Bits(x *Value, depth int) bool {
|
||||
|
||||
// isInlinableMemmove reports whether the given arch performs a Move of the given size
|
||||
// faster than memmove. It will only return true if replacing the memmove with a Move is
|
||||
// safe, either because Move is small or because the arguments are disjoint.
|
||||
// safe, either because Move will do all of its loads before any of its stores, or
|
||||
// because the arguments are known to be disjoint.
|
||||
// This is used as a check for replacing memmove with Move ops.
|
||||
func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
// It is always safe to convert memmove into Move when its arguments are disjoint.
|
||||
@@ -1381,6 +1382,9 @@ func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
func IsInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
return isInlinableMemmove(dst, src, sz, c)
|
||||
}
|
||||
|
||||
// logLargeCopy logs the occurrence of a large copy.
|
||||
// The best place to do this is in the rewrite rules where the size of the move is easy to find.
|
||||
@@ -1394,6 +1398,14 @@ func logLargeCopy(v *Value, s int64) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
func LogLargeCopy(funcName string, pos src.XPos, s int64) {
|
||||
if s < 128 {
|
||||
return
|
||||
}
|
||||
if logopt.Enabled() {
|
||||
logopt.LogOpt(pos, "copy", "lower", funcName, fmt.Sprintf("%d bytes", s))
|
||||
}
|
||||
}
|
||||
|
||||
// hasSmallRotate reports whether the architecture has rotate instructions
|
||||
// for sizes < 32-bit. This is used to decide whether to promote some rotations.
|
||||
|
||||
@@ -21317,8 +21317,8 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {t.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21348,21 +21348,20 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
if s3.Op != OpStore {
|
||||
break
|
||||
}
|
||||
t := auxToType(s3.Aux)
|
||||
mem := s3.Args[2]
|
||||
dst := s3.Args[1]
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(t.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {t.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21392,21 +21391,20 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
if s3.Op != OpStore {
|
||||
break
|
||||
}
|
||||
t := auxToType(s3.Aux)
|
||||
mem := s3.Args[2]
|
||||
dst := s3.Args[1]
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(t.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const64 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21424,18 +21422,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt64(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const32 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21453,18 +21451,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt32(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const64 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21482,18 +21480,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt64(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const32 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21511,12 +21509,12 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt32(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ func needAlloc(n *ir.Name) bool {
|
||||
func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
s.stksize = 0
|
||||
s.stkptrsize = 0
|
||||
s.stkalign = int64(types.RegSize)
|
||||
fn := s.curfn
|
||||
|
||||
// Mark the PAUTO's unused.
|
||||
@@ -160,6 +161,9 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
}
|
||||
s.stksize += w
|
||||
s.stksize = types.Rnd(s.stksize, n.Type().Alignment())
|
||||
if n.Type().Alignment() > int64(types.RegSize) {
|
||||
s.stkalign = n.Type().Alignment()
|
||||
}
|
||||
if n.Type().HasPointers() {
|
||||
s.stkptrsize = s.stksize
|
||||
lastHasPtr = true
|
||||
@@ -169,8 +173,8 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
n.SetFrameOffset(-s.stksize)
|
||||
}
|
||||
|
||||
s.stksize = types.Rnd(s.stksize, int64(types.RegSize))
|
||||
s.stkptrsize = types.Rnd(s.stkptrsize, int64(types.RegSize))
|
||||
s.stksize = types.Rnd(s.stksize, s.stkalign)
|
||||
s.stkptrsize = types.Rnd(s.stkptrsize, s.stkalign)
|
||||
}
|
||||
|
||||
const maxStackSize = 1 << 30
|
||||
|
||||
@@ -105,6 +105,7 @@ func InitConfig() {
|
||||
ir.Syms.GCWriteBarrier = typecheck.LookupRuntimeFunc("gcWriteBarrier")
|
||||
ir.Syms.Goschedguarded = typecheck.LookupRuntimeFunc("goschedguarded")
|
||||
ir.Syms.Growslice = typecheck.LookupRuntimeFunc("growslice")
|
||||
ir.Syms.Memmove = typecheck.LookupRuntimeFunc("memmove")
|
||||
ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread")
|
||||
ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite")
|
||||
ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove")
|
||||
@@ -1359,7 +1360,47 @@ func (s *state) zero(t *types.Type, dst *ssa.Value) {
|
||||
}
|
||||
|
||||
func (s *state) move(t *types.Type, dst, src *ssa.Value) {
|
||||
s.moveWhichMayOverlap(t, dst, src, false)
|
||||
}
|
||||
func (s *state) moveWhichMayOverlap(t *types.Type, dst, src *ssa.Value, mayOverlap bool) {
|
||||
s.instrumentMove(t, dst, src)
|
||||
if mayOverlap && t.IsArray() && t.NumElem() > 1 && !ssa.IsInlinableMemmove(dst, src, t.Size(), s.f.Config) {
|
||||
// Normally, when moving Go values of type T from one location to another,
|
||||
// we don't need to worry about partial overlaps. The two Ts must either be
|
||||
// in disjoint (nonoverlapping) memory or in exactly the same location.
|
||||
// There are 2 cases where this isn't true:
|
||||
// 1) Using unsafe you can arrange partial overlaps.
|
||||
// 2) Since Go 1.17, you can use a cast from a slice to a ptr-to-array.
|
||||
// https://go.dev/ref/spec#Conversions_from_slice_to_array_pointer
|
||||
// This feature can be used to construct partial overlaps of array types.
|
||||
// var a [3]int
|
||||
// p := (*[2]int)(a[:])
|
||||
// q := (*[2]int)(a[1:])
|
||||
// *p = *q
|
||||
// We don't care about solving 1. Or at least, we haven't historically
|
||||
// and no one has complained.
|
||||
// For 2, we need to ensure that if there might be partial overlap,
|
||||
// then we can't use OpMove; we must use memmove instead.
|
||||
// (memmove handles partial overlap by copying in the correct
|
||||
// direction. OpMove does not.)
|
||||
//
|
||||
// Note that we have to be careful here not to introduce a call when
|
||||
// we're marshaling arguments to a call or unmarshaling results from a call.
|
||||
// Cases where this is happening must pass mayOverlap to false.
|
||||
// (Currently this only happens when unmarshaling results of a call.)
|
||||
if t.HasPointers() {
|
||||
s.rtcall(ir.Syms.Typedmemmove, true, nil, s.reflectType(t), dst, src)
|
||||
// We would have otherwise implemented this move with straightline code,
|
||||
// including a write barrier. Pretend we issue a write barrier here,
|
||||
// so that the write barrier tests work. (Otherwise they'd need to know
|
||||
// the details of IsInlineableMemmove.)
|
||||
s.curfn.SetWBPos(s.peekPos())
|
||||
} else {
|
||||
s.rtcall(ir.Syms.Memmove, true, nil, dst, src, s.constInt(types.Types[types.TUINTPTR], t.Size()))
|
||||
}
|
||||
ssa.LogLargeCopy(s.f.Name, s.peekPos(), t.Size())
|
||||
return
|
||||
}
|
||||
store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.mem())
|
||||
store.Aux = t
|
||||
s.vars[memVar] = store
|
||||
@@ -1541,6 +1582,36 @@ func (s *state) stmt(n ir.Node) {
|
||||
return
|
||||
}
|
||||
|
||||
// mayOverlap keeps track of whether the LHS and RHS might
|
||||
// refer to overlapping memory.
|
||||
mayOverlap := true
|
||||
if n.Y == nil {
|
||||
// Not a move at all, mayOverlap is not relevant.
|
||||
} else if n.Def {
|
||||
// A variable being defined cannot overlap anything else.
|
||||
mayOverlap = false
|
||||
} else if n.X.Op() == ir.ONAME && n.Y.Op() == ir.ONAME {
|
||||
// Two named things never overlap.
|
||||
// (Or they are identical, which we treat as nonoverlapping.)
|
||||
mayOverlap = false
|
||||
} else if n.Y.Op() == ir.ODEREF {
|
||||
p := n.Y.(*ir.StarExpr).X
|
||||
for p.Op() == ir.OCONVNOP {
|
||||
p = p.(*ir.ConvExpr).X
|
||||
}
|
||||
if p.Op() == ir.OSPTR && p.(*ir.UnaryExpr).X.Type().IsString() {
|
||||
// Pointer fields of strings point to unmodifiable memory.
|
||||
// That memory can't overlap with the memory being written.
|
||||
mayOverlap = false
|
||||
}
|
||||
} else if n.Y.Op() == ir.ORESULT || n.Y.Op() == ir.OCALLFUNC || n.Y.Op() == ir.OCALLINTER {
|
||||
// When copying values out of the return area of a call, we know
|
||||
// the source and destination don't overlap. Importantly, we must
|
||||
// set mayOverlap so we don't introduce a call to memmove while
|
||||
// we still have live data in the argument area.
|
||||
mayOverlap = false
|
||||
}
|
||||
|
||||
// Evaluate RHS.
|
||||
rhs := n.Y
|
||||
if rhs != nil {
|
||||
@@ -1641,7 +1712,7 @@ func (s *state) stmt(n ir.Node) {
|
||||
}
|
||||
}
|
||||
|
||||
s.assign(n.X, r, deref, skip)
|
||||
s.assignWhichMayOverlap(n.X, r, deref, skip, mayOverlap)
|
||||
|
||||
case ir.OIF:
|
||||
n := n.(*ir.IfStmt)
|
||||
@@ -3564,7 +3635,11 @@ const (
|
||||
// If deref is true, then we do left = *right instead (and right has already been nil-checked).
|
||||
// If deref is true and right == nil, just do left = 0.
|
||||
// skip indicates assignments (at the top level) that can be avoided.
|
||||
// mayOverlap indicates whether left&right might partially overlap in memory. Default is false.
|
||||
func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask) {
|
||||
s.assignWhichMayOverlap(left, right, deref, skip, false)
|
||||
}
|
||||
func (s *state) assignWhichMayOverlap(left ir.Node, right *ssa.Value, deref bool, skip skipMask, mayOverlap bool) {
|
||||
if left.Op() == ir.ONAME && ir.IsBlank(left) {
|
||||
return
|
||||
}
|
||||
@@ -3665,7 +3740,7 @@ func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask
|
||||
if right == nil {
|
||||
s.zero(t, addr)
|
||||
} else {
|
||||
s.move(t, addr, right)
|
||||
s.moveWhichMayOverlap(t, addr, right, mayOverlap)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -7262,7 +7337,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
||||
func defframe(s *State, e *ssafn, f *ssa.Func) {
|
||||
pp := s.pp
|
||||
|
||||
frame := types.Rnd(s.maxarg+e.stksize, int64(types.RegSize))
|
||||
s.maxarg = types.Rnd(s.maxarg, e.stkalign)
|
||||
frame := s.maxarg + e.stksize
|
||||
if Arch.PadFrame != nil {
|
||||
frame = Arch.PadFrame(frame)
|
||||
}
|
||||
@@ -7700,7 +7776,14 @@ type ssafn struct {
|
||||
strings map[string]*obj.LSym // map from constant string to data symbols
|
||||
stksize int64 // stack size for current frame
|
||||
stkptrsize int64 // prefix of stack containing pointers
|
||||
log bool // print ssa debug to the stdout
|
||||
|
||||
// alignment for current frame.
|
||||
// NOTE: when stkalign > PtrSize, currently this only ensures the offsets of
|
||||
// objects in the stack frame are aligned. The stack pointer is still aligned
|
||||
// only PtrSize.
|
||||
stkalign int64
|
||||
|
||||
log bool // print ssa debug to the stdout
|
||||
}
|
||||
|
||||
// StringData returns a symbol which
|
||||
|
||||
@@ -14,7 +14,16 @@ import (
|
||||
// the first run and then simply copied into bv at the correct offset
|
||||
// on future calls with the same type t.
|
||||
func Set(t *types.Type, off int64, bv bitvec.BitVec) {
|
||||
if uint8(t.Alignment()) > 0 && off&int64(uint8(t.Alignment())-1) != 0 {
|
||||
set(t, off, bv, false)
|
||||
}
|
||||
|
||||
// SetNoCheck is like Set, but do not check for alignment.
|
||||
func SetNoCheck(t *types.Type, off int64, bv bitvec.BitVec) {
|
||||
set(t, off, bv, true)
|
||||
}
|
||||
|
||||
func set(t *types.Type, off int64, bv bitvec.BitVec, skip bool) {
|
||||
if !skip && uint8(t.Alignment()) > 0 && off&int64(uint8(t.Alignment())-1) != 0 {
|
||||
base.Fatalf("typebits.Set: invalid initial alignment: type %v has alignment %d, but offset is %v", t, uint8(t.Alignment()), off)
|
||||
}
|
||||
if !t.HasPointers() {
|
||||
@@ -72,13 +81,13 @@ func Set(t *types.Type, off int64, bv bitvec.BitVec) {
|
||||
break
|
||||
}
|
||||
for i := int64(0); i < t.NumElem(); i++ {
|
||||
Set(elt, off, bv)
|
||||
set(elt, off, bv, skip)
|
||||
off += elt.Size()
|
||||
}
|
||||
|
||||
case types.TSTRUCT:
|
||||
for _, f := range t.Fields().Slice() {
|
||||
Set(f.Type, off+f.Offset, bv)
|
||||
set(f.Type, off+f.Offset, bv, skip)
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -376,10 +376,10 @@ func runtimeTypes() []*types.Type {
|
||||
typs[142] = newSig(params(typs[7], typs[1], typs[5]), nil)
|
||||
typs[143] = types.NewSlice(typs[7])
|
||||
typs[144] = newSig(params(typs[7], typs[143]), nil)
|
||||
typs[145] = newSig(params(typs[66], typs[66], typs[15]), nil)
|
||||
typs[146] = newSig(params(typs[60], typs[60], typs[15]), nil)
|
||||
typs[147] = newSig(params(typs[62], typs[62], typs[15]), nil)
|
||||
typs[148] = newSig(params(typs[24], typs[24], typs[15]), nil)
|
||||
typs[149] = newSig(params(typs[28], typs[28], typs[15]), nil)
|
||||
typs[145] = newSig(params(typs[66], typs[66], typs[17]), nil)
|
||||
typs[146] = newSig(params(typs[60], typs[60], typs[17]), nil)
|
||||
typs[147] = newSig(params(typs[62], typs[62], typs[17]), nil)
|
||||
typs[148] = newSig(params(typs[24], typs[24], typs[17]), nil)
|
||||
typs[149] = newSig(params(typs[28], typs[28], typs[17]), nil)
|
||||
return typs[:]
|
||||
}
|
||||
|
||||
@@ -259,16 +259,16 @@ func asanwrite(addr, size uintptr)
|
||||
func checkptrAlignment(unsafe.Pointer, *byte, uintptr)
|
||||
func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer)
|
||||
|
||||
func libfuzzerTraceCmp1(uint8, uint8, int)
|
||||
func libfuzzerTraceCmp2(uint16, uint16, int)
|
||||
func libfuzzerTraceCmp4(uint32, uint32, int)
|
||||
func libfuzzerTraceCmp8(uint64, uint64, int)
|
||||
func libfuzzerTraceConstCmp1(uint8, uint8, int)
|
||||
func libfuzzerTraceConstCmp2(uint16, uint16, int)
|
||||
func libfuzzerTraceConstCmp4(uint32, uint32, int)
|
||||
func libfuzzerTraceConstCmp8(uint64, uint64, int)
|
||||
func libfuzzerHookStrCmp(string, string, int)
|
||||
func libfuzzerHookEqualFold(string, string, int)
|
||||
func libfuzzerTraceCmp1(uint8, uint8, uint)
|
||||
func libfuzzerTraceCmp2(uint16, uint16, uint)
|
||||
func libfuzzerTraceCmp4(uint32, uint32, uint)
|
||||
func libfuzzerTraceCmp8(uint64, uint64, uint)
|
||||
func libfuzzerTraceConstCmp1(uint8, uint8, uint)
|
||||
func libfuzzerTraceConstCmp2(uint16, uint16, uint)
|
||||
func libfuzzerTraceConstCmp4(uint32, uint32, uint)
|
||||
func libfuzzerTraceConstCmp8(uint64, uint64, uint)
|
||||
func libfuzzerHookStrCmp(string, string, uint)
|
||||
func libfuzzerHookEqualFold(string, string, uint)
|
||||
|
||||
// architecture variants
|
||||
var x86HasPOPCNT bool
|
||||
|
||||
@@ -1928,7 +1928,9 @@ func (w *exportWriter) expr(n ir.Node) {
|
||||
w.op(n.Op())
|
||||
w.pos(n.Pos())
|
||||
w.expr(n.X)
|
||||
w.expr(n.RType)
|
||||
if w.bool(n.RType != nil) {
|
||||
w.expr(n.RType)
|
||||
}
|
||||
if w.bool(n.ITab != nil) {
|
||||
w.expr(n.ITab)
|
||||
}
|
||||
|
||||
@@ -1465,7 +1465,10 @@ func (r *importReader) node() ir.Node {
|
||||
return n
|
||||
|
||||
case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
|
||||
n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), r.expr())
|
||||
n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), nil)
|
||||
if r.bool() {
|
||||
n.RType = r.expr()
|
||||
}
|
||||
if r.bool() {
|
||||
n.ITab = r.expr()
|
||||
}
|
||||
|
||||
@@ -637,3 +637,40 @@ func TestIssue50646(t *testing.T) {
|
||||
t.Errorf("comparable not assignable to any")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue55030(t *testing.T) {
|
||||
// makeSig makes the signature func(typ...)
|
||||
makeSig := func(typ Type) {
|
||||
par := NewVar(nopos, nil, "", typ)
|
||||
params := NewTuple(par)
|
||||
NewSignatureType(nil, nil, nil, params, nil, true)
|
||||
}
|
||||
|
||||
// makeSig must not panic for the following (example) types:
|
||||
// []int
|
||||
makeSig(NewSlice(Typ[Int]))
|
||||
|
||||
// string
|
||||
makeSig(Typ[String])
|
||||
|
||||
// P where P's core type is string
|
||||
{
|
||||
P := NewTypeName(nopos, nil, "P", nil) // [P string]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})))
|
||||
}
|
||||
|
||||
// P where P's core type is an (unnamed) slice
|
||||
{
|
||||
P := NewTypeName(nopos, nil, "P", nil) // [P []int]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})))
|
||||
}
|
||||
|
||||
// P where P's core type is bytestring (i.e., string or []byte)
|
||||
{
|
||||
t1 := NewTerm(true, Typ[String]) // ~string
|
||||
t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
|
||||
u := NewUnion([]*Term{t1, t2}) // ~string | []byte
|
||||
P := NewTypeName(nopos, nil, "P", nil) // [P ~string | []byte]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
|
||||
package types2
|
||||
|
||||
import "cmd/compile/internal/syntax"
|
||||
import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// API
|
||||
@@ -28,16 +31,18 @@ type Signature struct {
|
||||
// NewSignatureType creates a new function type for the given receiver,
|
||||
// receiver type parameters, type parameters, parameters, and results. If
|
||||
// variadic is set, params must hold at least one parameter and the last
|
||||
// parameter must be of unnamed slice type. If recv is non-nil, typeParams must
|
||||
// be empty. If recvTypeParams is non-empty, recv must be non-nil.
|
||||
// parameter's core type must be of unnamed slice or bytestring type.
|
||||
// If recv is non-nil, typeParams must be empty. If recvTypeParams is
|
||||
// non-empty, recv must be non-nil.
|
||||
func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
|
||||
if variadic {
|
||||
n := params.Len()
|
||||
if n == 0 {
|
||||
panic("variadic function must have at least one parameter")
|
||||
}
|
||||
if _, ok := params.At(n - 1).typ.(*Slice); !ok {
|
||||
panic("variadic parameter must be of unnamed slice type")
|
||||
core := coreString(params.At(n - 1).typ)
|
||||
if _, ok := core.(*Slice); !ok && !isString(core) {
|
||||
panic(fmt.Sprintf("got %s, want variadic parameter with unnamed slice type or string as core type", core.String()))
|
||||
}
|
||||
}
|
||||
sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||
return s.Alignof(t.elem)
|
||||
case *Struct:
|
||||
if len(t.fields) == 0 && isSyncAtomicAlign64(T) {
|
||||
if len(t.fields) == 0 && IsSyncAtomicAlign64(T) {
|
||||
// Special case: sync/atomic.align64 is an
|
||||
// empty struct we recognize as a signal that
|
||||
// the struct it contains must be
|
||||
@@ -104,7 +104,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||
return a
|
||||
}
|
||||
|
||||
func isSyncAtomicAlign64(T Type) bool {
|
||||
func IsSyncAtomicAlign64(T Type) bool {
|
||||
named, ok := T.(*Named)
|
||||
if !ok {
|
||||
return false
|
||||
|
||||
@@ -243,6 +243,7 @@ func fixedlit(ctxt initContext, kind initKind, n *ir.CompLitExpr, var_ ir.Node,
|
||||
// confuses about variables lifetime. So making sure those expressions
|
||||
// are ordered correctly here. See issue #52673.
|
||||
orderBlock(&sinit, map[string][]*ir.Name{})
|
||||
walkStmtList(sinit)
|
||||
}
|
||||
init.Append(sinit...)
|
||||
continue
|
||||
|
||||
3
src/cmd/dist/build.go
vendored
3
src/cmd/dist/build.go
vendored
@@ -938,7 +938,8 @@ func packagefile(pkg string) string {
|
||||
}
|
||||
|
||||
// unixOS is the set of GOOS values matched by the "unix" build tag.
|
||||
// This is the same list as in go/build/syslist.go.
|
||||
// This is the same list as in go/build/syslist.go and
|
||||
// cmd/go/internal/imports/build.go.
|
||||
var unixOS = map[string]bool{
|
||||
"aix": true,
|
||||
"android": true,
|
||||
|
||||
@@ -3,7 +3,7 @@ module cmd
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0
|
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1
|
||||
golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0 h1:XgEFTOJTsN3Li0Txfhn2UzsysGJfXIDe7wE07uY7ZfI=
|
||||
github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk=
|
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 h1:8pyqKJvrJqUYaKS851Ule26pwWvey6IDMiczaBLDKLQ=
|
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2 h1:rcanfLhLDA8nozr/K289V1zcntHr3V+SHlXwzz1ZI2g=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU=
|
||||
|
||||
@@ -174,8 +174,8 @@ func ExtraEnvVars() []cfg.EnvVar {
|
||||
// ExtraEnvVarsCostly returns environment variables that should not leak into child processes
|
||||
// but are costly to evaluate.
|
||||
func ExtraEnvVarsCostly() []cfg.EnvVar {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
|
||||
cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
|
||||
if err != nil {
|
||||
// Should not happen - b.CFlags was given an empty package.
|
||||
|
||||
@@ -20,6 +20,7 @@ package imports
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/go/internal/cfg"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build/constraint"
|
||||
@@ -201,17 +202,22 @@ func matchTag(name string, tags map[string]bool, prefer bool) bool {
|
||||
return prefer
|
||||
}
|
||||
|
||||
have := tags[name]
|
||||
if name == "linux" {
|
||||
have = have || tags["android"]
|
||||
if tags[name] {
|
||||
return true
|
||||
}
|
||||
if name == "solaris" {
|
||||
have = have || tags["illumos"]
|
||||
|
||||
switch name {
|
||||
case "linux":
|
||||
return tags["android"]
|
||||
case "solaris":
|
||||
return tags["illumos"]
|
||||
case "darwin":
|
||||
return tags["ios"]
|
||||
case "unix":
|
||||
return unixOS[cfg.BuildContext.GOOS]
|
||||
default:
|
||||
return false
|
||||
}
|
||||
if name == "darwin" {
|
||||
have = have || tags["ios"]
|
||||
}
|
||||
return have
|
||||
}
|
||||
|
||||
// eval is like
|
||||
@@ -322,6 +328,24 @@ var KnownOS = map[string]bool{
|
||||
"zos": true,
|
||||
}
|
||||
|
||||
// unixOS is the set of GOOS values matched by the "unix" build tag.
|
||||
// This is not used for filename matching.
|
||||
// This is the same list as in go/build/syslist.go and cmd/dist/build.go.
|
||||
var unixOS = map[string]bool{
|
||||
"aix": true,
|
||||
"android": true,
|
||||
"darwin": true,
|
||||
"dragonfly": true,
|
||||
"freebsd": true,
|
||||
"hurd": true,
|
||||
"illumos": true,
|
||||
"ios": true,
|
||||
"linux": true,
|
||||
"netbsd": true,
|
||||
"openbsd": true,
|
||||
"solaris": true,
|
||||
}
|
||||
|
||||
var KnownArch = map[string]bool{
|
||||
"386": true,
|
||||
"amd64": true,
|
||||
|
||||
@@ -36,6 +36,9 @@ func loadTags() map[string]bool {
|
||||
for _, tag := range cfg.BuildContext.BuildTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
for _, tag := range cfg.BuildContext.ToolTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
for _, tag := range cfg.BuildContext.ReleaseTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
|
||||
@@ -689,8 +689,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
||||
// Do we need to run a build to gather information?
|
||||
needStale := (listJson && listJsonFields.needAny("Stale", "StaleReason")) || strings.Contains(*listFmt, ".Stale")
|
||||
if needStale || *listExport || *listCompiled {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
b.IsCmdList = true
|
||||
b.NeedExport = *listExport
|
||||
b.NeedCompiledGoFiles = *listCompiled
|
||||
|
||||
@@ -37,7 +37,9 @@ const (
|
||||
// A Repo represents a code hosting source.
|
||||
// Typical implementations include local version control repositories,
|
||||
// remote version control servers, and code hosting sites.
|
||||
// A Repo must be safe for simultaneous use by multiple goroutines.
|
||||
//
|
||||
// A Repo must be safe for simultaneous use by multiple goroutines,
|
||||
// and callers must not modify returned values, which may be cached and shared.
|
||||
type Repo interface {
|
||||
// CheckReuse checks whether the old origin information
|
||||
// remains up to date. If so, whatever cached object it was
|
||||
|
||||
@@ -348,12 +348,21 @@ func (r *gitRepo) Latest() (*RevInfo, error) {
|
||||
if refs["HEAD"] == "" {
|
||||
return nil, ErrNoCommits
|
||||
}
|
||||
info, err := r.Stat(refs["HEAD"])
|
||||
statInfo, err := r.Stat(refs["HEAD"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stat may return cached info, so make a copy to modify here.
|
||||
info := new(RevInfo)
|
||||
*info = *statInfo
|
||||
info.Origin = new(Origin)
|
||||
if statInfo.Origin != nil {
|
||||
*info.Origin = *statInfo.Origin
|
||||
}
|
||||
info.Origin.Ref = "HEAD"
|
||||
info.Origin.Hash = refs["HEAD"]
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -560,7 +569,7 @@ func (r *gitRepo) fetchRefsLocked() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// statLocal returns a RevInfo describing rev in the local git repository.
|
||||
// statLocal returns a new RevInfo describing rev in the local git repository.
|
||||
// It uses version as info.Version.
|
||||
func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) {
|
||||
out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "--no-decorate", "-n1", "--format=format:%H %ct %D", rev, "--")
|
||||
|
||||
@@ -182,23 +182,27 @@ func mergeOrigin(m1, m2 *codehost.Origin) *codehost.Origin {
|
||||
if !m2.Checkable() {
|
||||
return m2
|
||||
}
|
||||
|
||||
merged := new(codehost.Origin)
|
||||
*merged = *m1 // Clone to avoid overwriting fields in cached results.
|
||||
|
||||
if m2.TagSum != "" {
|
||||
if m1.TagSum != "" && (m1.TagSum != m2.TagSum || m1.TagPrefix != m2.TagPrefix) {
|
||||
m1.ClearCheckable()
|
||||
return m1
|
||||
merged.ClearCheckable()
|
||||
return merged
|
||||
}
|
||||
m1.TagSum = m2.TagSum
|
||||
m1.TagPrefix = m2.TagPrefix
|
||||
merged.TagSum = m2.TagSum
|
||||
merged.TagPrefix = m2.TagPrefix
|
||||
}
|
||||
if m2.Hash != "" {
|
||||
if m1.Hash != "" && (m1.Hash != m2.Hash || m1.Ref != m2.Ref) {
|
||||
m1.ClearCheckable()
|
||||
return m1
|
||||
merged.ClearCheckable()
|
||||
return merged
|
||||
}
|
||||
m1.Hash = m2.Hash
|
||||
m1.Ref = m2.Ref
|
||||
merged.Hash = m2.Hash
|
||||
merged.Ref = m2.Ref
|
||||
}
|
||||
return m1
|
||||
return merged
|
||||
}
|
||||
|
||||
// addVersions fills in m.Versions with the list of known versions.
|
||||
|
||||
@@ -27,7 +27,7 @@ var importTests = []struct {
|
||||
},
|
||||
{
|
||||
path: "golang.org/x/net",
|
||||
err: `module golang.org/x/net@.* found \(v0.0.0-.*\), but does not contain package golang.org/x/net`,
|
||||
err: `module golang.org/x/net@.* found \(v[01]\.\d+\.\d+\), but does not contain package golang.org/x/net`,
|
||||
},
|
||||
{
|
||||
path: "golang.org/x/text",
|
||||
|
||||
@@ -220,6 +220,17 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
|
||||
return revErr, err
|
||||
}
|
||||
|
||||
mergeRevOrigin := func(rev *modfetch.RevInfo, origin *codehost.Origin) *modfetch.RevInfo {
|
||||
merged := mergeOrigin(rev.Origin, origin)
|
||||
if merged == rev.Origin {
|
||||
return rev
|
||||
}
|
||||
clone := new(modfetch.RevInfo)
|
||||
*clone = *rev
|
||||
clone.Origin = merged
|
||||
return clone
|
||||
}
|
||||
|
||||
lookup := func(v string) (*modfetch.RevInfo, error) {
|
||||
rev, err := repo.Stat(v)
|
||||
// Stat can return a non-nil rev and a non-nil err,
|
||||
@@ -227,7 +238,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
|
||||
if rev == nil && err != nil {
|
||||
return revErr, err
|
||||
}
|
||||
rev.Origin = mergeOrigin(rev.Origin, versions.Origin)
|
||||
rev = mergeRevOrigin(rev, versions.Origin)
|
||||
if err != nil {
|
||||
return rev, err
|
||||
}
|
||||
@@ -256,12 +267,12 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
|
||||
if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
|
||||
return revErr, err
|
||||
}
|
||||
info, err := repo.Stat(current)
|
||||
if info == nil && err != nil {
|
||||
rev, err = repo.Stat(current)
|
||||
if rev == nil && err != nil {
|
||||
return revErr, err
|
||||
}
|
||||
info.Origin = mergeOrigin(info.Origin, versions.Origin)
|
||||
return info, err
|
||||
rev = mergeRevOrigin(rev, versions.Origin)
|
||||
return rev, err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,8 +91,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
|
||||
}
|
||||
|
||||
work.BuildInit()
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
b.Print = printStderr
|
||||
|
||||
i := 0
|
||||
|
||||
@@ -744,8 +744,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
|
||||
if cfg.BuildI {
|
||||
fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
|
||||
@@ -800,7 +799,16 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
||||
if !testC || a.Failed {
|
||||
return
|
||||
}
|
||||
b.Init()
|
||||
|
||||
// TODO(bcmills): I have no idea why the Builder must be reset here, but
|
||||
// without this reset dance, TestGoTestDashIDashOWritesBinary fails with
|
||||
// lots of "vet config not found" errors. This was added in CL 5699088,
|
||||
// which had almost no public discussion, a very short commit description,
|
||||
// and left no comment in the code to explain what is going on here. 🤯
|
||||
//
|
||||
// Maybe this has the effect of removing actions that were registered by the
|
||||
// call to CompileAction above?
|
||||
b = work.NewBuilder("")
|
||||
}
|
||||
|
||||
var builds, runs, prints []*work.Action
|
||||
@@ -916,7 +924,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
||||
ensureImport(p, "sync/atomic")
|
||||
}
|
||||
|
||||
buildTest, runTest, printTest, err := builderTest(&b, ctx, pkgOpts, p, allImports[p])
|
||||
buildTest, runTest, printTest, err := builderTest(b, ctx, pkgOpts, p, allImports[p])
|
||||
if err != nil {
|
||||
str := err.Error()
|
||||
str = strings.TrimPrefix(str, "\n")
|
||||
|
||||
@@ -94,8 +94,7 @@ func runVet(ctx context.Context, cmd *base.Command, args []string) {
|
||||
base.Fatalf("no packages to vet")
|
||||
}
|
||||
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
|
||||
root := &work.Action{Mode: "go vet"}
|
||||
for _, p := range pkgs {
|
||||
|
||||
@@ -240,7 +240,13 @@ const (
|
||||
ModeVetOnly = 1 << 8
|
||||
)
|
||||
|
||||
func (b *Builder) Init() {
|
||||
// NewBuilder returns a new Builder ready for use.
|
||||
//
|
||||
// If workDir is the empty string, NewBuilder creates a WorkDir if needed
|
||||
// and arranges for it to be removed in case of an unclean exit.
|
||||
func NewBuilder(workDir string) *Builder {
|
||||
b := new(Builder)
|
||||
|
||||
b.Print = func(a ...any) (int, error) {
|
||||
return fmt.Fprint(os.Stderr, a...)
|
||||
}
|
||||
@@ -249,7 +255,9 @@ func (b *Builder) Init() {
|
||||
b.toolIDCache = make(map[string]string)
|
||||
b.buildIDCache = make(map[string]string)
|
||||
|
||||
if cfg.BuildN {
|
||||
if workDir != "" {
|
||||
b.WorkDir = workDir
|
||||
} else if cfg.BuildN {
|
||||
b.WorkDir = "$WORK"
|
||||
} else {
|
||||
tmp, err := os.MkdirTemp(cfg.Getenv("GOTMPDIR"), "go-build")
|
||||
@@ -306,6 +314,8 @@ func (b *Builder) Init() {
|
||||
base.Exit()
|
||||
}
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func CheckGOOSARCHPair(goos, goarch string) error {
|
||||
|
||||
@@ -403,8 +403,7 @@ var RuntimeVersion = runtime.Version()
|
||||
func runBuild(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.InitWorkfile()
|
||||
BuildInit()
|
||||
var b Builder
|
||||
b.Init()
|
||||
b := NewBuilder("")
|
||||
|
||||
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: true}, args)
|
||||
load.CheckPackageErrors(pkgs)
|
||||
@@ -728,8 +727,8 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag
|
||||
}
|
||||
base.ExitIfErrors()
|
||||
|
||||
var b Builder
|
||||
b.Init()
|
||||
b := NewBuilder("")
|
||||
|
||||
depMode := ModeBuild
|
||||
if cfg.BuildI {
|
||||
depMode = ModeInstall
|
||||
|
||||
@@ -556,10 +556,8 @@ func (ts *testScript) cmdCc(want simpleStatus, args []string) {
|
||||
ts.fatalf("usage: cc args... [&]")
|
||||
}
|
||||
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder(ts.workdir)
|
||||
ts.cmdExec(want, append(b.GccCmd(".", ""), args...))
|
||||
robustio.RemoveAll(b.WorkDir)
|
||||
}
|
||||
|
||||
// cd changes to a different directory.
|
||||
|
||||
32
src/cmd/go/testdata/script/import_unix_tag.txt
vendored
Normal file
32
src/cmd/go/testdata/script/import_unix_tag.txt
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Regression test for https://go.dev/issue/54712: the "unix" build constraint
|
||||
# was not applied consistently during package loading.
|
||||
|
||||
go list -x -f '{{if .Module}}{{.ImportPath}}{{end}}' -deps .
|
||||
stdout 'example.com/version'
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
|
||||
go 1.19
|
||||
|
||||
require example.com/version v1.1.0
|
||||
-- go.sum --
|
||||
example.com/version v1.1.0 h1:VdPnGmIF1NJrntStkxGrF3L/OfhaL567VzCjncGUgtM=
|
||||
example.com/version v1.1.0/go.mod h1:S7K9BnT4o5wT4PCczXPfWVzpjD4ud4e7AJMQJEgiu2Q=
|
||||
-- main_notunix.go --
|
||||
//go:build !(aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris)
|
||||
|
||||
package main
|
||||
|
||||
import _ "example.com/version"
|
||||
|
||||
func main() {}
|
||||
|
||||
-- main_unix.go --
|
||||
//go:build unix
|
||||
|
||||
package main
|
||||
|
||||
import _ "example.com/version"
|
||||
|
||||
func main() {}
|
||||
29
src/cmd/go/testdata/script/test_race_tag.txt
vendored
Normal file
29
src/cmd/go/testdata/script/test_race_tag.txt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Tests Issue #54468
|
||||
|
||||
[short] skip 'links a test binary'
|
||||
[!race] skip
|
||||
|
||||
go mod tidy
|
||||
go test -c -o=$devnull -race .
|
||||
|
||||
! stderr 'cannot find package'
|
||||
|
||||
-- go.mod --
|
||||
module testrace
|
||||
|
||||
go 1.18
|
||||
|
||||
require rsc.io/sampler v1.0.0
|
||||
-- race_test.go --
|
||||
//go:build race
|
||||
|
||||
package testrace
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
_ "rsc.io/sampler"
|
||||
)
|
||||
|
||||
func TestRaceTag(t *testing.T) {
|
||||
}
|
||||
@@ -1842,3 +1842,82 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestIssue54320(t *testing.T) {
|
||||
// Check that when trampolines are used, the DWARF LPT is correctly
|
||||
// emitted in the final binary
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
if runtime.GOOS == "plan9" {
|
||||
t.Skip("skipping on plan9; no DWARF symbol table in executables")
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
|
||||
const prog = `
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Hello world\n");
|
||||
}
|
||||
`
|
||||
|
||||
dir := t.TempDir()
|
||||
f := gobuild(t, dir, prog, "-ldflags=-debugtramp=2")
|
||||
defer f.Close()
|
||||
|
||||
d, err := f.DWARF()
|
||||
if err != nil {
|
||||
t.Fatalf("error reading DWARF: %v", err)
|
||||
}
|
||||
|
||||
rdr := d.Reader()
|
||||
found := false
|
||||
var entry *dwarf.Entry
|
||||
for entry, err = rdr.Next(); entry != nil; entry, err = rdr.Next() {
|
||||
if err != nil {
|
||||
t.Fatalf("error reading DWARF: %v", err)
|
||||
}
|
||||
if entry.Tag != dwarf.TagCompileUnit {
|
||||
continue
|
||||
}
|
||||
name, _ := entry.Val(dwarf.AttrName).(string)
|
||||
if name == "main" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
rdr.SkipChildren()
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("could not find main compile unit")
|
||||
}
|
||||
lr, err := d.LineReader(entry)
|
||||
if err != nil {
|
||||
t.Fatalf("error obtaining linereader: %v", err)
|
||||
}
|
||||
|
||||
var le dwarf.LineEntry
|
||||
found = false
|
||||
for {
|
||||
if err := lr.Next(&le); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
t.Fatalf("error reading linentry: %v", err)
|
||||
}
|
||||
// check LE contains an entry to test.go
|
||||
if le.File == nil {
|
||||
continue
|
||||
}
|
||||
file := filepath.Base(le.File.Name)
|
||||
if file == "test.go" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("no LPT entries for test.go")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1423,7 +1423,6 @@ func (ctxt *Link) hostlink() {
|
||||
if ctxt.HeadType == objabi.Hdarwin {
|
||||
if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() {
|
||||
argv = append(argv, "-Wl,-no_pie")
|
||||
argv = append(argv, "-Wl,-pagezero_size,4000000")
|
||||
}
|
||||
}
|
||||
if *flagRace && ctxt.HeadType == objabi.Hwindows {
|
||||
@@ -1775,6 +1774,13 @@ func (ctxt *Link) hostlink() {
|
||||
if len(out) > 0 {
|
||||
// always print external output even if the command is successful, so that we don't
|
||||
// swallow linker warnings (see https://golang.org/issue/17935).
|
||||
if ctxt.IsDarwin() && ctxt.IsAMD64() {
|
||||
const noPieWarning = "ld: warning: -no_pie is deprecated when targeting new OS versions\n"
|
||||
if i := bytes.Index(out, []byte(noPieWarning)); i >= 0 {
|
||||
// swallow -no_pie deprecation warning, issue 54482
|
||||
out = append(out[:i], out[i+len(noPieWarning):]...)
|
||||
}
|
||||
}
|
||||
ctxt.Logf("%s", out)
|
||||
}
|
||||
|
||||
|
||||
@@ -1610,13 +1610,8 @@ func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, a
|
||||
if l.SymType(fnSymIdx) != sym.STEXT {
|
||||
log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String())
|
||||
}
|
||||
if l.IsExternal(fnSymIdx) {
|
||||
// Current expectation is that any external function will
|
||||
// not have auxsyms.
|
||||
return
|
||||
}
|
||||
r, li := l.toLocal(fnSymIdx)
|
||||
auxs := r.Auxs(li)
|
||||
r, auxs := l.auxs(fnSymIdx)
|
||||
|
||||
for i := range auxs {
|
||||
a := &auxs[i]
|
||||
switch a.Type() {
|
||||
|
||||
8
src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
generated
vendored
8
src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
generated
vendored
@@ -79,7 +79,7 @@ type configMenuEntry struct {
|
||||
}
|
||||
|
||||
// configMenu returns a list of items to add to a menu in the web UI.
|
||||
func configMenu(fname string, url url.URL) []configMenuEntry {
|
||||
func configMenu(fname string, u url.URL) []configMenuEntry {
|
||||
// Start with system configs.
|
||||
configs := []namedConfig{{Name: "Default", config: defaultConfig()}}
|
||||
if settings, err := readSettings(fname); err == nil {
|
||||
@@ -91,13 +91,15 @@ func configMenu(fname string, url url.URL) []configMenuEntry {
|
||||
result := make([]configMenuEntry, len(configs))
|
||||
lastMatch := -1
|
||||
for i, cfg := range configs {
|
||||
dst, changed := cfg.config.makeURL(url)
|
||||
dst, changed := cfg.config.makeURL(u)
|
||||
if !changed {
|
||||
lastMatch = i
|
||||
}
|
||||
// Use a relative URL to work in presence of stripping/redirects in webui.go.
|
||||
rel := &url.URL{RawQuery: dst.RawQuery, ForceQuery: true}
|
||||
result[i] = configMenuEntry{
|
||||
Name: cfg.Name,
|
||||
URL: dst.String(),
|
||||
URL: rel.String(),
|
||||
UserConfig: (i != 0),
|
||||
}
|
||||
}
|
||||
|
||||
3
src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go
generated
vendored
3
src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go
generated
vendored
@@ -385,6 +385,9 @@ func multilinePrintableName(info *NodeInfo) string {
|
||||
infoCopy := *info
|
||||
infoCopy.Name = escapeForDot(ShortenFunctionName(infoCopy.Name))
|
||||
infoCopy.Name = strings.Replace(infoCopy.Name, "::", `\n`, -1)
|
||||
// Go type parameters are reported as "[...]" by Go pprof profiles.
|
||||
// Keep this ellipsis rather than replacing with newlines below.
|
||||
infoCopy.Name = strings.Replace(infoCopy.Name, "[...]", "[…]", -1)
|
||||
infoCopy.Name = strings.Replace(infoCopy.Name, ".", `\n`, -1)
|
||||
if infoCopy.File != "" {
|
||||
infoCopy.File = filepath.Base(infoCopy.File)
|
||||
|
||||
61
src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go
generated
vendored
61
src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go
generated
vendored
@@ -205,49 +205,64 @@ func Demangle(prof *profile.Profile, force bool, demanglerMode string) {
|
||||
}
|
||||
}
|
||||
|
||||
var options []demangle.Option
|
||||
options := demanglerModeToOptions(demanglerMode)
|
||||
for _, fn := range prof.Function {
|
||||
demangleSingleFunction(fn, options)
|
||||
}
|
||||
}
|
||||
|
||||
func demanglerModeToOptions(demanglerMode string) []demangle.Option {
|
||||
switch demanglerMode {
|
||||
case "": // demangled, simplified: no parameters, no templates, no return type
|
||||
options = []demangle.Option{demangle.NoParams, demangle.NoTemplateParams}
|
||||
return []demangle.Option{demangle.NoParams, demangle.NoTemplateParams}
|
||||
case "templates": // demangled, simplified: no parameters, no return type
|
||||
options = []demangle.Option{demangle.NoParams}
|
||||
return []demangle.Option{demangle.NoParams}
|
||||
case "full":
|
||||
options = []demangle.Option{demangle.NoClones}
|
||||
return []demangle.Option{demangle.NoClones}
|
||||
case "none": // no demangling
|
||||
return
|
||||
return []demangle.Option{}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unknown demanglerMode %s", demanglerMode))
|
||||
}
|
||||
|
||||
func demangleSingleFunction(fn *profile.Function, options []demangle.Option) {
|
||||
if fn.Name != "" && fn.SystemName != fn.Name {
|
||||
return // Already demangled.
|
||||
}
|
||||
// Copy the options because they may be updated by the call.
|
||||
o := make([]demangle.Option, len(options))
|
||||
for _, fn := range prof.Function {
|
||||
if fn.Name != "" && fn.SystemName != fn.Name {
|
||||
continue // Already demangled.
|
||||
}
|
||||
copy(o, options)
|
||||
if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
|
||||
fn.Name = demangled
|
||||
continue
|
||||
}
|
||||
// Could not demangle. Apply heuristics in case the name is
|
||||
// already demangled.
|
||||
name := fn.SystemName
|
||||
if looksLikeDemangledCPlusPlus(name) {
|
||||
if demanglerMode == "" || demanglerMode == "templates" {
|
||||
copy(o, options)
|
||||
if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
|
||||
fn.Name = demangled
|
||||
return
|
||||
}
|
||||
// Could not demangle. Apply heuristics in case the name is
|
||||
// already demangled.
|
||||
name := fn.SystemName
|
||||
if looksLikeDemangledCPlusPlus(name) {
|
||||
for _, o := range options {
|
||||
switch o {
|
||||
case demangle.NoParams:
|
||||
name = removeMatching(name, '(', ')')
|
||||
}
|
||||
if demanglerMode == "" {
|
||||
case demangle.NoTemplateParams:
|
||||
name = removeMatching(name, '<', '>')
|
||||
}
|
||||
}
|
||||
fn.Name = name
|
||||
}
|
||||
fn.Name = name
|
||||
}
|
||||
|
||||
// looksLikeDemangledCPlusPlus is a heuristic to decide if a name is
|
||||
// the result of demangling C++. If so, further heuristics will be
|
||||
// applied to simplify the name.
|
||||
func looksLikeDemangledCPlusPlus(demangled string) bool {
|
||||
if strings.Contains(demangled, ".<") { // Skip java names of the form "class.<init>"
|
||||
// Skip java names of the form "class.<init>".
|
||||
if strings.Contains(demangled, ".<") {
|
||||
return false
|
||||
}
|
||||
// Skip Go names of the form "foo.(*Bar[...]).Method".
|
||||
if strings.Contains(demangled, "]).") {
|
||||
return false
|
||||
}
|
||||
return strings.ContainsAny(demangled, "<>[]") || strings.Contains(demangled, "::")
|
||||
|
||||
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
@@ -1,4 +1,4 @@
|
||||
# github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0
|
||||
# github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1
|
||||
## explicit; go 1.17
|
||||
github.com/google/pprof/driver
|
||||
github.com/google/pprof/internal/binutils
|
||||
|
||||
@@ -240,7 +240,7 @@ func (hs *serverHandshakeState) processClientHello() error {
|
||||
|
||||
hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
|
||||
|
||||
if hs.ecdheOk {
|
||||
if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
|
||||
// Although omitting the ec_point_formats extension is permitted, some
|
||||
// old OpenSSL version will refuse to handshake if not present.
|
||||
//
|
||||
@@ -321,6 +321,13 @@ func supportsECDHE(c *Config, supportedCurves []CurveID, supportedPoints []uint8
|
||||
break
|
||||
}
|
||||
}
|
||||
// Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is
|
||||
// missing, uncompressed points are supported. If supportedPoints is empty,
|
||||
// the extension must be missing, as an empty extension body is rejected by
|
||||
// the parser. See https://go.dev/issue/49126.
|
||||
if len(supportedPoints) == 0 {
|
||||
supportsPointFormat = true
|
||||
}
|
||||
|
||||
return supportsCurve && supportsPointFormat
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ func TestTLS12OnlyCipherSuites(t *testing.T) {
|
||||
|
||||
func TestTLSPointFormats(t *testing.T) {
|
||||
// Test that a Server returns the ec_point_format extension when ECC is
|
||||
// negotiated, and not returned on RSA handshake.
|
||||
// negotiated, and not on a RSA handshake or if ec_point_format is missing.
|
||||
tests := []struct {
|
||||
name string
|
||||
cipherSuites []uint16
|
||||
@@ -289,8 +289,11 @@ func TestTLSPointFormats(t *testing.T) {
|
||||
supportedPoints []uint8
|
||||
wantSupportedPoints bool
|
||||
}{
|
||||
{"ECC", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{compressionNone}, true},
|
||||
{"ECC", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{pointFormatUncompressed}, true},
|
||||
{"ECC without ec_point_format", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, nil, false},
|
||||
{"ECC with extra values", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{13, 37, pointFormatUncompressed, 42}, true},
|
||||
{"RSA", []uint16{TLS_RSA_WITH_AES_256_GCM_SHA384}, nil, nil, false},
|
||||
{"RSA with ec_point_format", []uint16{TLS_RSA_WITH_AES_256_GCM_SHA384}, nil, []uint8{pointFormatUncompressed}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -330,18 +333,8 @@ func TestTLSPointFormats(t *testing.T) {
|
||||
t.Fatalf("didn't get ServerHello message in reply. Got %v\n", reply)
|
||||
}
|
||||
if tt.wantSupportedPoints {
|
||||
if len(serverHello.supportedPoints) < 1 {
|
||||
t.Fatal("missing ec_point_format extension from server")
|
||||
}
|
||||
found := false
|
||||
for _, p := range serverHello.supportedPoints {
|
||||
if p == pointFormatUncompressed {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("missing uncompressed format in ec_point_format extension from server")
|
||||
if !bytes.Equal(serverHello.supportedPoints, []uint8{pointFormatUncompressed}) {
|
||||
t.Fatal("incorrect ec_point_format extension from server")
|
||||
}
|
||||
} else {
|
||||
if len(serverHello.supportedPoints) != 0 {
|
||||
|
||||
@@ -54,6 +54,9 @@ func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
||||
// marshalECPrivateKey marshals an EC private key into ASN.1, DER format and
|
||||
// sets the curve ID to the given OID, or omits it if OID is nil.
|
||||
func marshalECPrivateKeyWithOID(key *ecdsa.PrivateKey, oid asn1.ObjectIdentifier) ([]byte, error) {
|
||||
if !key.Curve.IsOnCurve(key.X, key.Y) {
|
||||
return nil, errors.New("invalid elliptic key public key")
|
||||
}
|
||||
privateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8)
|
||||
return asn1.Marshal(ecPrivateKey{
|
||||
Version: 1,
|
||||
|
||||
@@ -84,11 +84,14 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
|
||||
// RFC 3279, Section 2.3.1.
|
||||
publicKeyAlgorithm.Parameters = asn1.NullRawValue
|
||||
case *ecdsa.PublicKey:
|
||||
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
oid, ok := oidFromNamedCurve(pub.Curve)
|
||||
if !ok {
|
||||
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve")
|
||||
}
|
||||
if !pub.Curve.IsOnCurve(pub.X, pub.Y) {
|
||||
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: invalid elliptic curve public key")
|
||||
}
|
||||
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
|
||||
var paramBytes []byte
|
||||
paramBytes, err = asn1.Marshal(oid)
|
||||
|
||||
@@ -68,6 +68,20 @@ func TestPKCS1MismatchPublicKeyFormat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalInvalidPublicKey(t *testing.T) {
|
||||
_, err := MarshalPKIXPublicKey(&ecdsa.PublicKey{})
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got MarshalPKIXPublicKey success")
|
||||
}
|
||||
_, err = MarshalPKIXPublicKey(&ecdsa.PublicKey{
|
||||
Curve: elliptic.P256(),
|
||||
X: big.NewInt(1), Y: big.NewInt(2),
|
||||
})
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got MarshalPKIXPublicKey success")
|
||||
}
|
||||
}
|
||||
|
||||
func testParsePKIXPublicKey(t *testing.T, pemBytes string) (pub any) {
|
||||
block, _ := pem.Decode([]byte(pemBytes))
|
||||
pub, err := ParsePKIXPublicKey(block.Bytes)
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.19
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8
|
||||
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87
|
||||
golang.org/x/net v0.0.0-20220907013720-d52c520e3766
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8 h1:y+mHpWoQJNAHt26Nhh6JP7hvM71IRZureyvZhoVALIs=
|
||||
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87 h1:cCR+9mKLOGyX4Zx+uBZDXEDAQsvKQ/XbW4vreG5v1jU=
|
||||
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220907013720-d52c520e3766 h1:D02YdIT3M6OQkZXTQiO761u/SmR3DDDiDXLN2oZIUac=
|
||||
golang.org/x/net v0.0.0-20220907013720-d52c520e3766/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 h1:PgOr27OhUx2IRqGJ2RxAWI4dJQ7bi9cSrB82uzFzfUA=
|
||||
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.8-0.20220509174342-b4bca84b0361 h1:h+pU/hCb7sEApigI6eII3/Emx5ZHaFWS+nulUp0Az/k=
|
||||
|
||||
@@ -33,7 +33,8 @@ var knownOS = map[string]bool{
|
||||
|
||||
// unixOS is the set of GOOS values matched by the "unix" build tag.
|
||||
// This is not used for filename matching.
|
||||
// This list also appears in cmd/dist/build.go.
|
||||
// This list also appears in cmd/dist/build.go and
|
||||
// cmd/go/internal/imports/build.go.
|
||||
var unixOS = map[string]bool{
|
||||
"aix": true,
|
||||
"android": true,
|
||||
|
||||
@@ -664,3 +664,40 @@ func TestIssue50646(t *testing.T) {
|
||||
t.Errorf("comparable not assignable to any")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue55030(t *testing.T) {
|
||||
// makeSig makes the signature func(typ...)
|
||||
makeSig := func(typ Type) {
|
||||
par := NewVar(token.NoPos, nil, "", typ)
|
||||
params := NewTuple(par)
|
||||
NewSignatureType(nil, nil, nil, params, nil, true)
|
||||
}
|
||||
|
||||
// makeSig must not panic for the following (example) types:
|
||||
// []int
|
||||
makeSig(NewSlice(Typ[Int]))
|
||||
|
||||
// string
|
||||
makeSig(Typ[String])
|
||||
|
||||
// P where P's core type is string
|
||||
{
|
||||
P := NewTypeName(token.NoPos, nil, "P", nil) // [P string]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})))
|
||||
}
|
||||
|
||||
// P where P's core type is an (unnamed) slice
|
||||
{
|
||||
P := NewTypeName(token.NoPos, nil, "P", nil) // [P []int]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})))
|
||||
}
|
||||
|
||||
// P where P's core type is bytestring (i.e., string or []byte)
|
||||
{
|
||||
t1 := NewTerm(true, Typ[String]) // ~string
|
||||
t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
|
||||
u := NewUnion([]*Term{t1, t2}) // ~string | []byte
|
||||
P := NewTypeName(token.NoPos, nil, "P", nil) // [P ~string | []byte]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
@@ -41,16 +42,18 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
|
||||
// NewSignatureType creates a new function type for the given receiver,
|
||||
// receiver type parameters, type parameters, parameters, and results. If
|
||||
// variadic is set, params must hold at least one parameter and the last
|
||||
// parameter must be of unnamed slice type. If recv is non-nil, typeParams must
|
||||
// be empty. If recvTypeParams is non-empty, recv must be non-nil.
|
||||
// parameter's core type must be of unnamed slice or bytestring type.
|
||||
// If recv is non-nil, typeParams must be empty. If recvTypeParams is
|
||||
// non-empty, recv must be non-nil.
|
||||
func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
|
||||
if variadic {
|
||||
n := params.Len()
|
||||
if n == 0 {
|
||||
panic("variadic function must have at least one parameter")
|
||||
}
|
||||
if _, ok := params.At(n - 1).typ.(*Slice); !ok {
|
||||
panic("variadic parameter must be of unnamed slice type")
|
||||
core := coreString(params.At(n - 1).typ)
|
||||
if _, ok := core.(*Slice); !ok && !isString(core) {
|
||||
panic(fmt.Sprintf("got %s, want variadic parameter with unnamed slice type or string as core type", core.String()))
|
||||
}
|
||||
}
|
||||
sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
|
||||
|
||||
@@ -21,15 +21,15 @@ import _ "unsafe" // for go:linkname
|
||||
//go:linkname libfuzzerHookStrCmp runtime.libfuzzerHookStrCmp
|
||||
//go:linkname libfuzzerHookEqualFold runtime.libfuzzerHookEqualFold
|
||||
|
||||
func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC int) {}
|
||||
func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC int) {}
|
||||
func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC int) {}
|
||||
func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC int) {}
|
||||
func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC uint) {}
|
||||
func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC uint) {}
|
||||
func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC uint) {}
|
||||
func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC uint) {}
|
||||
|
||||
func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC int) {}
|
||||
func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC int) {}
|
||||
func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC int) {}
|
||||
func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC int) {}
|
||||
func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC uint) {}
|
||||
func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC uint) {}
|
||||
func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC uint) {}
|
||||
func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC uint) {}
|
||||
|
||||
func libfuzzerHookStrCmp(arg0, arg1 string, fakePC int) {}
|
||||
func libfuzzerHookEqualFold(arg0, arg1 string, fakePC int) {}
|
||||
func libfuzzerHookStrCmp(arg0, arg1 string, fakePC uint) {}
|
||||
func libfuzzerHookEqualFold(arg0, arg1 string, fakePC uint) {}
|
||||
|
||||
@@ -5058,6 +5058,9 @@ func (sc *http2serverConn) startGracefulShutdownInternal() {
|
||||
func (sc *http2serverConn) goAway(code http2ErrCode) {
|
||||
sc.serveG.check()
|
||||
if sc.inGoAway {
|
||||
if sc.goAwayCode == http2ErrCodeNo {
|
||||
sc.goAwayCode = code
|
||||
}
|
||||
return
|
||||
}
|
||||
sc.inGoAway = true
|
||||
|
||||
@@ -261,6 +261,9 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
p.Director(outreq)
|
||||
if outreq.Form != nil {
|
||||
outreq.URL.RawQuery = cleanQueryParams(outreq.URL.RawQuery)
|
||||
}
|
||||
outreq.Close = false
|
||||
|
||||
reqUpType := upgradeType(outreq.Header)
|
||||
@@ -639,3 +642,36 @@ func (c switchProtocolCopier) copyToBackend(errc chan<- error) {
|
||||
_, err := io.Copy(c.backend, c.user)
|
||||
errc <- err
|
||||
}
|
||||
|
||||
func cleanQueryParams(s string) string {
|
||||
reencode := func(s string) string {
|
||||
v, _ := url.ParseQuery(s)
|
||||
return v.Encode()
|
||||
}
|
||||
for i := 0; i < len(s); {
|
||||
switch s[i] {
|
||||
case ';':
|
||||
return reencode(s)
|
||||
case '%':
|
||||
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
|
||||
return reencode(s)
|
||||
}
|
||||
i += 3
|
||||
default:
|
||||
i++
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func ishex(c byte) bool {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return true
|
||||
case 'a' <= c && c <= 'f':
|
||||
return true
|
||||
case 'A' <= c && c <= 'F':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1537,3 +1537,77 @@ func TestJoinURLPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
testWantsCleanQuery = true
|
||||
testWantsRawQuery = false
|
||||
)
|
||||
|
||||
func TestReverseProxyQueryParameterSmugglingDirectorDoesNotParseForm(t *testing.T) {
|
||||
testReverseProxyQueryParameterSmuggling(t, testWantsRawQuery, func(u *url.URL) *ReverseProxy {
|
||||
proxyHandler := NewSingleHostReverseProxy(u)
|
||||
oldDirector := proxyHandler.Director
|
||||
proxyHandler.Director = func(r *http.Request) {
|
||||
oldDirector(r)
|
||||
}
|
||||
return proxyHandler
|
||||
})
|
||||
}
|
||||
|
||||
func TestReverseProxyQueryParameterSmugglingDirectorParsesForm(t *testing.T) {
|
||||
testReverseProxyQueryParameterSmuggling(t, testWantsCleanQuery, func(u *url.URL) *ReverseProxy {
|
||||
proxyHandler := NewSingleHostReverseProxy(u)
|
||||
oldDirector := proxyHandler.Director
|
||||
proxyHandler.Director = func(r *http.Request) {
|
||||
// Parsing the form causes ReverseProxy to remove unparsable
|
||||
// query parameters before forwarding.
|
||||
r.FormValue("a")
|
||||
oldDirector(r)
|
||||
}
|
||||
return proxyHandler
|
||||
})
|
||||
}
|
||||
|
||||
func testReverseProxyQueryParameterSmuggling(t *testing.T, wantCleanQuery bool, newProxy func(*url.URL) *ReverseProxy) {
|
||||
const content = "response_content"
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(r.URL.RawQuery))
|
||||
}))
|
||||
defer backend.Close()
|
||||
backendURL, err := url.Parse(backend.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxyHandler := newProxy(backendURL)
|
||||
frontend := httptest.NewServer(proxyHandler)
|
||||
defer frontend.Close()
|
||||
|
||||
// Don't spam output with logs of queries containing semicolons.
|
||||
backend.Config.ErrorLog = log.New(io.Discard, "", 0)
|
||||
frontend.Config.ErrorLog = log.New(io.Discard, "", 0)
|
||||
|
||||
for _, test := range []struct {
|
||||
rawQuery string
|
||||
cleanQuery string
|
||||
}{{
|
||||
rawQuery: "a=1&a=2;b=3",
|
||||
cleanQuery: "a=1",
|
||||
}, {
|
||||
rawQuery: "a=1&a=%zz&b=3",
|
||||
cleanQuery: "a=1&b=3",
|
||||
}} {
|
||||
res, err := frontend.Client().Get(frontend.URL + "?" + test.rawQuery)
|
||||
if err != nil {
|
||||
t.Fatalf("Get: %v", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
wantQuery := test.rawQuery
|
||||
if wantCleanQuery {
|
||||
wantQuery = test.cleanQuery
|
||||
}
|
||||
if got, want := string(body), wantQuery; got != want {
|
||||
t.Errorf("proxy forwarded raw query %q as %q, want %q", test.rawQuery, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,17 +1191,23 @@ func (u *URL) UnmarshalBinary(text []byte) error {
|
||||
// any existing path and the resulting path cleaned of any ./ or ../ elements.
|
||||
// Any sequences of multiple / characters will be reduced to a single /.
|
||||
func (u *URL) JoinPath(elem ...string) *URL {
|
||||
url := *u
|
||||
if len(elem) > 0 {
|
||||
elem = append([]string{u.EscapedPath()}, elem...)
|
||||
p := path.Join(elem...)
|
||||
// path.Join will remove any trailing slashes.
|
||||
// Preserve at least one.
|
||||
if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") {
|
||||
p += "/"
|
||||
}
|
||||
url.setPath(p)
|
||||
elem = append([]string{u.EscapedPath()}, elem...)
|
||||
var p string
|
||||
if !strings.HasPrefix(elem[0], "/") {
|
||||
// Return a relative path if u is relative,
|
||||
// but ensure that it contains no ../ elements.
|
||||
elem[0] = "/" + elem[0]
|
||||
p = path.Join(elem...)[1:]
|
||||
} else {
|
||||
p = path.Join(elem...)
|
||||
}
|
||||
// path.Join will remove any trailing slashes.
|
||||
// Preserve at least one.
|
||||
if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") {
|
||||
p += "/"
|
||||
}
|
||||
url := *u
|
||||
url.setPath(p)
|
||||
return &url
|
||||
}
|
||||
|
||||
|
||||
@@ -2080,6 +2080,26 @@ func TestJoinPath(t *testing.T) {
|
||||
elem: []string{"../../../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/",
|
||||
elem: []string{"../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com",
|
||||
elem: []string{"../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com",
|
||||
elem: []string{"../go", "../../go", "../../../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/../go",
|
||||
elem: nil,
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/",
|
||||
elem: []string{"./go"},
|
||||
@@ -2112,7 +2132,7 @@ func TestJoinPath(t *testing.T) {
|
||||
{
|
||||
base: "https://go.googlesource.com",
|
||||
elem: nil,
|
||||
out: "https://go.googlesource.com",
|
||||
out: "https://go.googlesource.com/",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/",
|
||||
@@ -2129,11 +2149,46 @@ func TestJoinPath(t *testing.T) {
|
||||
elem: []string{"c%2fd"},
|
||||
out: "https://go.googlesource.com/a%2fb/c%2fd",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/a/b",
|
||||
elem: []string{"/go"},
|
||||
out: "https://go.googlesource.com/a/b/go",
|
||||
},
|
||||
{
|
||||
base: "/",
|
||||
elem: nil,
|
||||
out: "/",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: nil,
|
||||
out: "a",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: []string{"b"},
|
||||
out: "a/b",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: []string{"../b"},
|
||||
out: "b",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: []string{"../../b"},
|
||||
out: "b",
|
||||
},
|
||||
{
|
||||
base: "",
|
||||
elem: []string{"a"},
|
||||
out: "a",
|
||||
},
|
||||
{
|
||||
base: "",
|
||||
elem: []string{"../a"},
|
||||
out: "a",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
wantErr := "nil"
|
||||
|
||||
@@ -11,9 +11,10 @@ import (
|
||||
|
||||
func TestDedupEnv(t *testing.T) {
|
||||
tests := []struct {
|
||||
noCase bool
|
||||
in []string
|
||||
want []string
|
||||
noCase bool
|
||||
in []string
|
||||
want []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
noCase: true,
|
||||
@@ -41,11 +42,17 @@ func TestDedupEnv(t *testing.T) {
|
||||
in: []string{"dodgy", "entries"},
|
||||
want: []string{"dodgy", "entries"},
|
||||
},
|
||||
{
|
||||
// Filter out entries containing NULs.
|
||||
in: []string{"A=a\x00b", "B=b", "C\x00C=c"},
|
||||
want: []string{"B=b"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := dedupEnvCase(tt.noCase, tt.in)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Dedup(%v, %q) = %q; want %q", tt.noCase, tt.in, got, tt.want)
|
||||
got, err := dedupEnvCase(tt.noCase, tt.in)
|
||||
if !reflect.DeepEqual(got, tt.want) || (err != nil) != tt.wantErr {
|
||||
t.Errorf("Dedup(%v, %q) = %q, %v; want %q, error:%v", tt.noCase, tt.in, got, err, tt.want, tt.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,7 +912,11 @@ func (c *Cmd) environ() ([]string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return addCriticalEnv(dedupEnv(env)), err
|
||||
env, dedupErr := dedupEnv(env)
|
||||
if err == nil {
|
||||
err = dedupErr
|
||||
}
|
||||
return addCriticalEnv(env), err
|
||||
}
|
||||
|
||||
// Environ returns a copy of the environment in which the command would be run
|
||||
@@ -926,20 +930,27 @@ func (c *Cmd) Environ() []string {
|
||||
// dedupEnv returns a copy of env with any duplicates removed, in favor of
|
||||
// later values.
|
||||
// Items not of the normal environment "key=value" form are preserved unchanged.
|
||||
func dedupEnv(env []string) []string {
|
||||
// Items containing NUL characters are removed, and an error is returned along with
|
||||
// the remaining values.
|
||||
func dedupEnv(env []string) ([]string, error) {
|
||||
return dedupEnvCase(runtime.GOOS == "windows", env)
|
||||
}
|
||||
|
||||
// dedupEnvCase is dedupEnv with a case option for testing.
|
||||
// If caseInsensitive is true, the case of keys is ignored.
|
||||
func dedupEnvCase(caseInsensitive bool, env []string) []string {
|
||||
func dedupEnvCase(caseInsensitive bool, env []string) ([]string, error) {
|
||||
// Construct the output in reverse order, to preserve the
|
||||
// last occurrence of each key.
|
||||
var err error
|
||||
out := make([]string, 0, len(env))
|
||||
saw := make(map[string]bool, len(env))
|
||||
for n := len(env); n > 0; n-- {
|
||||
kv := env[n-1]
|
||||
|
||||
if strings.IndexByte(kv, 0) != -1 {
|
||||
err = errors.New("exec: environment variable contains NUL")
|
||||
continue
|
||||
}
|
||||
i := strings.Index(kv, "=")
|
||||
if i == 0 {
|
||||
// We observe in practice keys with a single leading "=" on Windows.
|
||||
@@ -974,7 +985,7 @@ func dedupEnvCase(caseInsensitive bool, env []string) []string {
|
||||
out[i], out[j] = out[j], out[i]
|
||||
}
|
||||
|
||||
return out
|
||||
return out, err
|
||||
}
|
||||
|
||||
// addCriticalEnv adds any critical environment variables that are required
|
||||
|
||||
@@ -1053,6 +1053,15 @@ func TestDedupEnvEcho(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvNULCharacter(t *testing.T) {
|
||||
cmd := helperCommand(t, "echoenv", "FOO", "BAR")
|
||||
cmd.Env = append(cmd.Environ(), "FOO=foo\x00BAR=bar")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Errorf("output = %q; want error", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
echoPath, err := exec.LookPath("echo")
|
||||
if err != nil {
|
||||
|
||||
@@ -91,15 +91,49 @@ const (
|
||||
// until we've allocated at least maxHeight Regexp structures.
|
||||
const maxHeight = 1000
|
||||
|
||||
// maxSize is the maximum size of a compiled regexp in Insts.
|
||||
// It too is somewhat arbitrarily chosen, but the idea is to be large enough
|
||||
// to allow significant regexps while at the same time small enough that
|
||||
// the compiled form will not take up too much memory.
|
||||
// 128 MB is enough for a 3.3 million Inst structures, which roughly
|
||||
// corresponds to a 3.3 MB regexp.
|
||||
const (
|
||||
maxSize = 128 << 20 / instSize
|
||||
instSize = 5 * 8 // byte, 2 uint32, slice is 5 64-bit words
|
||||
)
|
||||
|
||||
// maxRunes is the maximum number of runes allowed in a regexp tree
|
||||
// counting the runes in all the nodes.
|
||||
// Ignoring character classes p.numRunes is always less than the length of the regexp.
|
||||
// Character classes can make it much larger: each \pL adds 1292 runes.
|
||||
// 128 MB is enough for 32M runes, which is over 26k \pL instances.
|
||||
// Note that repetitions do not make copies of the rune slices,
|
||||
// so \pL{1000} is only one rune slice, not 1000.
|
||||
// We could keep a cache of character classes we've seen,
|
||||
// so that all the \pL we see use the same rune list,
|
||||
// but that doesn't remove the problem entirely:
|
||||
// consider something like [\pL01234][\pL01235][\pL01236]...[\pL^&*()].
|
||||
// And because the Rune slice is exposed directly in the Regexp,
|
||||
// there is not an opportunity to change the representation to allow
|
||||
// partial sharing between different character classes.
|
||||
// So the limit is the best we can do.
|
||||
const (
|
||||
maxRunes = 128 << 20 / runeSize
|
||||
runeSize = 4 // rune is int32
|
||||
)
|
||||
|
||||
type parser struct {
|
||||
flags Flags // parse mode flags
|
||||
stack []*Regexp // stack of parsed expressions
|
||||
free *Regexp
|
||||
numCap int // number of capturing groups seen
|
||||
wholeRegexp string
|
||||
tmpClass []rune // temporary char class work space
|
||||
numRegexp int // number of regexps allocated
|
||||
height map[*Regexp]int // regexp height for height limit check
|
||||
tmpClass []rune // temporary char class work space
|
||||
numRegexp int // number of regexps allocated
|
||||
numRunes int // number of runes in char classes
|
||||
repeats int64 // product of all repetitions seen
|
||||
height map[*Regexp]int // regexp height, for height limit check
|
||||
size map[*Regexp]int64 // regexp compiled size, for size limit check
|
||||
}
|
||||
|
||||
func (p *parser) newRegexp(op Op) *Regexp {
|
||||
@@ -123,6 +157,104 @@ func (p *parser) reuse(re *Regexp) {
|
||||
p.free = re
|
||||
}
|
||||
|
||||
func (p *parser) checkLimits(re *Regexp) {
|
||||
if p.numRunes > maxRunes {
|
||||
panic(ErrInternalError)
|
||||
}
|
||||
p.checkSize(re)
|
||||
p.checkHeight(re)
|
||||
}
|
||||
|
||||
func (p *parser) checkSize(re *Regexp) {
|
||||
if p.size == nil {
|
||||
// We haven't started tracking size yet.
|
||||
// Do a relatively cheap check to see if we need to start.
|
||||
// Maintain the product of all the repeats we've seen
|
||||
// and don't track if the total number of regexp nodes
|
||||
// we've seen times the repeat product is in budget.
|
||||
if p.repeats == 0 {
|
||||
p.repeats = 1
|
||||
}
|
||||
if re.Op == OpRepeat {
|
||||
n := re.Max
|
||||
if n == -1 {
|
||||
n = re.Min
|
||||
}
|
||||
if n <= 0 {
|
||||
n = 1
|
||||
}
|
||||
if int64(n) > maxSize/p.repeats {
|
||||
p.repeats = maxSize
|
||||
} else {
|
||||
p.repeats *= int64(n)
|
||||
}
|
||||
}
|
||||
if int64(p.numRegexp) < maxSize/p.repeats {
|
||||
return
|
||||
}
|
||||
|
||||
// We need to start tracking size.
|
||||
// Make the map and belatedly populate it
|
||||
// with info about everything we've constructed so far.
|
||||
p.size = make(map[*Regexp]int64)
|
||||
for _, re := range p.stack {
|
||||
p.checkSize(re)
|
||||
}
|
||||
}
|
||||
|
||||
if p.calcSize(re, true) > maxSize {
|
||||
panic(ErrInternalError)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) calcSize(re *Regexp, force bool) int64 {
|
||||
if !force {
|
||||
if size, ok := p.size[re]; ok {
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
var size int64
|
||||
switch re.Op {
|
||||
case OpLiteral:
|
||||
size = int64(len(re.Rune))
|
||||
case OpCapture, OpStar:
|
||||
// star can be 1+ or 2+; assume 2 pessimistically
|
||||
size = 2 + p.calcSize(re.Sub[0], false)
|
||||
case OpPlus, OpQuest:
|
||||
size = 1 + p.calcSize(re.Sub[0], false)
|
||||
case OpConcat:
|
||||
for _, sub := range re.Sub {
|
||||
size += p.calcSize(sub, false)
|
||||
}
|
||||
case OpAlternate:
|
||||
for _, sub := range re.Sub {
|
||||
size += p.calcSize(sub, false)
|
||||
}
|
||||
if len(re.Sub) > 1 {
|
||||
size += int64(len(re.Sub)) - 1
|
||||
}
|
||||
case OpRepeat:
|
||||
sub := p.calcSize(re.Sub[0], false)
|
||||
if re.Max == -1 {
|
||||
if re.Min == 0 {
|
||||
size = 2 + sub // x*
|
||||
} else {
|
||||
size = 1 + int64(re.Min)*sub // xxx+
|
||||
}
|
||||
break
|
||||
}
|
||||
// x{2,5} = xx(x(x(x)?)?)?
|
||||
size = int64(re.Max)*sub + int64(re.Max-re.Min)
|
||||
}
|
||||
|
||||
if size < 1 {
|
||||
size = 1
|
||||
}
|
||||
p.size[re] = size
|
||||
return size
|
||||
}
|
||||
|
||||
func (p *parser) checkHeight(re *Regexp) {
|
||||
if p.numRegexp < maxHeight {
|
||||
return
|
||||
@@ -159,6 +291,7 @@ func (p *parser) calcHeight(re *Regexp, force bool) int {
|
||||
|
||||
// push pushes the regexp re onto the parse stack and returns the regexp.
|
||||
func (p *parser) push(re *Regexp) *Regexp {
|
||||
p.numRunes += len(re.Rune)
|
||||
if re.Op == OpCharClass && len(re.Rune) == 2 && re.Rune[0] == re.Rune[1] {
|
||||
// Single rune.
|
||||
if p.maybeConcat(re.Rune[0], p.flags&^FoldCase) {
|
||||
@@ -190,7 +323,7 @@ func (p *parser) push(re *Regexp) *Regexp {
|
||||
}
|
||||
|
||||
p.stack = append(p.stack, re)
|
||||
p.checkHeight(re)
|
||||
p.checkLimits(re)
|
||||
return re
|
||||
}
|
||||
|
||||
@@ -300,7 +433,7 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) (
|
||||
re.Sub = re.Sub0[:1]
|
||||
re.Sub[0] = sub
|
||||
p.stack[n-1] = re
|
||||
p.checkHeight(re)
|
||||
p.checkLimits(re)
|
||||
|
||||
if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) {
|
||||
return "", &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]}
|
||||
@@ -508,6 +641,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp {
|
||||
|
||||
for j := start; j < i; j++ {
|
||||
sub[j] = p.removeLeadingString(sub[j], len(str))
|
||||
p.checkLimits(sub[j])
|
||||
}
|
||||
suffix := p.collapse(sub[start:i], OpAlternate) // recurse
|
||||
|
||||
@@ -565,6 +699,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp {
|
||||
for j := start; j < i; j++ {
|
||||
reuse := j != start // prefix came from sub[start]
|
||||
sub[j] = p.removeLeadingRegexp(sub[j], reuse)
|
||||
p.checkLimits(sub[j])
|
||||
}
|
||||
suffix := p.collapse(sub[start:i], OpAlternate) // recurse
|
||||
|
||||
@@ -762,6 +897,8 @@ func parse(s string, flags Flags) (_ *Regexp, err error) {
|
||||
panic(r)
|
||||
case nil:
|
||||
// ok
|
||||
case ErrInternalError: // too big
|
||||
err = &Error{Code: ErrInternalError, Expr: s}
|
||||
case ErrNestingDepth:
|
||||
err = &Error{Code: ErrNestingDepth, Expr: s}
|
||||
}
|
||||
|
||||
@@ -484,12 +484,15 @@ var invalidRegexps = []string{
|
||||
`(?P<>a)`,
|
||||
`[a-Z]`,
|
||||
`(?i)[a-Z]`,
|
||||
`a{100000}`,
|
||||
`a{100000,}`,
|
||||
"((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})",
|
||||
strings.Repeat("(", 1000) + strings.Repeat(")", 1000),
|
||||
strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000),
|
||||
`\Q\E*`,
|
||||
`a{100000}`, // too much repetition
|
||||
`a{100000,}`, // too much repetition
|
||||
"((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", // too much repetition
|
||||
strings.Repeat("(", 1000) + strings.Repeat(")", 1000), // too deep
|
||||
strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), // too deep
|
||||
"(" + strings.Repeat("(xx?)", 1000) + "){1000}", // too long
|
||||
strings.Repeat("(xx?){1000}", 1000), // too long
|
||||
strings.Repeat(`\pL`, 27000), // too many runes
|
||||
}
|
||||
|
||||
var onlyPerl = []string{
|
||||
|
||||
@@ -387,6 +387,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
RET
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R3), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVW R13, R13
|
||||
|
||||
MOVW $0, R7
|
||||
B runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -320,6 +320,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R3), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVD RSP, RSP
|
||||
|
||||
MOVW $0, R26
|
||||
B runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -258,6 +258,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R3), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVV R29, R29
|
||||
|
||||
MOVV R0, REGCTXT
|
||||
JMP runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -257,6 +257,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R3), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVW R29, R29
|
||||
|
||||
MOVW R0, REGCTXT
|
||||
JMP runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -334,6 +334,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R5), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVD R1, R1
|
||||
|
||||
MOVD R0, R11
|
||||
BR runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -158,8 +158,8 @@ TEXT runtime·getcallerpc(SB),NOSPLIT|NOFRAME,$0-8
|
||||
*/
|
||||
|
||||
// Called during function prolog when more stack is needed.
|
||||
// Caller has already loaded:
|
||||
// R1: framesize, R2: argsize, R3: LR
|
||||
// Called with return address (i.e. caller's PC) in X5 (aka T0),
|
||||
// and the LR register contains the caller's LR.
|
||||
//
|
||||
// The traceback routines see morestack on a g0 as being
|
||||
// the top of a stack (for example, morestack calling newstack
|
||||
@@ -209,6 +209,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
|
||||
// func morestack_noctxt()
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register, and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOV X2, X2
|
||||
|
||||
MOV ZERO, CTXT
|
||||
JMP runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -346,6 +346,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R5), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVD R15, R15
|
||||
|
||||
MOVD $0, R12
|
||||
BR runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -23,7 +23,9 @@ package cgo
|
||||
#cgo solaris LDFLAGS: -lxnet
|
||||
#cgo solaris LDFLAGS: -lsocket
|
||||
|
||||
#cgo CFLAGS: -Wall -Werror
|
||||
// We use -fno-stack-protector because internal linking won't find
|
||||
// the support functions. See issues #52919 and #54313.
|
||||
#cgo CFLAGS: -Wall -Werror -fno-stack-protector
|
||||
|
||||
#cgo solaris CPPFLAGS: -D_POSIX_PTHREAD_SEMANTICS
|
||||
|
||||
|
||||
@@ -20,49 +20,49 @@ const retSledSize = 512
|
||||
// This may result in these functions having callers that are nosplit. That is why they must be nosplit.
|
||||
//
|
||||
//go:nosplit
|
||||
func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC int) {
|
||||
func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC uint) {
|
||||
fakePC = fakePC % retSledSize
|
||||
libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp1, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC int) {
|
||||
func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC uint) {
|
||||
fakePC = fakePC % retSledSize
|
||||
libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp2, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC int) {
|
||||
func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC uint) {
|
||||
fakePC = fakePC % retSledSize
|
||||
libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp4, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC int) {
|
||||
func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC uint) {
|
||||
fakePC = fakePC % retSledSize
|
||||
libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp8, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC int) {
|
||||
func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC uint) {
|
||||
fakePC = fakePC % retSledSize
|
||||
libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp1, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC int) {
|
||||
func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC uint) {
|
||||
fakePC = fakePC % retSledSize
|
||||
libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp2, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC int) {
|
||||
func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC uint) {
|
||||
fakePC = fakePC % retSledSize
|
||||
libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp4, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC int) {
|
||||
func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC uint) {
|
||||
fakePC = fakePC % retSledSize
|
||||
libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp8, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package runtime
|
||||
import (
|
||||
"internal/abi"
|
||||
"internal/goarch"
|
||||
"runtime/internal/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -182,7 +183,7 @@ func newosproc(mp *m) {
|
||||
}
|
||||
}
|
||||
|
||||
func exitThread(wait *uint32) {
|
||||
func exitThread(wait *atomic.Uint32) {
|
||||
// We should never reach exitThread on Solaris because we let
|
||||
// libc clean up threads.
|
||||
throw("exitThread")
|
||||
|
||||
@@ -8,6 +8,7 @@ package runtime
|
||||
|
||||
import (
|
||||
"internal/abi"
|
||||
"runtime/internal/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -233,7 +234,7 @@ func newosproc(mp *m) {
|
||||
|
||||
}
|
||||
|
||||
func exitThread(wait *uint32) {
|
||||
func exitThread(wait *atomic.Uint32) {
|
||||
// We should never reach exitThread on AIX because we let
|
||||
// libc clean up threads.
|
||||
throw("exitThread")
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"runtime/internal/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -35,7 +36,7 @@ func usleep_no_g(usec uint32) {
|
||||
usleep(usec)
|
||||
}
|
||||
|
||||
func exitThread(wait *uint32)
|
||||
func exitThread(wait *atomic.Uint32)
|
||||
|
||||
type mOS struct{}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"runtime/internal/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -49,11 +50,11 @@ func open(name *byte, mode, perm int32) int32
|
||||
// return value is only set on linux to be used in osinit()
|
||||
func madvise(addr unsafe.Pointer, n uintptr, flags int32) int32
|
||||
|
||||
// exitThread terminates the current thread, writing *wait = 0 when
|
||||
// exitThread terminates the current thread, writing *wait = freeMStack when
|
||||
// the stack is safe to reclaim.
|
||||
//
|
||||
//go:noescape
|
||||
func exitThread(wait *uint32)
|
||||
func exitThread(wait *atomic.Uint32)
|
||||
|
||||
//go:noescape
|
||||
func obsdsigprocmask(how int32, new sigset) sigset
|
||||
|
||||
@@ -461,7 +461,7 @@ func newosproc(mp *m) {
|
||||
}
|
||||
}
|
||||
|
||||
func exitThread(wait *uint32) {
|
||||
func exitThread(wait *atomic.Uint32) {
|
||||
// We should never reach exitThread on Plan 9 because we let
|
||||
// the OS clean up threads.
|
||||
throw("exitThread")
|
||||
|
||||
@@ -941,7 +941,7 @@ func newosproc0(mp *m, stk unsafe.Pointer) {
|
||||
throw("bad newosproc0")
|
||||
}
|
||||
|
||||
func exitThread(wait *uint32) {
|
||||
func exitThread(wait *atomic.Uint32) {
|
||||
// We should never reach exitThread on Windows because we let
|
||||
// the OS clean up threads.
|
||||
throw("exitThread")
|
||||
|
||||
@@ -1516,19 +1516,18 @@ func mexit(osStack bool) {
|
||||
}
|
||||
throw("m not found in allm")
|
||||
found:
|
||||
if !osStack {
|
||||
// Delay reaping m until it's done with the stack.
|
||||
//
|
||||
// If this is using an OS stack, the OS will free it
|
||||
// so there's no need for reaping.
|
||||
atomic.Store(&m.freeWait, 1)
|
||||
// Put m on the free list, though it will not be reaped until
|
||||
// freeWait is 0. Note that the free list must not be linked
|
||||
// through alllink because some functions walk allm without
|
||||
// locking, so may be using alllink.
|
||||
m.freelink = sched.freem
|
||||
sched.freem = m
|
||||
}
|
||||
// Delay reaping m until it's done with the stack.
|
||||
//
|
||||
// Put mp on the free list, though it will not be reaped while freeWait
|
||||
// is freeMWait. mp is no longer reachable via allm, so even if it is
|
||||
// on an OS stack, we must keep a reference to mp alive so that the GC
|
||||
// doesn't free mp while we are still using it.
|
||||
//
|
||||
// Note that the free list must not be linked through alllink because
|
||||
// some functions walk allm without locking, so may be using alllink.
|
||||
m.freeWait.Store(freeMWait)
|
||||
m.freelink = sched.freem
|
||||
sched.freem = m
|
||||
unlock(&sched.lock)
|
||||
|
||||
atomic.Xadd64(&ncgocall, int64(m.ncgocall))
|
||||
@@ -1558,6 +1557,9 @@ found:
|
||||
mdestroy(m)
|
||||
|
||||
if osStack {
|
||||
// No more uses of mp, so it is safe to drop the reference.
|
||||
m.freeWait.Store(freeMRef)
|
||||
|
||||
// Return from mstart and let the system thread
|
||||
// library free the g0 stack and terminate the thread.
|
||||
return
|
||||
@@ -1729,19 +1731,25 @@ func allocm(_p_ *p, fn func(), id int64) *m {
|
||||
lock(&sched.lock)
|
||||
var newList *m
|
||||
for freem := sched.freem; freem != nil; {
|
||||
if freem.freeWait != 0 {
|
||||
wait := freem.freeWait.Load()
|
||||
if wait == freeMWait {
|
||||
next := freem.freelink
|
||||
freem.freelink = newList
|
||||
newList = freem
|
||||
freem = next
|
||||
continue
|
||||
}
|
||||
// stackfree must be on the system stack, but allocm is
|
||||
// reachable off the system stack transitively from
|
||||
// startm.
|
||||
systemstack(func() {
|
||||
stackfree(freem.g0.stack)
|
||||
})
|
||||
// Free the stack if needed. For freeMRef, there is
|
||||
// nothing to do except drop freem from the sched.freem
|
||||
// list.
|
||||
if wait == freeMStack {
|
||||
// stackfree must be on the system stack, but allocm is
|
||||
// reachable off the system stack transitively from
|
||||
// startm.
|
||||
systemstack(func() {
|
||||
stackfree(freem.g0.stack)
|
||||
})
|
||||
}
|
||||
freem = freem.freelink
|
||||
}
|
||||
sched.freem = newList
|
||||
|
||||
@@ -22,6 +22,7 @@ TEXT _main<>(SB),NOSPLIT,$-8
|
||||
// There is no TLS base pointer.
|
||||
//
|
||||
// TODO(austin): Support ABI v1 dynamic linking entry point
|
||||
XOR R0, R0 // Note, newer kernels may not always set R0 to 0.
|
||||
MOVD $runtime·rt0_go(SB), R12
|
||||
MOVD R12, CTR
|
||||
MOVBZ runtime·iscgo(SB), R5
|
||||
|
||||
@@ -516,6 +516,13 @@ const (
|
||||
tlsSize = tlsSlots * goarch.PtrSize
|
||||
)
|
||||
|
||||
// Values for m.freeWait.
|
||||
const (
|
||||
freeMStack = 0 // M done, free stack and reference.
|
||||
freeMRef = 1 // M done, free reference.
|
||||
freeMWait = 2 // M still in use.
|
||||
)
|
||||
|
||||
type m struct {
|
||||
g0 *g // goroutine with scheduling stack
|
||||
morebuf gobuf // gobuf arg to morestack
|
||||
@@ -546,7 +553,7 @@ type m struct {
|
||||
newSigstack bool // minit on C thread called sigaltstack
|
||||
printlock int8
|
||||
incgo bool // m is executing a cgo call
|
||||
freeWait uint32 // if == 0, safe to free g0 and delete m (atomic)
|
||||
freeWait atomic.Uint32 // Whether it is safe to free g0 and delete m (one of freeMRef, freeMStack, freeMWait)
|
||||
fastrand uint64
|
||||
needextram bool
|
||||
traceback uint8
|
||||
|
||||
@@ -84,6 +84,10 @@ func (c *sigctxt) fixsigcode(sig uint32) {
|
||||
// in real life, people will probably search for it and find this code.
|
||||
// There are no Google hits for b01dfacedebac1e or 0xb01dfacedebac1e
|
||||
// as I type this comment.
|
||||
//
|
||||
// Note: if this code is removed, please consider
|
||||
// enabling TestSignalForwardingGo for darwin-amd64 in
|
||||
// misc/cgo/testcarchive/carchive_test.go.
|
||||
if c.sigcode() == _SI_USER {
|
||||
c.set_sigcode(_SI_USER + 1)
|
||||
c.set_sigaddr(0xb01dfacedebac1e)
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
package runtime
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"runtime/internal/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// read calls the read system call.
|
||||
// It returns a non-negative number of bytes written or a negative errno value.
|
||||
@@ -34,8 +37,8 @@ func open(name *byte, mode, perm int32) int32
|
||||
// return value is only set on linux to be used in osinit()
|
||||
func madvise(addr unsafe.Pointer, n uintptr, flags int32) int32
|
||||
|
||||
// exitThread terminates the current thread, writing *wait = 0 when
|
||||
// exitThread terminates the current thread, writing *wait = freeMStack when
|
||||
// the stack is safe to reclaim.
|
||||
//
|
||||
//go:noescape
|
||||
func exitThread(wait *uint32)
|
||||
func exitThread(wait *atomic.Uint32)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user