diff --git a/builder/darwin-libsystem.go b/builder/darwin-libsystem.go index e47b54230e..ae2df995a6 100644 --- a/builder/darwin-libsystem.go +++ b/builder/darwin-libsystem.go @@ -1,6 +1,7 @@ package builder import ( + "os" "path/filepath" "strings" @@ -8,6 +9,42 @@ import ( "github.com/tinygo-org/tinygo/goenv" ) +// Extra declarations for libSystem exports absent from macos-minimal-sdk. +// See https://github.com/tinygo-org/macos-minimal-sdk for the SDK sources. +var darwinExtraLibSystemSymbols = []string{ + // The BSD socket API, which the host netdev in src/net reaches through + // the standard library syscall package. + "accept", + "bind", + "connect", + "getpeername", + "getsockname", + "getsockopt", + "listen", + "recvfrom", + "recvmsg", + "sendmsg", + "sendto", + "setsockopt", + "shutdown", + "socket", + "socketpair", + + // addchdir_np requires macOS 10.15 or later. + "posix_spawn", + "posix_spawn_file_actions_addchdir_np", + "posix_spawn_file_actions_addclose", + "posix_spawn_file_actions_adddup2", + "posix_spawn_file_actions_addopen", + "posix_spawn_file_actions_destroy", + "posix_spawn_file_actions_init", + "posix_spawnattr_destroy", + "posix_spawnattr_init", + "posix_spawnattr_setflags", + "posix_spawnattr_setpgroup", + "posix_spawnattr_setsigmask", +} + // Create a job that builds a Darwin libSystem.dylib stub library. This library // contains all the symbols needed so that we can link against it, but it // doesn't contain any real symbol implementations. @@ -36,7 +73,34 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ return err } - // Link object file to dynamic library. + // Compile the extra stubs into a second object file, so that the + // generated one stays as it is. + extrapath := filepath.Join(tmpdir, "libSystem-extra.s") + extraobjpath := filepath.Join(tmpdir, "libSystem-extra.o") + var extra strings.Builder + extra.WriteString("// Stubs for symbols exported by libSystem but not declared in lib/macos-minimal-sdk.\n") + for _, symbol := range darwinExtraLibSystemSymbols { + extra.WriteString("\n.global _" + symbol + "\n_" + symbol + ":\n") + } + if err := os.WriteFile(extrapath, []byte(extra.String()), 0o666); err != nil { + return err + } + flags = []string{ + "-nostdlib", + "--target=" + config.Triple(), + "-c", + "-o", extraobjpath, + extrapath, + } + if config.Options.PrintCommands != nil { + config.Options.PrintCommands("clang", flags...) + } + err = runCCompiler(flags...) + if err != nil { + return err + } + + // Link object files to dynamic library. platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx") flags = []string{ "-flavor", "darwin", @@ -48,6 +112,7 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ "-install_name", "/usr/lib/libSystem.B.dylib", "-o", job.result, objpath, + extraobjpath, } if config.Options.PrintCommands != nil { config.Options.PrintCommands("ld.lld", flags...) diff --git a/main_test.go b/main_test.go index 07c531a392..985af8b9e5 100644 --- a/main_test.go +++ b/main_test.go @@ -122,6 +122,9 @@ func TestBuild(t *testing.T) { t.Parallel() hostOptions := optionsFromTarget("", sema) runPlatTests(hostOptions, tests, t) + if runtime.GOOS == "darwin" { + runPlatTests(hostOptions, []string{"darwinsockets.go"}, t) + } // scheduler.threads needs threadID, which exists only on Linux and Darwin. // scheduler.none does not link on Windows. diff --git a/testdata/darwinsockets.go b/testdata/darwinsockets.go new file mode 100644 index 0000000000..2d8bd0e904 --- /dev/null +++ b/testdata/darwinsockets.go @@ -0,0 +1,34 @@ +package main + +import "syscall" + +func main() { + fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0) + check(err) + defer syscall.Close(fd) + check(syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)) + check(syscall.Bind(fd, &syscall.SockaddrInet4{Addr: [4]byte{127, 0, 0, 1}})) + check(syscall.Listen(fd, 1)) + addr, err := syscall.Getsockname(fd) + check(err) + if addr.(*syscall.SockaddrInet4).Port == 0 { + panic("the listener has no port") + } + client, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0) + check(err) + defer syscall.Close(client) + check(syscall.Connect(client, addr)) + peer, _, err := syscall.Accept(fd) + check(err) + defer syscall.Close(peer) + _, err = syscall.Getpeername(peer) + check(err) + check(syscall.Shutdown(client, syscall.SHUT_RDWR)) + println("Darwin socket symbols linked and ran") +} + +func check(err error) { + if err != nil { + panic(err) + } +} diff --git a/testdata/darwinsockets.txt b/testdata/darwinsockets.txt new file mode 100644 index 0000000000..bd5f5f83d5 --- /dev/null +++ b/testdata/darwinsockets.txt @@ -0,0 +1 @@ +Darwin socket symbols linked and ran