Skip to content

v0.42.0-net.2

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 30 Aug 03:26
· 5 commits to dev since this release

A test build of the TinyGo 0.42 development tree with working host networking over real TLS and working process spawning on hosted linux and macOS. It exists so a downstream project can build and run a program whose release paths are all HTTPS and which shells out to the binaries it installs. It is not an official TinyGo release and it is not built from tinygo-org/tinygo.

This is v0.42.0-net.1 plus a working os/exec.

New in net.2: os/exec starts processes

TinyGo's os package stubbed the process layer out. StartProcess rejected every ProcAttr carrying Dir, Sys or Files — and os/exec always passes three Files — so no command could ever be run; Wait, Kill and Signal returned operation not implemented, and ProcessState was an empty struct whose methods all reported failure. What did exist on linux, a bare fork() followed by execve() with no branch on the return value, was unsound anyway: parent and child both fell into the exec.

  • Processes are started with posix_spawn(3) on hosted linux (musl) and macOS (libSystem), not with a fork. These targets run the threads scheduler and collect with Boehm, so a fork() from Go would hand the child a single thread holding whatever locks the other threads owned — malloc's among them — with the collector's stop-the-world signal free to land between the fork and the exec. posix_spawn moves the clone and the exec inside libc, where no Go code runs in between.
  • ProcAttr.Files, .Dir and .Env all work. Descriptors are laid out with dup2/close file actions, Dir with posix_spawn_file_actions_addchdir_np, and a nil Env inherits the parent's environment, as in upstream Go.
  • Wait reaps with wait4 and retries on EINTR, which is routine here rather than exceptional: a thread parked in wait4 is exactly the kind the collector has to interrupt.
  • ProcessState is real. It carries the pid and the actual syscall.WaitStatus, so exec.ExitError reports exit status N, and ExitCode, Exited, Success, Sys and String all work. A killed child is reported as signalled.
  • Kill/Signal work, and map ESRCH to os.ErrProcessDone — which is what exec.CommandContext expects when its context fires at the same moment the command finishes.
  • The child gets an empty signal mask. Unlike a handler disposition, a blocked mask survives an exec, and the spawning thread may be carrying the collector's signal blocked.
  • os.Pipe on macOS now sets FD_CLOEXEC on both ends. macOS has no pipe2, so every pipe used to leak into every process started afterwards, and a child holding a duplicate of a write end keeps the pipe from ever reporting end of file — which is exactly how os/exec collects a command's output.
  • The darwin libSystem stub declares the posix_spawn family, which the minimal macOS SDK omits.

Targets without a process model — baremetal, wasm, Windows — keep exactly the previous stubs.

Carried over from net.1

  • crypto/tls is the real one. TinyGo replaces crypto/tls with a stub whose handshake does nothing, so https:// produced a plaintext connection wearing the TLS API. On hosted linux and darwin the standard library's own crypto/tls is used instead; the stub stays in place for baremetal, wasm and Windows targets.
  • The host netdev works on macOS. src/net's raw-socket netdev was linux-only, so a darwin binary reported Netdev not set for every dial. It now builds on darwin too, with SO_NOSIGPIPE on every socket.
  • HTTPS on macOS has trust roots. crypto/x509's macOS verifier is a stub in TinyGo, so net/http supplies a root pool of its own, read from $SSL_CERT_FILE or from /etc/ssl/cert.pem.
  • weak.runtime_makeStrongFromWeak is implemented, which crypto/tls's certificate cache needs to link.
  • The darwin libSystem stub declares the BSD socket API, which the minimal macOS SDK omits.

Verified

tests/spawnprobe — the process layer asked one piece at a time: output, combined stderr, exit status, cmd.Env, an inherited environment, cmd.Dir, stdin from a pipe, ExtraFiles as descriptor 3, no descriptor leak into an unrelated child, a context deadline killing sleep, a missing binary as os.ErrNotExist, and sixteen concurrent spawns. All twelve checks pass on linux/arm64 and linux/amd64, as does the os package's own test suite, including 200 spawns under continuous garbage collection.

tests/netprobe — TCP, TLS, plain HTTP, HTTPS and DNS — continues to report every layer working, unchanged from net.1.

macOS is verified by CI only. The os package tests, which now cover starting a process, collecting its exit status and honouring Dir and Files, run on the macOS job of this build. No darwin binary from this release has been exercised by hand; run tests/spawnprobe on a Mac before trusting it in anger.

