1 // +build !nacl 2 // run 3 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. 7 8 package main 9 10 import ( 11 "bytes" 12 "fmt" 13 "io/ioutil" 14 "log" 15 "os" 16 "os/exec" 17 "path/filepath" 18 "regexp" 19 "runtime" 20 "strconv" 21 "strings" 22 ) 23 24 var tests = ` 25 # These are test cases for the linker analysis that detects chains of 26 # nosplit functions that would cause a stack overflow. 27 # 28 # Lines beginning with # are comments. 29 # 30 # Each test case describes a sequence of functions, one per line. 31 # Each function definition is the function name, then the frame size, 32 # then optionally the keyword 'nosplit', then the body of the function. 33 # The body is assembly code, with some shorthands. 34 # The shorthand 'call x' stands for CALL x(SB). 35 # The shorthand 'callind' stands for 'CALL R0', where R0 is a register. 36 # Each test case must define a function named main, and it must be first. 37 # That is, a line beginning "main " indicates the start of a new test case. 38 # Within a stanza, ; can be used instead of \n to separate lines. 39 # 40 # After the function definition, the test case ends with an optional 41 # REJECT line, specifying the architectures on which the case should 42 # be rejected. "REJECT" without any architectures means reject on all architectures. 43 # The linker should accept the test case on systems not explicitly rejected. 44 # 45 # 64-bit systems do not attempt to execute test cases with frame sizes 46 # that are only 32-bit aligned. 47 48 # Ordinary function should work 49 main 0 50 51 # Large frame marked nosplit is always wrong. 52 main 10000 nosplit 53 REJECT 54 55 # Calling a large frame is okay. 56 main 0 call big 57 big 10000 58 59 # But not if the frame is nosplit. 60 main 0 call big 61 big 10000 nosplit 62 REJECT 63 64 # Recursion is okay. 65 main 0 call main 66 67 # Recursive nosplit runs out of space. 68 main 0 nosplit call main 69 REJECT 70 71 # Chains of ordinary functions okay. 72 main 0 call f1 73 f1 80 call f2 74 f2 80 75 76 # Chains of nosplit must fit in the stack limit, 128 bytes. 77 main 0 call f1 78 f1 80 nosplit call f2 79 f2 80 nosplit 80 REJECT 81 82 # Larger chains. 83 main 0 call f1 84 f1 16 call f2 85 f2 16 call f3 86 f3 16 call f4 87 f4 16 call f5 88 f5 16 call f6 89 f6 16 call f7 90 f7 16 call f8 91 f8 16 call end 92 end 1000 93 94 main 0 call f1 95 f1 16 nosplit call f2 96 f2 16 nosplit call f3 97 f3 16 nosplit call f4 98 f4 16 nosplit call f5 99 f5 16 nosplit call f6 100 f6 16 nosplit call f7 101 f7 16 nosplit call f8 102 f8 16 nosplit call end 103 end 1000 104 REJECT 105 106 # Test cases near the 128-byte limit. 107 108 # Ordinary stack split frame is always okay. 109 main 112 110 main 116 111 main 120 112 main 124 113 main 128 114 main 132 115 main 136 116 117 # A nosplit leaf can use the whole 128-CallSize bytes available on entry. 118 # (CallSize is 32 on ppc64) 119 main 96 nosplit 120 main 100 nosplit; REJECT ppc64 ppc64le 121 main 104 nosplit; REJECT ppc64 ppc64le 122 main 108 nosplit; REJECT ppc64 ppc64le 123 main 112 nosplit; REJECT ppc64 ppc64le 124 main 116 nosplit; REJECT ppc64 ppc64le 125 main 120 nosplit; REJECT ppc64 ppc64le 126 main 124 nosplit; REJECT ppc64 ppc64le 127 main 128 nosplit; REJECT 128 main 132 nosplit; REJECT 129 main 136 nosplit; REJECT 130 131 # Calling a nosplit function from a nosplit function requires 132 # having room for the saved caller PC and the called frame. 133 # Because ARM doesn't save LR in the leaf, it gets an extra 4 bytes. 134 # Because arm64 doesn't save LR in the leaf, it gets an extra 8 bytes. 135 # ppc64 doesn't save LR in the leaf, but CallSize is 32, so it gets 24 fewer bytes than amd64. 136 main 96 nosplit call f; f 0 nosplit 137 main 100 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le 138 main 104 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le 139 main 108 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le 140 main 112 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le 141 main 116 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le 142 main 120 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 143 main 124 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 386 144 main 128 nosplit call f; f 0 nosplit; REJECT 145 main 132 nosplit call f; f 0 nosplit; REJECT 146 main 136 nosplit call f; f 0 nosplit; REJECT 147 148 # Calling a splitting function from a nosplit function requires 149 # having room for the saved caller PC of the call but also the 150 # saved caller PC for the call to morestack. 151 # RISC architectures differ in the same way as before. 152 main 96 nosplit call f; f 0 call f 153 main 100 nosplit call f; f 0 call f; REJECT ppc64 ppc64le 154 main 104 nosplit call f; f 0 call f; REJECT ppc64 ppc64le 155 main 108 nosplit call f; f 0 call f; REJECT ppc64 ppc64le 156 main 112 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 157 main 116 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 158 main 120 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386 159 main 124 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386 160 main 128 nosplit call f; f 0 call f; REJECT 161 main 132 nosplit call f; f 0 call f; REJECT 162 main 136 nosplit call f; f 0 call f; REJECT 163 164 # Indirect calls are assumed to be splitting functions. 165 main 96 nosplit callind 166 main 100 nosplit callind; REJECT ppc64 ppc64le 167 main 104 nosplit callind; REJECT ppc64 ppc64le 168 main 108 nosplit callind; REJECT ppc64 ppc64le 169 main 112 nosplit callind; REJECT ppc64 ppc64le amd64 170 main 116 nosplit callind; REJECT ppc64 ppc64le amd64 171 main 120 nosplit callind; REJECT ppc64 ppc64le amd64 386 172 main 124 nosplit callind; REJECT ppc64 ppc64le amd64 386 173 main 128 nosplit callind; REJECT 174 main 132 nosplit callind; REJECT 175 main 136 nosplit callind; REJECT 176 177 # Issue 7623 178 main 0 call f; f 112 179 main 0 call f; f 116 180 main 0 call f; f 120 181 main 0 call f; f 124 182 main 0 call f; f 128 183 main 0 call f; f 132 184 main 0 call f; f 136 185 ` 186 187 var ( 188 commentRE = regexp.MustCompile(`(?m)^#.*`) 189 rejectRE = regexp.MustCompile(`(?s)\A(.+?)((\n|; *)REJECT(.*))?\z`) 190 lineRE = regexp.MustCompile(`(\w+) (\d+)( nosplit)?(.*)`) 191 callRE = regexp.MustCompile(`\bcall (\w+)\b`) 192 callindRE = regexp.MustCompile(`\bcallind\b`) 193 ) 194 195 func main() { 196 goarch := os.Getenv("GOARCH") 197 if goarch == "" { 198 goarch = runtime.GOARCH 199 } 200 201 version, err := exec.Command("go", "tool", "compile", "-V").Output() 202 if err != nil { 203 bug() 204 fmt.Printf("running go tool compile -V: %v\n", err) 205 return 206 } 207 if strings.Contains(string(version), "framepointer") { 208 // Skip this test if GOEXPERIMENT=framepointer 209 return 210 } 211 212 dir, err := ioutil.TempDir("", "go-test-nosplit") 213 if err != nil { 214 bug() 215 fmt.Printf("creating temp dir: %v\n", err) 216 return 217 } 218 defer os.RemoveAll(dir) 219 220 tests = strings.Replace(tests, "\t", " ", -1) 221 tests = commentRE.ReplaceAllString(tests, "") 222 223 nok := 0 224 nfail := 0 225 TestCases: 226 for len(tests) > 0 { 227 var stanza string 228 i := strings.Index(tests, "\nmain ") 229 if i < 0 { 230 stanza, tests = tests, "" 231 } else { 232 stanza, tests = tests[:i], tests[i+1:] 233 } 234 235 m := rejectRE.FindStringSubmatch(stanza) 236 if m == nil { 237 bug() 238 fmt.Printf("invalid stanza:\n\t%s\n", indent(stanza)) 239 continue 240 } 241 lines := strings.TrimSpace(m[1]) 242 reject := false 243 if m[2] != "" { 244 if strings.TrimSpace(m[4]) == "" { 245 reject = true 246 } else { 247 for _, rej := range strings.Fields(m[4]) { 248 if rej == goarch { 249 reject = true 250 } 251 } 252 } 253 } 254 if lines == "" && !reject { 255 continue 256 } 257 258 var gobuf bytes.Buffer 259 fmt.Fprintf(&gobuf, "package main\n") 260 261 var buf bytes.Buffer 262 ptrSize := 4 263 switch goarch { 264 case "mips", "mipsle": 265 fmt.Fprintf(&buf, "#define CALL JAL\n#define REGISTER (R0)\n") 266 case "mips64", "mips64le": 267 ptrSize = 8 268 fmt.Fprintf(&buf, "#define CALL JAL\n#define REGISTER (R0)\n") 269 case "ppc64", "ppc64le": 270 ptrSize = 8 271 fmt.Fprintf(&buf, "#define CALL BL\n#define REGISTER (CTR)\n") 272 case "arm": 273 fmt.Fprintf(&buf, "#define CALL BL\n#define REGISTER (R0)\n") 274 case "arm64": 275 ptrSize = 8 276 fmt.Fprintf(&buf, "#define CALL BL\n#define REGISTER (R0)\n") 277 case "amd64": 278 ptrSize = 8 279 fmt.Fprintf(&buf, "#define REGISTER AX\n") 280 case "s390x": 281 ptrSize = 8 282 fmt.Fprintf(&buf, "#define REGISTER R10\n") 283 default: 284 fmt.Fprintf(&buf, "#define REGISTER AX\n") 285 } 286 287 for _, line := range strings.Split(lines, "\n") { 288 line = strings.TrimSpace(line) 289 if line == "" { 290 continue 291 } 292 for i, subline := range strings.Split(line, ";") { 293 subline = strings.TrimSpace(subline) 294 if subline == "" { 295 continue 296 } 297 m := lineRE.FindStringSubmatch(subline) 298 if m == nil { 299 bug() 300 fmt.Printf("invalid function line: %s\n", subline) 301 continue TestCases 302 } 303 name := m[1] 304 size, _ := strconv.Atoi(m[2]) 305 306 // The limit was originally 128 but is now 592. 307 // Instead of rewriting the test cases above, adjust 308 // the first stack frame to use up the extra bytes. 309 if i == 0 { 310 size += (880 - 128) - 128 311 // Noopt builds have a larger stackguard. 312 // See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier 313 // This increase is included in obj.StackGuard 314 for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") { 315 if s == "-N" { 316 size += 880 317 } 318 } 319 } 320 321 if size%ptrSize == 4 || goarch == "arm64" && size != 0 && (size+8)%16 != 0 { 322 continue TestCases 323 } 324 nosplit := m[3] 325 body := m[4] 326 327 if nosplit != "" { 328 nosplit = ",7" 329 } else { 330 nosplit = ",0" 331 } 332 body = callRE.ReplaceAllString(body, "CALL $1(SB);") 333 body = callindRE.ReplaceAllString(body, "CALL REGISTER;") 334 335 fmt.Fprintf(&gobuf, "func %s()\n", name) 336 fmt.Fprintf(&buf, "TEXT %s(SB)%s,$%d-0\n\t%s\n\tRET\n\n", name, nosplit, size, body) 337 } 338 } 339 340 if err := ioutil.WriteFile(filepath.Join(dir, "asm.s"), buf.Bytes(), 0666); err != nil { 341 log.Fatal(err) 342 } 343 if err := ioutil.WriteFile(filepath.Join(dir, "main.go"), gobuf.Bytes(), 0666); err != nil { 344 log.Fatal(err) 345 } 346 347 cmd := exec.Command("go", "build") 348 cmd.Dir = dir 349 output, err := cmd.CombinedOutput() 350 if err == nil { 351 nok++ 352 if reject { 353 bug() 354 fmt.Printf("accepted incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza))) 355 } 356 } else { 357 nfail++ 358 if !reject { 359 bug() 360 fmt.Printf("rejected incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza))) 361 fmt.Printf("\n\tlinker output:\n\t%s\n", indent(string(output))) 362 } 363 } 364 } 365 366 if !bugged && (nok == 0 || nfail == 0) { 367 bug() 368 fmt.Printf("not enough test cases run\n") 369 } 370 } 371 372 func indent(s string) string { 373 return strings.Replace(s, "\n", "\n\t", -1) 374 } 375 376 var bugged = false 377 378 func bug() { 379 if !bugged { 380 bugged = true 381 fmt.Printf("BUG\n") 382 } 383 } 384