1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build darwin 6 7 package unix 8 9 import ( 10 "os" 11 "os/exec" 12 "strings" 13 "testing" 14 ) 15 16 type darwinTest struct { 17 name string 18 f uintptr 19 } 20 21 // TODO(khr): decide whether to keep this test enabled permanently or 22 // only temporarily. 23 func TestDarwinLoader(t *testing.T) { 24 // Make sure the Darwin dynamic loader can actually resolve 25 // all the system calls into libSystem.dylib. Unfortunately 26 // there is no easy way to test this at compile time. So we 27 // implement a crazy hack here, calling into the syscall 28 // function with all its arguments set to junk, and see what 29 // error we get. We are happy with any error (or none) except 30 // an error from the dynamic loader. 31 // 32 // We have to run each test in a separate subprocess for fault isolation. 33 // 34 // Hopefully the junk args won't accidentally ask the system to do "rm -fr /". 35 // 36 // In an ideal world each syscall would have its own test, so this test 37 // would be unnecessary. Unfortunately, we do not live in that world. 38 exe, err := os.Executable() 39 if err != nil { 40 t.Fatal(err) 41 } 42 for _, test := range darwinTests { 43 // Call the test binary recursively, giving it a magic argument 44 // (see init below) and the name of the test to run. 45 cmd := exec.Command(exe, "testDarwinLoader", test.name) 46 47 // Run subprocess, collect results. Note that we expect the subprocess 48 // to fail somehow, so the error is irrelevant. 49 out, _ := cmd.CombinedOutput() 50 51 if strings.Contains(string(out), "dyld: Symbol not found:") { 52 t.Errorf("can't resolve %s in libSystem.dylib", test.name) 53 } 54 if !strings.Contains(string(out), "success") { 55 // Not really an error. Might be a syscall that never returns, 56 // like exit, or one that segfaults, like gettimeofday. 57 t.Logf("test never finished: %s: %s", test.name, string(out)) 58 } 59 } 60 } 61 62 func init() { 63 // The test binary execs itself with the "testDarwinLoader" argument. 64 // Run the test specified by os.Args[2], then panic. 65 if len(os.Args) >= 3 && os.Args[1] == "testDarwinLoader" { 66 for _, test := range darwinTests { 67 if test.name == os.Args[2] { 68 syscall_syscall(test.f, ^uintptr(0), ^uintptr(0), ^uintptr(0)) 69 } 70 } 71 // Panic with a "success" label, so the parent process can check it. 72 panic("success") 73 } 74 } 75