1 // +build !nacl,!js,!aix,!wasip1,!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.
54 # Frame is so large it overflows cmd/link's int16.
58 # Calling a large frame is okay.
62 # But not if the frame is nosplit.
70 # Recursive nosplit runs out of space.
71 start 0 nosplit call start
74 # Non-trivial recursion runs out of space.
79 # Same but cycle starts below nosplit entry.
86 # Chains of ordinary functions okay.
91 # Chains of nosplit must fit in the stack limit, 128 bytes.
110 f1 16 nosplit call f2
111 f2 16 nosplit call f3
112 f3 16 nosplit call f4
113 f4 16 nosplit call f5
114 f5 16 nosplit call f6
115 f6 16 nosplit call f7
116 f7 16 nosplit call f8
117 f8 16 nosplit call end
121 # Two paths both go over the stack limit.
123 f1 80 nosplit call f2 call f3
124 f2 40 nosplit call f4
129 # Test cases near the 128-byte limit.
131 # Ordinary stack split frame is always okay.
140 # A nosplit leaf can use the whole 128-CallSize bytes available on entry.
141 # (CallSize is 32 on ppc64, 8 on amd64 for frame pointer.)
143 start 100 nosplit; REJECT ppc64 ppc64le
144 start 104 nosplit; REJECT ppc64 ppc64le arm64
145 start 108 nosplit; REJECT ppc64 ppc64le
146 start 112 nosplit; REJECT ppc64 ppc64le arm64
147 start 116 nosplit; REJECT ppc64 ppc64le
148 start 120 nosplit; REJECT ppc64 ppc64le amd64 arm64
149 start 124 nosplit; REJECT ppc64 ppc64le amd64
150 start 128 nosplit; REJECT
151 start 132 nosplit; REJECT
152 start 136 nosplit; REJECT
154 # Calling a nosplit function from a nosplit function requires
155 # having room for the saved caller PC and the called frame.
156 # Because ARM doesn't save LR in the leaf, it gets an extra 4 bytes.
157 # Because arm64 doesn't save LR in the leaf, it gets an extra 8 bytes.
158 # ppc64 doesn't save LR in the leaf, but CallSize is 32, so it gets 24 bytes.
159 # Because AMD64 uses frame pointer, it has 8 fewer bytes.
160 start 96 nosplit call f; f 0 nosplit
161 start 100 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
162 start 104 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le arm64
163 start 108 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le
164 start 112 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64
165 start 116 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64
166 start 120 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64
167 start 124 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 386
168 start 128 nosplit call f; f 0 nosplit; REJECT
169 start 132 nosplit call f; f 0 nosplit; REJECT
170 start 136 nosplit call f; f 0 nosplit; REJECT
172 # Calling a splitting function from a nosplit function requires
173 # having room for the saved caller PC of the call but also the
174 # saved caller PC for the call to morestack.
175 # Architectures differ in the same way as before.
176 start 96 nosplit call f; f 0 call f
177 start 100 nosplit call f; f 0 call f; REJECT ppc64 ppc64le
178 start 104 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64
179 start 108 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
180 start 112 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64
181 start 116 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64
182 start 120 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386 arm64
183 start 124 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386
184 start 128 nosplit call f; f 0 call f; REJECT
185 start 132 nosplit call f; f 0 call f; REJECT
186 start 136 nosplit call f; f 0 call f; REJECT
188 # Indirect calls are assumed to be splitting functions.
189 start 96 nosplit callind
190 start 100 nosplit callind; REJECT ppc64 ppc64le
191 start 104 nosplit callind; REJECT ppc64 ppc64le amd64 arm64
192 start 108 nosplit callind; REJECT ppc64 ppc64le amd64
193 start 112 nosplit callind; REJECT ppc64 ppc64le amd64 arm64
194 start 116 nosplit callind; REJECT ppc64 ppc64le amd64
195 start 120 nosplit callind; REJECT ppc64 ppc64le amd64 386 arm64
196 start 124 nosplit callind; REJECT ppc64 ppc64le amd64 386
197 start 128 nosplit callind; REJECT
198 start 132 nosplit callind; REJECT
199 start 136 nosplit callind; REJECT
202 start 0 call f; f 112
203 start 0 call f; f 116
204 start 0 call f; f 120
205 start 0 call f; f 124
206 start 0 call f; f 128
207 start 0 call f; f 132
208 start 0 call f; f 136
212 commentRE = regexp.MustCompile(`(?m)^#.*`)
213 rejectRE = regexp.MustCompile(`(?s)\A(.+?)((\n|; *)REJECT(.*))?\z`)
214 lineRE = regexp.MustCompile(`(\w+) (\d+)( nosplit)?(.*)`)
215 callRE = regexp.MustCompile(`\bcall (\w+)\b`)
216 callindRE = regexp.MustCompile(`\bcallind\b`)
220 goarch := os.Getenv("GOARCH")
222 goarch = runtime.GOARCH
225 dir, err := ioutil.TempDir("", "go-test-nosplit")
228 fmt.Printf("creating temp dir: %v\n", err)
231 defer os.RemoveAll(dir)
232 os.Setenv("GOPATH", filepath.Join(dir, "_gopath"))
234 if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module go-test-nosplit\n"), 0666); err != nil {
238 tests = strings.Replace(tests, "\t", " ", -1)
239 tests = commentRE.ReplaceAllString(tests, "")
246 i := strings.Index(tests, "\nstart ")
248 stanza, tests = tests, ""
250 stanza, tests = tests[:i], tests[i+1:]
253 m := rejectRE.FindStringSubmatch(stanza)
256 fmt.Printf("invalid stanza:\n\t%s\n", indent(stanza))
259 lines := strings.TrimSpace(m[1])
262 if strings.TrimSpace(m[4]) == "" {
265 for _, rej := range strings.Fields(m[4]) {
272 if lines == "" && !reject {
276 var gobuf bytes.Buffer
277 fmt.Fprintf(&gobuf, "package main\n")
282 case "mips", "mipsle":
283 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
284 case "mips64", "mips64le":
286 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
289 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
290 case "ppc64", "ppc64le":
292 fmt.Fprintf(&buf, "#define REGISTER (CTR)\n")
294 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
297 fmt.Fprintf(&buf, "#define REGISTER (R0)\n")
300 fmt.Fprintf(&buf, "#define REGISTER AX\n")
303 fmt.Fprintf(&buf, "#define REGISTER A0\n")
306 fmt.Fprintf(&buf, "#define REGISTER R10\n")
308 fmt.Fprintf(&buf, "#define REGISTER AX\n")
311 // Since all of the functions we're generating are
312 // ABI0, first enter ABI0 via a splittable function
313 // and then go to the chain we're testing. This way we
314 // don't have to account for ABI wrappers in the chain.
315 fmt.Fprintf(&gobuf, "func main0()\n")
316 fmt.Fprintf(&gobuf, "func main() { main0() }\n")
317 fmt.Fprintf(&buf, "TEXT ·main0(SB),0,$0-0\n\tCALL ·start(SB)\n")
320 for _, line := range strings.Split(lines, "\n") {
321 line = strings.TrimSpace(line)
325 for _, subline := range strings.Split(line, ";") {
326 subline = strings.TrimSpace(subline)
330 m := lineRE.FindStringSubmatch(subline)
333 fmt.Printf("invalid function line: %s\n", subline)
337 size, _ := strconv.Atoi(m[2])
339 if size%ptrSize == 4 {
345 // The limit was originally 128 but is now 800.
346 // Instead of rewriting the test cases above, adjust
347 // the first nosplit frame to use up the extra bytes.
348 // This isn't exactly right because we could have
349 // nosplit -> split -> nosplit, but it's good enough.
350 if !adjusted && nosplit != "" {
351 const stackNosplitBase = 800 // internal/abi.StackNosplitBase
353 size += stackNosplitBase - 128
361 body = callRE.ReplaceAllString(body, "CALL ·$1(SB);")
362 body = callindRE.ReplaceAllString(body, "CALL REGISTER;")
364 fmt.Fprintf(&gobuf, "func %s()\n", name)
365 fmt.Fprintf(&buf, "TEXT ·%s(SB)%s,$%d-0\n\t%s\n\tRET\n\n", name, nosplit, size, body)
370 fmt.Printf("===\n%s\n", strings.TrimSpace(stanza))
371 fmt.Printf("-- main.go --\n%s", gobuf.String())
372 fmt.Printf("-- asm.s --\n%s", buf.String())
375 if err := ioutil.WriteFile(filepath.Join(dir, "asm.s"), buf.Bytes(), 0666); err != nil {
378 if err := ioutil.WriteFile(filepath.Join(dir, "main.go"), gobuf.Bytes(), 0666); err != nil {
382 cmd := exec.Command("go", "build")
384 output, err := cmd.CombinedOutput()
389 fmt.Printf("accepted incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza)))
395 fmt.Printf("rejected incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza)))
396 fmt.Printf("\n\tlinker output:\n\t%s\n", indent(string(output)))
401 if !bugged && (nok == 0 || nfail == 0) {
403 fmt.Printf("not enough test cases run\n")
407 func indent(s string) string {
408 return strings.Replace(s, "\n", "\n\t", -1)