1 // +build !nacl,!js,!aix,!gcflags_noopt,gc
4 // Copyright 2014 The Go Authors. All rights reserved.
5 // Use of this source code is governed by a BSD-style
6 // license that can be found in the LICENSE file.
27 # These are test cases for the linker analysis that detects chains of
28 # nosplit functions that would cause a stack overflow.
30 # Lines beginning with # are comments.
32 # Each test case describes a sequence of functions, one per line.
33 # Each function definition is the function name, then the frame size,
34 # then optionally the keyword 'nosplit', then the body of the function.
35 # The body is assembly code, with some shorthands.
36 # The shorthand 'call x' stands for CALL x(SB).
37 # The shorthand 'callind' stands for 'CALL R0', where R0 is a register.
38 # Each test case must define a function named start, and it must be first.
39 # That is, a line beginning "start " indicates the start of a new test case.
40 # Within a stanza, ; can be used instead of \n to separate lines.
42 # After the function definition, the test case ends with an optional
43 # REJECT line, specifying the architectures on which the case should
44 # be rejected. "REJECT" without any architectures means reject on all architectures.
45 # The linker should accept the test case on systems not explicitly rejected.
47 # 64-bit systems do not attempt to execute test cases with frame sizes
48 # that are only 32-bit aligned.
50 # Ordinary function should work
53 # Large frame marked nosplit is always wrong.
57 # Calling a large frame is okay.
61 # But not if the frame is nosplit.
69 # Recursive nosplit runs out of space.
70 start 0 nosplit call start
73 # Chains of ordinary functions okay.
78 # Chains of nosplit must fit in the stack limit, 128 bytes.
100 f4 16 nosplit call f5
101 f5 16 nosplit call f6
102 f6 16 nosplit call f7
103 f7 16 nosplit call f8
104 f8 16 nosplit call end
108 # Test cases near the 128-byte limit.
110 # Ordinary stack split frame is always okay.
119 # A nosplit leaf can use the whole 128-CallSize bytes available on entry.
120 # (CallSize is 32 on ppc64, 8 on amd64 for frame pointer.)
122 start 100 nosplit; REJECT ppc64 ppc64le
123 start 104 nosplit; REJECT ppc64 ppc64le arm64
124 start 108 nosplit; REJECT ppc64 ppc64le
125 start 112 nosplit; REJECT ppc64 ppc64le arm64
126 start 116 nosplit; REJECT ppc64 ppc64le
127 start 120 nosplit; REJECT ppc64 ppc64le amd64 arm64
128 start 124 nosplit; REJECT ppc64 ppc64le amd64
129 start 128 nosplit; REJECT
130 start 132 nosplit; REJECT
131 start 136 nosplit; REJECT
133 # Calling a nosplit function from a nosplit function requires
134 # having room for the saved caller PC and the called frame.
135 # Because ARM doesn't save LR in the leaf, it gets an extra 4 bytes.
136 # Because arm64 doesn't save LR in the leaf, it gets an extra 8 bytes.
137 # ppc64 doesn't save LR in the leaf, but CallSize is 32, so it gets 24 bytes.
138 # Because AMD64 uses frame pointer, it has 8 fewer bytes.
139 start 96 nosplit call f; f 0 nosplit
140 start 100 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
141 start 104 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le arm64
142 start 108 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
143 start 112 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64
144 start 116 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64
145 start 120 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64
146 start 124 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 386
147 start 128 nosplit call f; f 0 nosplit; REJECT
148 start 132 nosplit call f; f 0 nosplit; REJECT
149 start 136 nosplit call f; f 0 nosplit; REJECT
151 # Calling a splitting function from a nosplit function requires
152 # having room for the saved caller PC of the call but also the
153 # saved caller PC for the call to morestack.
154 # Architectures differ in the same way as before.
155 start 96 nosplit call f; f 0 call f
156 start 100 nosplit call f; f 0 call f; REJECT ppc64 ppc64le
157 start 104 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64
158 start 108 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
159 start 112 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64
160 start 116 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
161 start 120 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386 arm64
162 start 124 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386
163 start 128 nosplit call f; f 0 call f; REJECT
164 start 132 nosplit call f; f 0 call f; REJECT
165 start 136 nosplit call f; f 0 call f; REJECT
167 # Indirect calls are assumed to be splitting functions.
168 start 96 nosplit callind
169 start 100 nosplit callind; REJECT ppc64 ppc64le
170 start 104 nosplit callind; REJECT ppc64 ppc64le amd64 arm64
171 start 108 nosplit callind; REJECT ppc64 ppc64le amd64
172 start 112 nosplit callind; REJECT ppc64 ppc64le amd64 arm64
173 start 116 nosplit callind; REJECT ppc64 ppc64le amd64
174 start 120 nosplit callind; REJECT ppc64 ppc64le amd64 386 arm64
175 start 124 nosplit callind; REJECT ppc64 ppc64le amd64 386
176 start 128 nosplit callind; REJECT
177 start 132 nosplit callind; REJECT
178 start 136 nosplit callind; REJECT
181 start 0 call f; f 112
182 start 0 call f; f 116
183 start 0 call f; f 120
184 start 0 call f; f 124
185 start 0 call f; f 128
186 start 0 call f; f 132
187 start 0 call f; f 136
191 commentRE = regexp.MustCompile(`(?m)^#.*`)
192 rejectRE = regexp.MustCompile(`(?s)\A(.+?)((\n|; *)REJECT(.*))?\z`)
193 lineRE = regexp.MustCompile(`(\w+) (\d+)( nosplit)?(.*)`)
194 callRE = regexp.MustCompile(`\bcall (\w+)\b`)
195 callindRE = regexp.MustCompile(`\bcallind\b`)
199 goarch := os.Getenv("GOARCH")
201 goarch = runtime.GOARCH
204 dir, err := ioutil.TempDir("", "go-test-nosplit")
207 fmt.Printf("creating temp dir: %v\n", err)
210 defer os.RemoveAll(dir)
211 os.Setenv("GOPATH", filepath.Join(dir, "_gopath"))
213 if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module go-test-nosplit\n"), 0666); err != nil {
217 tests = strings.Replace(tests, "\t", " ", -1)
218 tests = commentRE.ReplaceAllString(tests, "")
225 i := strings.Index(tests, "\nstart ")
227 stanza, tests = tests, ""
229 stanza, tests = tests[:i], tests[i+1:]
232 m := rejectRE.FindStringSubmatch(stanza)
235 fmt.Printf("invalid stanza:\n\t%s\n", indent(stanza))
238 lines := strings.TrimSpace(m[1])
241 if strings.TrimSpace(m[4]) == "" {
244 for _, rej := range strings.Fields(m[4]) {
251 if lines == "" && !reject {
255 var gobuf bytes.Buffer
256 fmt.Fprintf(&gobuf, "package main\n")
261 case "mips", "mipsle":
262 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
263 case "mips64", "mips64le":
265 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
266 case "ppc64", "ppc64le":
268 fmt.Fprintf(&buf, "#define REGISTER (CTR)\n")
270 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
273 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
276 fmt.Fprintf(&buf, "#define REGISTER AX\n")
279 fmt.Fprintf(&buf, "#define REGISTER A0\n")
282 fmt.Fprintf(&buf, "#define REGISTER R10\n")
284 fmt.Fprintf(&buf, "#define REGISTER AX\n")
287 // Since all of the functions we're generating are
288 // ABI0, first enter ABI0 via a splittable function
289 // and then go to the chain we're testing. This way we
290 // don't have to account for ABI wrappers in the chain.
291 fmt.Fprintf(&gobuf, "func main0()\n")
292 fmt.Fprintf(&gobuf, "func main() { main0() }\n")
293 fmt.Fprintf(&buf, "TEXT ·main0(SB),0,$0-0\n\tCALL ·start(SB)\n")
296 for _, line := range strings.Split(lines, "\n") {
297 line = strings.TrimSpace(line)
301 for _, subline := range strings.Split(line, ";") {
302 subline = strings.TrimSpace(subline)
306 m := lineRE.FindStringSubmatch(subline)
309 fmt.Printf("invalid function line: %s\n", subline)
313 size, _ := strconv.Atoi(m[2])
315 if size%ptrSize == 4 {
321 // The limit was originally 128 but is now 800 (928-128).
322 // Instead of rewriting the test cases above, adjust
323 // the first nosplit frame to use up the extra bytes.
324 // This isn't exactly right because we could have
325 // nosplit -> split -> nosplit, but it's good enough.
326 if !adjusted && nosplit != "" {
328 size += (928 - 128) - 128
329 // Noopt builds have a larger stackguard.
330 // See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier
331 // This increase is included in objabi.StackGuard
332 for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") {
344 body = callRE.ReplaceAllString(body, "CALL ·$1(SB);")
345 body = callindRE.ReplaceAllString(body, "CALL REGISTER;")
347 fmt.Fprintf(&gobuf, "func %s()\n", name)
348 fmt.Fprintf(&buf, "TEXT ·%s(SB)%s,$%d-0\n\t%s\n\tRET\n\n", name, nosplit, size, body)
353 fmt.Printf("===\n%s\n", strings.TrimSpace(stanza))
354 fmt.Printf("-- main.go --\n%s", gobuf.String())
355 fmt.Printf("-- asm.s --\n%s", buf.String())
358 if err := ioutil.WriteFile(filepath.Join(dir, "asm.s"), buf.Bytes(), 0666); err != nil {
361 if err := ioutil.WriteFile(filepath.Join(dir, "main.go"), gobuf.Bytes(), 0666); err != nil {
365 cmd := exec.Command("go", "build")
367 output, err := cmd.CombinedOutput()
372 fmt.Printf("accepted incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza)))
378 fmt.Printf("rejected incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza)))
379 fmt.Printf("\n\tlinker output:\n\t%s\n", indent(string(output)))
384 if !bugged && (nok == 0 || nfail == 0) {
386 fmt.Printf("not enough test cases run\n")
390 func indent(s string) string {
391 return strings.Replace(s, "\n", "\n\t", -1)