Windows is unchanged and still gets the stubs. A Windows binary built with this toolchain compiles crypto/tls to the no-op handshake, so https:// there is plaintext wearing the TLS API, and os.StartProcess still returns operation not implemented. Only hosted linux and macOS were switched over.

Install

Each tinygo<version>.<os>-<arch>.tar.gz unpacks to a single tinygo/ directory holding bin/, lib/, src/ and targets/.

curl -L -O https://github.com/yohimik/tinygo/releases/download/v0.42.0-net.2/tinygo0.42.0-net.2.linux-amd64.tar.gz
tar xzf tinygo0.42.0-net.2.linux-amd64.tar.gz -C /usr/local/lib
export TINYGOROOT=/usr/local/lib/tinygo
export PATH=$PATH:$TINYGOROOT/bin
tinygo version

TINYGOROOT must point at that directory whenever bin/tinygo is not run from inside it.

A host Go toolchain is required. TinyGo runs go list and reads the standard library from $(go env GOROOT), so a Go toolchain must be installed on the machine that runs tinygo — including when cross-compiling. This build accepts Go 1.25 through 1.27; crypto/tls and os/exec themselves come from that toolchain's GOROOT, which is why the version matters more than usual here.

.deb packages are also attached; they install to /usr/local/lib/tinygo with a symlink at /usr/local/bin/tinygo.

Using it in a Dockerfile instead of the tinygo/tinygo image

Replace the toolchain, not the base image: start from golang:1.27, unpack the tarball, and point TINYGOROOT at it.

FROM golang:1.27
ARG TINYGO_VERSION=0.42.0-net.2
ARG TARGETARCH
RUN curl -fsSL -o /tmp/tinygo.tar.gz \
      "https://github.com/yohimik/tinygo/releases/download/v${TINYGO_VERSION}/tinygo${TINYGO_VERSION}.linux-${TARGETARCH}.tar.gz" \
 && tar xzf /tmp/tinygo.tar.gz -C /usr/local/lib \
 && rm /tmp/tinygo.tar.gz
ENV TINYGOROOT=/usr/local/lib/tinygo
ENV PATH="/usr/local/lib/tinygo/bin:${PATH}"

Cross-compiling to macOS works from that image with GOOS=darwin GOARCH=arm64 tinygo build ….

macOS caveats

  • A binary from this toolchain now needs macOS 10.15 or later. posix_spawn_file_actions_addchdir_np, which carries cmd.Dir into the child, arrived in 10.15, and the libSystem stub declares it unconditionally. The deployment target itself is unchanged (10.12 on amd64, 11.0 on arm64).

  • A raw tls.Dial(…, nil) still fails. crypto/x509's platform verifier is a stub on darwin, and a nil RootCAs sends verification straight to it. net/http supplies roots for itself, so http.Client over https:// is fine; code that dials TLS directly must pass its own pool:

    pem, _ := os.ReadFile("/etc/ssl/cert.pem")
    pool := x509.NewCertPool()
    pool.AppendCertsFromPEM(pem)
    conn, err := tls.Dial("tcp", host, &tls.Config{ServerName: name, RootCAs: pool})
  • SSL_CERT_FILE overrides the bundle that net/http loads, for a container image or a host without /etc/ssl/cert.pem.

  • The resolver is a stub resolver. It reads /etc/hosts and the nameserver lines of /etc/resolv.conf, and queries them over UDP. It does not use the macOS system resolver, so scoped and split-horizon DNS, mDNS .local names, and IPv6 nameservers are not supported. It returns one address per name, preferring A over AAAA.

Process-layer caveats, both platforms

  • ProcAttr.Sys is still rejected. syscall.SysProcAttr asks for things posix_spawn cannot express — setsid, credentials, a controlling terminal, process groups — so StartProcess returns sys setting not implemented rather than silently ignoring it. exec.Cmd only sets it if you do.
  • os.Process.Wait reaps with wait4 on the pid directly, so a program that also reaps children itself, or that installs its own SIGCHLD handling, may race with it.
  • os/signal is unchanged; this release did not touch signal delivery to the TinyGo process itself.

Source

Verifying downloads

GitHub records a SHA-256 digest for every asset below. To print them:

gh release view v0.42.0-net.2 --repo yohimik/tinygo --json assets --jq '.assets[] | "\(.digest)  \(.name)"'