Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion builder/darwin-libsystem.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,50 @@
package builder

import (
"os"
"path/filepath"
"strings"

"github.com/tinygo-org/tinygo/compileopts"
"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.
Expand Down Expand Up @@ -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",
Expand All @@ -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...)
Expand Down
3 changes: 3 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
34 changes: 34 additions & 0 deletions testdata/darwinsockets.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
1 change: 1 addition & 0 deletions testdata/darwinsockets.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Darwin socket symbols linked and ran