blob: 2089badbe91a71b3d32909ecdb67d66949beee5f [file] [log] [blame]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ld
import (
"fmt"
"internal/testenv"
"os"
"os/exec"
"regexp"
"strconv"
"testing"
)
// See also $GOROOT/test/nosplit.go for multi-platform edge case tests.
func TestStackCheckOutput(t *testing.T) {
testenv.MustHaveGoBuild(t)
t.Parallel()
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", os.DevNull, "./testdata/stackcheck")
// The rules for computing frame sizes on all of the
// architectures are complicated, so just do this on amd64.
cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux")
outB, err := cmd.CombinedOutput()
if err == nil {
t.Fatalf("expected link to fail")
}
out := string(outB)
t.Logf("linker output:\n%s", out)
// Get expected limit.
limitRe := regexp.MustCompile("nosplit stack over ([0-9]+) byte limit")
m := limitRe.FindStringSubmatch(out)
if m == nil {
t.Fatalf("no overflow errors in output")
}
limit, _ := strconv.Atoi(m[1])
wantMap := map[string]string{
"main.startSelf": fmt.Sprintf(
`main.startSelf<0>
grows 1008 bytes
%d bytes over limit
`, 1008-limit),
"main.startChain": fmt.Sprintf(
`main.startChain<0>
grows 32 bytes, calls main.chain0<0>
grows 48 bytes, calls main.chainEnd<0>
grows 1008 bytes
%d bytes over limit
grows 32 bytes, calls main.chain2<0>
grows 80 bytes, calls main.chainEnd<0>
grows 1008 bytes
%d bytes over limit
`, 32+48+1008-limit, 32+80+1008-limit),
"main.startRec": `main.startRec<0>
grows 8 bytes, calls main.startRec0<0>
grows 8 bytes, calls main.startRec<0>
infinite cycle
`,
}
// Parse stanzas
stanza := regexp.MustCompile(`^(.*): nosplit stack over [0-9]+ byte limit\n(.*\n(?: .*\n)*)`)
// Strip comments from cmd/go
out = regexp.MustCompile(`(?m)^#.*\n`).ReplaceAllString(out, "")
for len(out) > 0 {
m := stanza.FindStringSubmatch(out)
if m == nil {
t.Fatalf("unexpected output:\n%s", out)
}
out = out[len(m[0]):]
fn := m[1]
got := m[2]
want, ok := wantMap[fn]
if !ok {
t.Errorf("unexpected function: %s", fn)
} else if want != got {
t.Errorf("want:\n%sgot:\n%s", want, got)
}
}
}