3 //go:build !nacl && !js && !aix && !wasip1 && !gcflags_noopt && gc
5 // Copyright 2014 The Go Authors. All rights reserved.
6 // Use of this source code is governed by a BSD-style
7 // license that can be found in the LICENSE file.
28 # These are test cases for the linker analysis that detects chains of
29 # nosplit functions that would cause a stack overflow.
31 # Lines beginning with # are comments.
33 # Each test case describes a sequence of functions, one per line.
34 # Each function definition is the function name, then the frame size,
35 # then optionally the keyword 'nosplit', then the body of the function.
36 # The body is assembly code, with some shorthands.
37 # The shorthand 'call x' stands for CALL x(SB).
38 # The shorthand 'callind' stands for 'CALL R0', where R0 is a register.
39 # Each test case must define a function named start, and it must be first.
40 # That is, a line beginning "start " indicates the start of a new test case.
41 # Within a stanza, ; can be used instead of \n to separate lines.
43 # After the function definition, the test case ends with an optional
44 # REJECT line, specifying the architectures on which the case should
45 # be rejected. "REJECT" without any architectures means reject on all architectures.
46 # The linker should accept the test case on systems not explicitly rejected.
48 # 64-bit systems do not attempt to execute test cases with frame sizes
49 # that are only 32-bit aligned.
51 # Ordinary function should work
54 # Large frame marked nosplit is always wrong.
55 # Frame is so large it overflows cmd/link's int16.
59 # Calling a large frame is okay.
63 # But not if the frame is nosplit.
71 # Recursive nosplit runs out of space.
72 start 0 nosplit call start
75 # Non-trivial recursion runs out of space.
80 # Same but cycle starts below nosplit entry.
87 # Chains of ordinary functions okay.
92 # Chains of nosplit must fit in the stack limit, 128 bytes.
111 f1 16 nosplit call f2
112 f2 16 nosplit call f3
113 f3 16 nosplit call f4
114 f4 16 nosplit call f5
115 f5 16 nosplit call f6
116 f6 16 nosplit call f7
117 f7 16 nosplit call f8
118 f8 16 nosplit call end
122 # Two paths both go over the stack limit.
124 f1 80 nosplit call f2 call f3
125 f2 40 nosplit call f4
130 # Test cases near the 128-byte limit.
132 # Ordinary stack split frame is always okay.
141 # A nosplit leaf can use the whole 128-CallSize bytes available on entry.
142 # (CallSize is 32 on ppc64, 8 on amd64 for frame pointer.)
144 start 100 nosplit; REJECT ppc64 ppc64le
145 start 104 nosplit; REJECT ppc64 ppc64le arm64
146 start 108 nosplit; REJECT ppc64 ppc64le
147 start 112 nosplit; REJECT ppc64 ppc64le arm64
148 start 116 nosplit; REJECT ppc64 ppc64le
149 start 120 nosplit; REJECT ppc64 ppc64le amd64 arm64
150 start 124 nosplit; REJECT ppc64 ppc64le amd64
151 start 128 nosplit; REJECT
152 start 132 nosplit; REJECT
153 start 136 nosplit; REJECT
155 # Calling a nosplit function from a nosplit function requires
156 # having room for the saved caller PC and the called frame.
157 # Because ARM doesn't save LR in the leaf, it gets an extra 4 bytes.
158 # Because arm64 doesn't save LR in the leaf, it gets an extra 8 bytes.
159 # ppc64 doesn't save LR in the leaf, but CallSize is 32, so it gets 24 bytes.
160 # Because AMD64 uses frame pointer, it has 8 fewer bytes.
161 start 96 nosplit call f; f 0 nosplit
162 start 100 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
163 start 104 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le arm64
164 start 108 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
165 start 112 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64
166 start 116 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64
167 start 120 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64
168 start 124 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 386
169 start 128 nosplit call f; f 0 nosplit; REJECT
170 start 132 nosplit call f; f 0 nosplit; REJECT
171 start 136 nosplit call f; f 0 nosplit; REJECT
173 # Calling a splitting function from a nosplit function requires
174 # having room for the saved caller PC of the call but also the
175 # saved caller PC for the call to morestack.
176 # Architectures differ in the same way as before.
177 start 96 nosplit call f; f 0 call f
178 start 100 nosplit call f; f 0 call f; REJECT ppc64 ppc64le
179 start 104 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64
180 start 108 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
181 start 112 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64
182 start 116 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
183 start 120 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386 arm64
184 start 124 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386
185 start 128 nosplit call f; f 0 call f; REJECT
186 start 132 nosplit call f; f 0 call f; REJECT
187 start 136 nosplit call f; f 0 call f; REJECT
189 # Indirect calls are assumed to be splitting functions.
190 start 96 nosplit callind
191 start 100 nosplit callind; REJECT ppc64 ppc64le
192 start 104 nosplit callind; REJECT ppc64 ppc64le amd64 arm64
193 start 108 nosplit callind; REJECT ppc64 ppc64le amd64
194 start 112 nosplit callind; REJECT ppc64 ppc64le amd64 arm64
195 start 116 nosplit callind; REJECT ppc64 ppc64le amd64
196 start 120 nosplit callind; REJECT ppc64 ppc64le amd64 386 arm64
197 start 124 nosplit callind; REJECT ppc64 ppc64le amd64 386
198 start 128 nosplit callind; REJECT
199 start 132 nosplit callind; REJECT
200 start 136 nosplit callind; REJECT
203 start 0 call f; f 112
204 start 0 call f; f 116
205 start 0 call f; f 120
206 start 0 call f; f 124
207 start 0 call f; f 128
208 start 0 call f; f 132
209 start 0 call f; f 136
213 commentRE = regexp.MustCompile(`(?m)^#.*`)
214 rejectRE = regexp.MustCompile(`(?s)\A(.+?)((\n|; *)REJECT(.*))?\z`)
215 lineRE = regexp.MustCompile(`(\w+) (\d+)( nosplit)?(.*)`)
216 callRE = regexp.MustCompile(`\bcall (\w+)\b`)
217 callindRE = regexp.MustCompile(`\bcallind\b`)
221 goarch := os.Getenv("GOARCH")
223 goarch = runtime.GOARCH
226 dir, err := ioutil.TempDir("", "go-test-nosplit")
229 fmt.Printf("creating temp dir: %v\n", err)
232 defer os.RemoveAll(dir)
233 os.Setenv("GOPATH", filepath.Join(dir, "_gopath"))
235 if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module go-test-nosplit\n"), 0666); err != nil {
239 tests = strings.Replace(tests, "\t", " ", -1)
240 tests = commentRE.ReplaceAllString(tests, "")
247 i := strings.Index(tests, "\nstart ")
249 stanza, tests = tests, ""
251 stanza, tests = tests[:i], tests[i+1:]
254 m := rejectRE.FindStringSubmatch(stanza)
257 fmt.Printf("invalid stanza:\n\t%s\n", indent(stanza))
260 lines := strings.TrimSpace(m[1])
263 if strings.TrimSpace(m[4]) == "" {
266 for _, rej := range strings.Fields(m[4]) {
273 if lines == "" && !reject {
277 var gobuf bytes.Buffer
278 fmt.Fprintf(&gobuf, "package main\n")
283 case "mips", "mipsle":
284 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
285 case "mips64", "mips64le":
287 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
290 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
291 case "ppc64", "ppc64le":
293 fmt.Fprintf(&buf, "#define REGISTER (CTR)\n")
295 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
298 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
301 fmt.Fprintf(&buf, "#define REGISTER AX\n")
304 fmt.Fprintf(&buf, "#define REGISTER A0\n")
307 fmt.Fprintf(&buf, "#define REGISTER R10\n")
309 fmt.Fprintf(&buf, "#define REGISTER AX\n")
312 // Since all of the functions we're generating are
313 // ABI0, first enter ABI0 via a splittable function
314 // and then go to the chain we're testing. This way we
315 // don't have to account for ABI wrappers in the chain.
316 fmt.Fprintf(&gobuf, "func main0()\n")
317 fmt.Fprintf(&gobuf, "func main() { main0() }\n")
318 fmt.Fprintf(&buf, "TEXT ·main0(SB),0,$0-0\n\tCALL ·start(SB)\n")
321 for _, line := range strings.Split(lines, "\n") {
322 line = strings.TrimSpace(line)
326 for _, subline := range strings.Split(line, ";") {
327 subline = strings.TrimSpace(subline)
331 m := lineRE.FindStringSubmatch(subline)
334 fmt.Printf("invalid function line: %s\n", subline)
338 size, _ := strconv.Atoi(m[2])
340 if size%ptrSize == 4 {
346 // The limit was originally 128 but is now 800.
347 // Instead of rewriting the test cases above, adjust
348 // the first nosplit frame to use up the extra bytes.
349 // This isn't exactly right because we could have
350 // nosplit -> split -> nosplit, but it's good enough.
351 if !adjusted && nosplit != "" {
352 const stackNosplitBase = 800 // internal/abi.StackNosplitBase
354 size += stackNosplitBase - 128
362 body = callRE.ReplaceAllString(body, "CALL ·$1(SB);")
363 body = callindRE.ReplaceAllString(body, "CALL REGISTER;")
365 fmt.Fprintf(&gobuf, "func %s()\n", name)
366 fmt.Fprintf(&buf, "TEXT ·%s(SB)%s,$%d-0\n\t%s\n\tRET\n\n", name, nosplit, size, body)
371 fmt.Printf("===\n%s\n", strings.TrimSpace(stanza))
372 fmt.Printf("-- main.go --\n%s", gobuf.String())
373 fmt.Printf("-- asm.s --\n%s", buf.String())
376 if err := ioutil.WriteFile(filepath.Join(dir, "asm.s"), buf.Bytes(), 0666); err != nil {
379 if err := ioutil.WriteFile(filepath.Join(dir, "main.go"), gobuf.Bytes(), 0666); err != nil {
383 cmd := exec.Command("go", "build")
385 output, err := cmd.CombinedOutput()
390 fmt.Printf("accepted incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza)))
396 fmt.Printf("rejected incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza)))
397 fmt.Printf("\n\tlinker output:\n\t%s\n", indent(string(output)))
402 if !bugged && (nok == 0 || nfail == 0) {
404 fmt.Printf("not enough test cases run\n")
408 func indent(s string) string {
409 return strings.Replace(s, "\n", "\n\t", -1)