From f57a392310b735c87b416c075fe176a5df918dba Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Mon, 28 Sep 2026 19:08:52 +0000 Subject: [PATCH 01/11] linux: Go's standard-library tests can run as guests Go's own test suites check thousands of Linux behaviours, but nothing could run them inside NONOS: go test runs each test binary from its package's directory, beside testdata/, and the boot guest always starts at /. NONOS_LINUX_GO_SUITE=1 now enrols the test binaries that `go test -c` makes for the packages NONOS_LINUX_GO_SUITE_PKGS names, as guests gs, ids 5042 upward in list order. gostd (5040) changes to the directory named first and becomes the program named second. A suite store holds gostd, the packages NONOS_LINUX_GO_SUITE_STORE names, their testdata/ at the paths they have on the build host, and Go's zone database, and nothing else, since one test binary is 4 to 15 MB against the store's 16 MiB. The default build does not change. --- userland/linux_guests/GuestFiles.mk | 19 ++++++++++++++++ userland/linux_guests/Guests.mk | 22 +++++++++++++++++++ userland/linux_guests/go/std/go.mod | 3 +++ userland/linux_guests/go/std/main.go | 33 ++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 userland/linux_guests/go/std/go.mod create mode 100644 userland/linux_guests/go/std/main.go diff --git a/userland/linux_guests/GuestFiles.mk b/userland/linux_guests/GuestFiles.mk index 75a418455..b6abc052f 100644 --- a/userland/linux_guests/GuestFiles.mk +++ b/userland/linux_guests/GuestFiles.mk @@ -41,3 +41,22 @@ LINUX_GUEST_STORE_ENTRIES += --entry /linux/lib/libprobe_bad.so=$(LINUX_GUEST_BA --entry /linux/lib/libprobe_bad.so.nonos_id_cert.bin=$(linux-guest-libprobe_CERT) \ --entry /linux/lib/libprobe_bad.so.manifest.bin=$(linux-guest-libprobe_MANIFEST) \ --entry /linux/lib/libprobe_bad.so.zk_trailer.bin=$(linux-guest-libprobe_ATTESTATION) + +# A Go suite image holds the wrapper, the packages NONOS_LINUX_GO_SUITE_STORE +# names (all enrolled ones unless narrowed), each package's testdata/ at the +# path it has on the build host, and Go's zone database, and nothing else: one +# test binary is 4 to 15 MB against the store's 16 MiB and 128 entries +# (tools/nonos-store-pack). Changing this list needs only the store step. +ifeq ($(NONOS_LINUX_GO_SUITE),1) +NONOS_LINUX_GO_SUITE_STORE ?= $(NONOS_LINUX_GO_SUITE_PKGS) +GO_SUITE_ENTRY = --entry /linux/bin/$(1)=$(linux-guest-$(1)_BIN) \ + --entry /linux/bin/$(1).nonos_id_cert.bin=$(linux-guest-$(1)_CERT) \ + --entry /linux/bin/$(1).manifest.bin=$(linux-guest-$(1)_MANIFEST) \ + --entry /linux/bin/$(1).zk_trailer.bin=$(linux-guest-$(1)_ATTESTATION) +GO_SUITE_TESTDATA = $(foreach f,$(shell cd $(GO_ROOT)/src/$(1) && find testdata -type f 2>/dev/null | sort), \ + --entry /linux$(GO_ROOT)/src/$(1)/$(f)=$(GO_ROOT)/src/$(1)/$(f)) +LINUX_GUEST_STORE_ENTRIES := $(call GO_SUITE_ENTRY,gostd) \ + $(foreach p,$(NONOS_LINUX_GO_SUITE_STORE),$(call GO_SUITE_ENTRY,gs$(subst /,,$(p))) $(call GO_SUITE_TESTDATA,$(p))) \ + --entry /linux$(GO_ROOT)/lib/time/zoneinfo.zip=$(GO_ROOT)/lib/time/zoneinfo.zip \ + --entry /linux/etc/nonos-boot-guest=$(LINUX_GUEST_BOOT_FILE) +endif diff --git a/userland/linux_guests/Guests.mk b/userland/linux_guests/Guests.mk index c42c8e873..ebbcea41d 100644 --- a/userland/linux_guests/Guests.mk +++ b/userland/linux_guests/Guests.mk @@ -119,6 +119,28 @@ $(LINUX_GUESTS_C)/cwait: $(LINUX_GUESTS_DIR)/c/cwait.c @mkdir -p $(@D) && musl-gcc -O2 -static -o $@ $< $(eval $(call LINUX_GUEST,cwait,4978,4979,$(LINUX_GUESTS_C)/cwait)) +# Go's own standard-library tests as guests, opt in with NONOS_LINUX_GO_SUITE=1, +# so the default build does not grow: each test binary is the one `go test -c` +# makes, and the same bytes are run on the build host for comparison. +# NONOS_LINUX_GO_SUITE_PKGS names the packages enrolled; each is the guest +# gs, ids 5042 upward in list order, 29 at most in +# the 5040 to 5099 range. gostd (5040) changes to the package's directory and +# becomes its test binary, since go test runs each one there, beside testdata/. +ifeq ($(NONOS_LINUX_GO_SUITE),1) +NONOS_LINUX_GO_SUITE_PKGS ?= sync time os +GO_STD_OUT := $(TARGET_DIR)/linux-guests/go-std +GO_ROOT := $(shell $(GO) env GOROOT) +ifneq ($(word 30,$(NONOS_LINUX_GO_SUITE_PKGS)),) +$(error NONOS_LINUX_GO_SUITE_PKGS names more than the 29 packages ids 5042 to 5099 hold) +endif +$(GO_STD_OUT)/%.test: $(GO) + @mkdir -p $(@D) && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + GOCACHE=$(abspath $(GO_OUT))/cache GOPATH=$(abspath $(GO_OUT))/path \ + $(GO) test -c -o $(abspath $@) $(subst _,/,$*) +$(eval $(call LINUX_GUEST,gostd,5040,5041,$(GO_OUT)/std)) +$(foreach i,$(shell seq 1 $(words $(NONOS_LINUX_GO_SUITE_PKGS))),$(eval $(call LINUX_GUEST,gs$(subst /,,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))),$(shell expr 5040 + 2 \* $(i)),$(shell expr 5041 + 2 \* $(i)),$(GO_STD_OUT)/$(subst /,_,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))).test))) +endif + # The Linux-guest test store is about guests, not the desktop's media and demo # capsules. Drop both so the signed guest set fits the vfs load budget; the # normal image, which does not set NONOS_LINUX_GUESTS, still ships them. diff --git a/userland/linux_guests/go/std/go.mod b/userland/linux_guests/go/std/go.mod new file mode 100644 index 000000000..2b74c965e --- /dev/null +++ b/userland/linux_guests/go/std/go.mod @@ -0,0 +1,3 @@ +module nonos/guest/std + +go 1.24 diff --git a/userland/linux_guests/go/std/main.go b/userland/linux_guests/go/std/main.go new file mode 100644 index 000000000..033c48f2c --- /dev/null +++ b/userland/linux_guests/go/std/main.go @@ -0,0 +1,33 @@ +// The start of a Go standard-library test inside the guest: go test runs each +// test binary from its package's directory, where testdata/ sits, and the boot +// guest always starts at /. So this changes to the directory named first and +// then becomes the program named second, with the rest as its arguments and +// the environment unchanged: +// +// /bin/gostd /usr/local/go/src/time /bin/gstime -test.v -test.short +// +// A refused chdir or execve is printed with its errno and ends with status +// 127, the shell's status for a program that could not be run. +package main + +import ( + "fmt" + "os" + "syscall" +) + +func main() { + if len(os.Args) < 3 { + fmt.Fprintln(os.Stderr, "[GOSTD] usage: gostd [args...]") + os.Exit(127) + } + dir, prog := os.Args[1], os.Args[2] + if err := syscall.Chdir(dir); err != nil { + fmt.Fprintf(os.Stderr, "[GOSTD] chdir %s: %v\n", dir, err) + os.Exit(127) + } + argv := append([]string{prog}, os.Args[3:]...) + err := syscall.Exec(prog, argv, os.Environ()) + fmt.Fprintf(os.Stderr, "[GOSTD] execve %s: %v\n", prog, err) + os.Exit(127) +} From 861d2000e805e80f553f5dde51ac22c0dda03c49 Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Mon, 28 Sep 2026 20:12:16 +0000 Subject: [PATCH 02/11] linux: gostd sets environment words before the test it runs A Go test that hung inside the guest could not be looked into: the boot guest's environment is fixed, so GODEBUG and GOTRACEBACK could not be set. gostd now adds NAME=value words that come between the directory and the program to the program's environment, as env(1) does. A run can ask Go's scheduler to trace itself, or turn a Go setting on or off, from the boot file alone. --- userland/linux_guests/go/std/main.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/userland/linux_guests/go/std/main.go b/userland/linux_guests/go/std/main.go index 033c48f2c..9c9a484f2 100644 --- a/userland/linux_guests/go/std/main.go +++ b/userland/linux_guests/go/std/main.go @@ -1,10 +1,11 @@ // The start of a Go standard-library test inside the guest: go test runs each // test binary from its package's directory, where testdata/ sits, and the boot // guest always starts at /. So this changes to the directory named first and -// then becomes the program named second, with the rest as its arguments and -// the environment unchanged: +// then becomes the program named after it, with the rest as its arguments. +// NAME=value words between the two are added to the environment, as env(1) +// adds them, so a run can set GODEBUG or GOTRACEBACK: // -// /bin/gostd /usr/local/go/src/time /bin/gstime -test.v -test.short +// /bin/gostd /usr/local/go/src/time GODEBUG=schedtrace=1000 /bin/gstime -test.v // // A refused chdir or execve is printed with its errno and ends with status // 127, the shell's status for a program that could not be run. @@ -13,6 +14,7 @@ package main import ( "fmt" "os" + "strings" "syscall" ) @@ -21,13 +23,16 @@ func main() { fmt.Fprintln(os.Stderr, "[GOSTD] usage: gostd [args...]") os.Exit(127) } - dir, prog := os.Args[1], os.Args[2] + dir, rest, env := os.Args[1], os.Args[2:], os.Environ() + for len(rest) > 1 && !strings.HasPrefix(rest[0], "/") && strings.Contains(rest[0], "=") { + env, rest = append(env, rest[0]), rest[1:] + } if err := syscall.Chdir(dir); err != nil { fmt.Fprintf(os.Stderr, "[GOSTD] chdir %s: %v\n", dir, err) os.Exit(127) } - argv := append([]string{prog}, os.Args[3:]...) - err := syscall.Exec(prog, argv, os.Environ()) + prog, argv := rest[0], rest + err := syscall.Exec(prog, argv, env) fmt.Fprintf(os.Stderr, "[GOSTD] execve %s: %v\n", prog, err) os.Exit(127) } From c73bd4469f35171d03cee291c9e87112b3a43bd7 Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Mon, 28 Sep 2026 21:24:35 +0000 Subject: [PATCH 03/11] linux: a Go test's own sources travel with it into the guest Go runs each test binary in its package's directory, and some tests read their own sources there: sync's ExampleOnceValues reads example_test.go. The suite image carried only testdata/, so from / that example read nothing and its result never came back, and a package without testdata/ had no directory for gostd to change into. A suite image now also carries each package's *_test.go files at their build-host paths, so every package runs from its own directory, as go test runs it. NONOS_LINUX_GO_SUITE_SOURCES=0 leaves them out for runtime, whose testdata/ alone nearly fills the store's 128 entries. --- userland/linux_guests/GuestFiles.mk | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/userland/linux_guests/GuestFiles.mk b/userland/linux_guests/GuestFiles.mk index b6abc052f..e4c439ae6 100644 --- a/userland/linux_guests/GuestFiles.mk +++ b/userland/linux_guests/GuestFiles.mk @@ -43,17 +43,24 @@ LINUX_GUEST_STORE_ENTRIES += --entry /linux/lib/libprobe_bad.so=$(LINUX_GUEST_BA --entry /linux/lib/libprobe_bad.so.zk_trailer.bin=$(linux-guest-libprobe_ATTESTATION) # A Go suite image holds the wrapper, the packages NONOS_LINUX_GO_SUITE_STORE -# names (all enrolled ones unless narrowed), each package's testdata/ at the -# path it has on the build host, and Go's zone database, and nothing else: one -# test binary is 4 to 15 MB against the store's 16 MiB and 128 entries -# (tools/nonos-store-pack). Changing this list needs only the store step. +# names (all enrolled ones unless narrowed), each package's testdata/ and test +# sources at the paths they have on the build host, and Go's zone database, +# and nothing else: one test binary is 4 to 15 MB against the store's 16 MiB +# and 128 entries (tools/nonos-store-pack). Changing this list needs only the +# store step. ifeq ($(NONOS_LINUX_GO_SUITE),1) NONOS_LINUX_GO_SUITE_STORE ?= $(NONOS_LINUX_GO_SUITE_PKGS) GO_SUITE_ENTRY = --entry /linux/bin/$(1)=$(linux-guest-$(1)_BIN) \ --entry /linux/bin/$(1).nonos_id_cert.bin=$(linux-guest-$(1)_CERT) \ --entry /linux/bin/$(1).manifest.bin=$(linux-guest-$(1)_MANIFEST) \ --entry /linux/bin/$(1).zk_trailer.bin=$(linux-guest-$(1)_ATTESTATION) -GO_SUITE_TESTDATA = $(foreach f,$(shell cd $(GO_ROOT)/src/$(1) && find testdata -type f 2>/dev/null | sort), \ +# A test also reads its own sources: an example reads example_test.go, and +# the package directory exists for gostd to change into only if something is +# in it. NONOS_LINUX_GO_SUITE_SOURCES=0 leaves them out for a package whose +# testdata alone nearly fills the 128 entries (runtime). +NONOS_LINUX_GO_SUITE_SOURCES ?= 1 +GO_SUITE_SOURCES = $(if $(filter 1,$(NONOS_LINUX_GO_SUITE_SOURCES)),$(notdir $(wildcard $(GO_ROOT)/src/$(1)/*_test.go))) +GO_SUITE_TESTDATA = $(foreach f,$(shell cd $(GO_ROOT)/src/$(1) && find testdata -type f 2>/dev/null | sort) $(GO_SUITE_SOURCES), \ --entry /linux$(GO_ROOT)/src/$(1)/$(f)=$(GO_ROOT)/src/$(1)/$(f)) LINUX_GUEST_STORE_ENTRIES := $(call GO_SUITE_ENTRY,gostd) \ $(foreach p,$(NONOS_LINUX_GO_SUITE_STORE),$(call GO_SUITE_ENTRY,gs$(subst /,,$(p))) $(call GO_SUITE_TESTDATA,$(p))) \ From e006fb8c84528a12e68d97b7b0eb922fcba5442e Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Mon, 28 Sep 2026 22:28:02 +0000 Subject: [PATCH 04/11] linux: gostd is a C program, so it takes no signals of its own gostd was a Go program, and the Go runtime starts its own threads and asks for SIGURG to preempt them. A Go test run with async preemption turned off still hung before its first test: the SIGURG went to gostd's own handler, before it reached the test, and a Go setting given to the test cannot reach the program that starts it. gostd is now static C: it changes directory, adds the NAME=value words to the environment and execs the test, with no runtime, no threads and no signal handlers. time now reaches its tests through it. --- userland/linux_guests/Guests.mk | 9 ++++-- userland/linux_guests/go/std/go.mod | 3 -- userland/linux_guests/go/std/gostd.c | 41 ++++++++++++++++++++++++++++ userland/linux_guests/go/std/main.go | 38 -------------------------- 4 files changed, 47 insertions(+), 44 deletions(-) delete mode 100644 userland/linux_guests/go/std/go.mod create mode 100644 userland/linux_guests/go/std/gostd.c delete mode 100644 userland/linux_guests/go/std/main.go diff --git a/userland/linux_guests/Guests.mk b/userland/linux_guests/Guests.mk index ebbcea41d..84699b909 100644 --- a/userland/linux_guests/Guests.mk +++ b/userland/linux_guests/Guests.mk @@ -124,8 +124,9 @@ $(eval $(call LINUX_GUEST,cwait,4978,4979,$(LINUX_GUESTS_C)/cwait)) # makes, and the same bytes are run on the build host for comparison. # NONOS_LINUX_GO_SUITE_PKGS names the packages enrolled; each is the guest # gs, ids 5042 upward in list order, 29 at most in -# the 5040 to 5099 range. gostd (5040) changes to the package's directory and -# becomes its test binary, since go test runs each one there, beside testdata/. +# the 5040 to 5099 range. gostd (5040), a static C program, changes to the +# package's directory and becomes its test binary, since go test runs each one +# there, beside testdata/. ifeq ($(NONOS_LINUX_GO_SUITE),1) NONOS_LINUX_GO_SUITE_PKGS ?= sync time os GO_STD_OUT := $(TARGET_DIR)/linux-guests/go-std @@ -137,7 +138,9 @@ $(GO_STD_OUT)/%.test: $(GO) @mkdir -p $(@D) && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ GOCACHE=$(abspath $(GO_OUT))/cache GOPATH=$(abspath $(GO_OUT))/path \ $(GO) test -c -o $(abspath $@) $(subst _,/,$*) -$(eval $(call LINUX_GUEST,gostd,5040,5041,$(GO_OUT)/std)) +$(LINUX_GUESTS_C)/gostd: $(LINUX_GUESTS_DIR)/go/std/gostd.c + @mkdir -p $(@D) && musl-gcc -O2 -static -o $@ $< +$(eval $(call LINUX_GUEST,gostd,5040,5041,$(LINUX_GUESTS_C)/gostd)) $(foreach i,$(shell seq 1 $(words $(NONOS_LINUX_GO_SUITE_PKGS))),$(eval $(call LINUX_GUEST,gs$(subst /,,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))),$(shell expr 5040 + 2 \* $(i)),$(shell expr 5041 + 2 \* $(i)),$(GO_STD_OUT)/$(subst /,_,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))).test))) endif diff --git a/userland/linux_guests/go/std/go.mod b/userland/linux_guests/go/std/go.mod deleted file mode 100644 index 2b74c965e..000000000 --- a/userland/linux_guests/go/std/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module nonos/guest/std - -go 1.24 diff --git a/userland/linux_guests/go/std/gostd.c b/userland/linux_guests/go/std/gostd.c new file mode 100644 index 000000000..14dd7a712 --- /dev/null +++ b/userland/linux_guests/go/std/gostd.c @@ -0,0 +1,41 @@ +/* + * The start of a Go standard-library test inside the guest. go test runs each + * test binary from its package's directory, beside testdata/, and the boot + * guest always starts at /. So this changes to the directory named first and + * becomes the program named after it, with the rest as its arguments. + * NAME=value words between the two are added to the environment, as env(1) + * adds them, so a run can set GODEBUG or GOTRACEBACK: + * + * /bin/gostd /usr/local/go/src/time GODEBUG=schedtrace=1000 /bin/gstime -test.v + * + * It is C, not Go: a Go runtime here would take signals of its own before the + * test starts, and the test's own settings could not reach it. A refused chdir + * or execve is printed with its errno and ends with status 127, the shell's + * status for a program that could not be run. + */ +#include +#include +#include +#include +#include + +extern char **environ; + +int main(int argc, char **argv) +{ + int i = 2; + + if (argc < 3) { + fprintf(stderr, "[GOSTD] usage: gostd [NAME=value...] [args...]\n"); + return 127; + } + for (; i < argc - 1 && argv[i][0] != '/' && strchr(argv[i], '='); i++) + putenv(argv[i]); + if (chdir(argv[1]) != 0) { + fprintf(stderr, "[GOSTD] chdir %s: %s\n", argv[1], strerror(errno)); + return 127; + } + execve(argv[i], argv + i, environ); + fprintf(stderr, "[GOSTD] execve %s: %s\n", argv[i], strerror(errno)); + return 127; +} diff --git a/userland/linux_guests/go/std/main.go b/userland/linux_guests/go/std/main.go deleted file mode 100644 index 9c9a484f2..000000000 --- a/userland/linux_guests/go/std/main.go +++ /dev/null @@ -1,38 +0,0 @@ -// The start of a Go standard-library test inside the guest: go test runs each -// test binary from its package's directory, where testdata/ sits, and the boot -// guest always starts at /. So this changes to the directory named first and -// then becomes the program named after it, with the rest as its arguments. -// NAME=value words between the two are added to the environment, as env(1) -// adds them, so a run can set GODEBUG or GOTRACEBACK: -// -// /bin/gostd /usr/local/go/src/time GODEBUG=schedtrace=1000 /bin/gstime -test.v -// -// A refused chdir or execve is printed with its errno and ends with status -// 127, the shell's status for a program that could not be run. -package main - -import ( - "fmt" - "os" - "strings" - "syscall" -) - -func main() { - if len(os.Args) < 3 { - fmt.Fprintln(os.Stderr, "[GOSTD] usage: gostd [args...]") - os.Exit(127) - } - dir, rest, env := os.Args[1], os.Args[2:], os.Environ() - for len(rest) > 1 && !strings.HasPrefix(rest[0], "/") && strings.Contains(rest[0], "=") { - env, rest = append(env, rest[0]), rest[1:] - } - if err := syscall.Chdir(dir); err != nil { - fmt.Fprintf(os.Stderr, "[GOSTD] chdir %s: %v\n", dir, err) - os.Exit(127) - } - prog, argv := rest[0], rest - err := syscall.Exec(prog, argv, env) - fmt.Fprintf(os.Stderr, "[GOSTD] execve %s: %v\n", prog, err) - os.Exit(127) -} From 87c953f367979fe7025b9bcbd4eaf2554b8fedb4 Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Tue, 29 Sep 2026 00:22:59 +0000 Subject: [PATCH 05/11] linux: a Go suite image carries all of a package's Go sources A test may read any file of its package, not only its own: io/fs TestGlob globs for glob.go and path/filepath TestGlob for match.go. The image carried only *_test.go, so both failed on NONOS and passed on the host. Every .go file of the package is now packed when they fit the store's 128 entries, and only *_test.go otherwise (os and syscall, with 153 and 298 files). io/fs now passes all 18 of the tests the host passes. --- userland/linux_guests/GuestFiles.mk | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/userland/linux_guests/GuestFiles.mk b/userland/linux_guests/GuestFiles.mk index e4c439ae6..51b896387 100644 --- a/userland/linux_guests/GuestFiles.mk +++ b/userland/linux_guests/GuestFiles.mk @@ -59,7 +59,12 @@ GO_SUITE_ENTRY = --entry /linux/bin/$(1)=$(linux-guest-$(1)_BIN) \ # in it. NONOS_LINUX_GO_SUITE_SOURCES=0 leaves them out for a package whose # testdata alone nearly fills the 128 entries (runtime). NONOS_LINUX_GO_SUITE_SOURCES ?= 1 -GO_SUITE_SOURCES = $(if $(filter 1,$(NONOS_LINUX_GO_SUITE_SOURCES)),$(notdir $(wildcard $(GO_ROOT)/src/$(1)/*_test.go))) +GO_SUITE_SOURCES = $(if $(filter 1,$(NONOS_LINUX_GO_SUITE_SOURCES)),$(call GO_SUITE_GO,$(1))) +# Every .go file where they fit, since a test may glob or read the package's +# other sources (io/fs TestGlob reads glob.go); otherwise only *_test.go (os +# and syscall have 153 and 298 files against the 128 entries). +GO_SUITE_ALL = $(notdir $(wildcard $(GO_ROOT)/src/$(1)/*.go)) +GO_SUITE_GO = $(if $(word 100,$(GO_SUITE_ALL)),$(notdir $(wildcard $(GO_ROOT)/src/$(1)/*_test.go)),$(GO_SUITE_ALL)) GO_SUITE_TESTDATA = $(foreach f,$(shell cd $(GO_ROOT)/src/$(1) && find testdata -type f 2>/dev/null | sort) $(GO_SUITE_SOURCES), \ --entry /linux$(GO_ROOT)/src/$(1)/$(f)=$(GO_ROOT)/src/$(1)/$(f)) LINUX_GUEST_STORE_ENTRIES := $(call GO_SUITE_ENTRY,gostd) \ From c948c98a911dd68df5e57f34ea3f53dcf7634ab1 Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Tue, 29 Sep 2026 01:35:52 +0000 Subject: [PATCH 06/11] linux: the personality's run heap holds a 16 MiB program A run read each program it starts or execs whole into one buffer that doubles as it fills, inside a fixed 16 MiB heap. A Go test binary of 4.9 MB was enough: its execve asked for an 8 MiB buffer, the allocation failed, and the personality ended with every guest it hosted ("memory allocation of 8388608 bytes failed", exit 134). A run now takes a 40 MiB heap: the 24 MiB peak of reading the largest program the kernel verifies (16 MiB), on top of the 16 MiB every run had. Go's runtime (13.5 MB) and encoding/json (11.9 MB) test binaries now load. The heap is committed when it is made, so each Linux run holds 40 MiB. --- userland/capsule_linux/src/linux/heap.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/userland/capsule_linux/src/linux/heap.rs b/userland/capsule_linux/src/linux/heap.rs index b669aa944..13ccfd162 100644 --- a/userland/capsule_linux/src/linux/heap.rs +++ b/userland/capsule_linux/src/linux/heap.rs @@ -21,9 +21,19 @@ use nonos_libc::{heap_init, heap_init_sized, mk_args}; /// An install holds a distribution's index while it resolves a closure. /// Kali's main is 21 MB fetched and 85 MB inflated, parsed into records -/// beside it; Alpine's is a few. A run takes the default. +/// beside it; Alpine's is a few. A run takes RUN_HEAP. const INSTALL_HEAP: usize = 320 << 20; +/* + * A run reads each program it starts or execs whole into one buffer that + * doubles as it fills, and the kernel verifies a program of up to 16 MiB + * (capsule_load/copy.rs MAX_ARTIFACT): 8 MiB outgrown beside 16 MiB at the + * peak. On top of that, the 16 MiB every run held before, which served guests + * whose programs are a few MB; a 4.9 MB Go program's execve ended the whole + * family there, failing to allocate its 8 MiB buffer. + */ +const RUN_HEAP: usize = (16 << 20) + (24 << 20); + pub fn init() { let mut buf = [0u8; 256]; let n = mk_args(buf.as_mut_ptr(), buf.len()); @@ -36,5 +46,10 @@ pub fn init() { let line = b"[LINUX] no room for a large index, installing in the default heap\n"; let _ = nonos_libc::mk_debug(line.as_ptr(), line.len()); } + if heap_init_sized(RUN_HEAP).is_ok() { + return; + } + let line = b"[LINUX] no room for a 40 MiB heap, running in the default 16 MiB\n"; + let _ = nonos_libc::mk_debug(line.as_ptr(), line.len()); let _ = heap_init(); } From 65d9c3fd2f43366b05602ede1da2a54a104f42f1 Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Tue, 29 Sep 2026 01:35:52 +0000 Subject: [PATCH 07/11] linux: the first program's bytes go once it is running The program a run starts was held in the heap for the whole life of the personality, though nothing reads it after it is mapped into the guest. Every execve that followed had that much less room to read its own program into. start() now takes the launch rather than borrowing it, and its bytes are freed when the program is running. --- userland/capsule_linux/src/linux/start.rs | 2 +- userland/capsule_linux/src/linux/start_guest.rs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/userland/capsule_linux/src/linux/start.rs b/userland/capsule_linux/src/linux/start.rs index d7b1d9b38..034693388 100644 --- a/userland/capsule_linux/src/linux/start.rs +++ b/userland/capsule_linux/src/linux/start.rs @@ -54,7 +54,7 @@ pub fn run() -> ! { } let mut guest = Guest::new(pid as u32); guest.links = alloc::rc::Rc::new(super::guest::Links::load()); - let code = match start(&mut guest, &launch) { + let code = match start(&mut guest, launch) { Ok(()) => { say(b"[LINUX] guest running\n"); serve(guest) diff --git a/userland/capsule_linux/src/linux/start_guest.rs b/userland/capsule_linux/src/linux/start_guest.rs index c4802a5ee..094c1cc23 100644 --- a/userland/capsule_linux/src/linux/start_guest.rs +++ b/userland/capsule_linux/src/linux/start_guest.rs @@ -25,7 +25,12 @@ use super::launch::Launch; use super::origin::Origin; use super::start::say; -pub(super) fn start(guest: &mut Guest, launch: &Launch) -> Result<(), &'static [u8]> { +/* + * The launch is taken, not borrowed: once the program is mapped its bytes are + * never read again, and kept they would hold up to 16 MiB of the heap that + * every later execve reads its own program into. They go when this returns. + */ +pub(super) fn start(guest: &mut Guest, launch: Launch) -> Result<(), &'static [u8]> { let (path, bytes) = (&launch.path[..], &launch.bytes[..]); if let Err(why) = prove(path, bytes, &launch.origin) { say(b"[LINUX] refused: "); From 51f0dc8b61997aeba7bdcf3b4af6087f4178db5a Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Tue, 29 Sep 2026 01:35:52 +0000 Subject: [PATCH 08/11] linux: console writes over 256 bytes reach the log The kernel's debug channel refuses a line over 256 bytes whole, and the console ignored that and told the guest every byte was written. Longer writes vanished: Go prints a parallel test's results in one write, so context's TestCause and its 20 subtests never reached the log. The console now forwards in pieces of at most 256 bytes and returns the count it carried, a short write if the log refuses part way. context now reports all 65 of its tests. --- .../capsule_linux/src/linux/call/console.rs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/userland/capsule_linux/src/linux/call/console.rs b/userland/capsule_linux/src/linux/call/console.rs index f70884604..fe37c6f69 100644 --- a/userland/capsule_linux/src/linux/call/console.rs +++ b/userland/capsule_linux/src/linux/call/console.rs @@ -23,8 +23,17 @@ use crate::linux::guest::Guest; /// Cap on one transfer, matching the kernel's own peer-copy ceiling. const MAX_IO: u64 = 1 << 20; +/* The longest line the kernel's debug channel takes; it refuses a longer one + * whole (src/syscall/microkernel/debug.rs MAX_LEN). */ +const MAX_LINE: usize = 256; + /// A guest's console output, carried to the host's log. The bytes are the /// guest's and are never interpreted, only forwarded. +/* + * Forwarded in pieces the kernel takes. The count returned is what was + * carried: a write the log refuses part way is a short write, as Linux + * reports one, never a claimed success. + */ pub(super) fn console(guest: &Guest, buf: u64, len: u64) -> u64 { if len == 0 { return errno::ok(0); @@ -33,7 +42,16 @@ pub(super) fn console(guest: &Guest, buf: u64, len: u64) -> u64 { let Some(bytes) = guest.read(buf, take as usize) else { return errno::fail(errno::EFAULT); }; - let _ = nonos_libc::mk_debug(bytes.as_ptr(), bytes.len()); - errno::ok(take) + let mut done = 0; + for piece in bytes.chunks(MAX_LINE) { + if nonos_libc::mk_debug(piece.as_ptr(), piece.len()) < 0 { + break; + } + done += piece.len(); + } + match done { + 0 => errno::fail(errno::EIO), + n => errno::ok(n as u64), + } } From f8c4711f06ac60a6bae40238b8008a7aaa647878 Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Tue, 29 Sep 2026 01:35:53 +0000 Subject: [PATCH 09/11] linux: a boot file over 1024 bytes is refused, not cut The boot guest's file was read with a 1024-byte limit, and the store cuts a longer file short without saying so. A long argument lost its end, and the program ran with an argument nobody gave it: a -test.skip pattern lost its closing parenthesis and the Go test binary exited at once. A file over 1024 bytes is now refused with a line that says why. --- userland/capsule_linux/src/linux/boot_guest.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/userland/capsule_linux/src/linux/boot_guest.rs b/userland/capsule_linux/src/linux/boot_guest.rs index 8b604395b..9c30233ea 100644 --- a/userland/capsule_linux/src/linux/boot_guest.rs +++ b/userland/capsule_linux/src/linux/boot_guest.rs @@ -54,7 +54,13 @@ fn read_when_ready() -> Option> { if !super::settle::wait_settled() { return None; } - match store_read(&key(BOOT_GUEST), MAX_NAME) { + /* One byte past the limit is asked for: the store cuts a longer file + * short without saying so, and a cut line is an argument never given. */ + match store_read(&key(BOOT_GUEST), MAX_NAME + 1) { + Ok(named) if named.len() > MAX_NAME as usize => { + say(b"[LINUX] boot guest refused: its file is over 1024 bytes\n"); + None + } Ok(named) => Some(named), Err("vfs open failed") => None, Err(_) => { From fc6e26ba314b4e58e6d85e2db340716ac1853042 Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Tue, 29 Sep 2026 04:45:41 +0000 Subject: [PATCH 10/11] linux: execbig proves a large program can exec itself Nothing showed whether the personality let a program's bytes go once it was running: the Go test binaries that exec themselves open /dev/null first, and fail there before any second image is read. execbig is a static C program with 12 MiB of initialised data that execs itself once and prints a line from each image. With the first image's bytes released it prints both and ends with status 0, as on Linux; with them held, the personality fails to allocate 16 MiB for the second read. NONOS_LINUX_GO_SUITE_EXECBIG=1 enrols it on the id pair after the packages, and the word execbig in NONOS_LINUX_GO_SUITE_STORE packs it. --- userland/linux_guests/GuestFiles.mk | 15 +++++++------ userland/linux_guests/Guests.mk | 12 ++++++++++ userland/linux_guests/go/std/execbig.c | 31 ++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 userland/linux_guests/go/std/execbig.c diff --git a/userland/linux_guests/GuestFiles.mk b/userland/linux_guests/GuestFiles.mk index 51b896387..035c227b6 100644 --- a/userland/linux_guests/GuestFiles.mk +++ b/userland/linux_guests/GuestFiles.mk @@ -43,11 +43,11 @@ LINUX_GUEST_STORE_ENTRIES += --entry /linux/lib/libprobe_bad.so=$(LINUX_GUEST_BA --entry /linux/lib/libprobe_bad.so.zk_trailer.bin=$(linux-guest-libprobe_ATTESTATION) # A Go suite image holds the wrapper, the packages NONOS_LINUX_GO_SUITE_STORE -# names (all enrolled ones unless narrowed), each package's testdata/ and test -# sources at the paths they have on the build host, and Go's zone database, -# and nothing else: one test binary is 4 to 15 MB against the store's 16 MiB -# and 128 entries (tools/nonos-store-pack). Changing this list needs only the -# store step. +# names (all enrolled ones unless narrowed; the word execbig adds that proof), +# each package's testdata/ and sources at the paths they have on the build +# host, and Go's zone database, and nothing else: one test binary is 4 to 15 +# MB against the store's 16 MiB and 128 entries (tools/nonos-store-pack). +# Changing this list needs only the store step. ifeq ($(NONOS_LINUX_GO_SUITE),1) NONOS_LINUX_GO_SUITE_STORE ?= $(NONOS_LINUX_GO_SUITE_PKGS) GO_SUITE_ENTRY = --entry /linux/bin/$(1)=$(linux-guest-$(1)_BIN) \ @@ -68,7 +68,8 @@ GO_SUITE_GO = $(if $(word 100,$(GO_SUITE_ALL)),$(notdir $(wildcard $(GO_ROOT)/sr GO_SUITE_TESTDATA = $(foreach f,$(shell cd $(GO_ROOT)/src/$(1) && find testdata -type f 2>/dev/null | sort) $(GO_SUITE_SOURCES), \ --entry /linux$(GO_ROOT)/src/$(1)/$(f)=$(GO_ROOT)/src/$(1)/$(f)) LINUX_GUEST_STORE_ENTRIES := $(call GO_SUITE_ENTRY,gostd) \ - $(foreach p,$(NONOS_LINUX_GO_SUITE_STORE),$(call GO_SUITE_ENTRY,gs$(subst /,,$(p))) $(call GO_SUITE_TESTDATA,$(p))) \ + $(foreach p,$(filter-out execbig,$(NONOS_LINUX_GO_SUITE_STORE)),$(call GO_SUITE_ENTRY,gs$(subst /,,$(p))) $(call GO_SUITE_TESTDATA,$(p))) \ --entry /linux$(GO_ROOT)/lib/time/zoneinfo.zip=$(GO_ROOT)/lib/time/zoneinfo.zip \ - --entry /linux/etc/nonos-boot-guest=$(LINUX_GUEST_BOOT_FILE) + --entry /linux/etc/nonos-boot-guest=$(LINUX_GUEST_BOOT_FILE) \ + $(if $(filter execbig,$(NONOS_LINUX_GO_SUITE_STORE)),$(call GO_SUITE_ENTRY,execbig)) endif diff --git a/userland/linux_guests/Guests.mk b/userland/linux_guests/Guests.mk index 84699b909..de94180a4 100644 --- a/userland/linux_guests/Guests.mk +++ b/userland/linux_guests/Guests.mk @@ -142,6 +142,18 @@ $(LINUX_GUESTS_C)/gostd: $(LINUX_GUESTS_DIR)/go/std/gostd.c @mkdir -p $(@D) && musl-gcc -O2 -static -o $@ $< $(eval $(call LINUX_GUEST,gostd,5040,5041,$(LINUX_GUESTS_C)/gostd)) $(foreach i,$(shell seq 1 $(words $(NONOS_LINUX_GO_SUITE_PKGS))),$(eval $(call LINUX_GUEST,gs$(subst /,,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))),$(shell expr 5040 + 2 \* $(i)),$(shell expr 5041 + 2 \* $(i)),$(GO_STD_OUT)/$(subst /,_,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))).test))) +# A 12 MB program that execs itself: it runs only if the personality lets the +# first program's bytes go once it is running. It takes the ids after the +# packages, so the list is one shorter when it is asked for. +ifeq ($(NONOS_LINUX_GO_SUITE_EXECBIG),1) +GO_SUITE_EXECBIG_ID := $(shell expr 5042 + 2 \* $(words $(NONOS_LINUX_GO_SUITE_PKGS))) +ifneq ($(shell test $(GO_SUITE_EXECBIG_ID) -le 5098 && echo ok),ok) +$(error NONOS_LINUX_GO_SUITE_EXECBIG=1 needs a free id pair; name at most 28 packages) +endif +$(LINUX_GUESTS_C)/execbig: $(LINUX_GUESTS_DIR)/go/std/execbig.c + @mkdir -p $(@D) && musl-gcc -O2 -static -o $@ $< +$(eval $(call LINUX_GUEST,execbig,$(GO_SUITE_EXECBIG_ID),$(shell expr $(GO_SUITE_EXECBIG_ID) + 1),$(LINUX_GUESTS_C)/execbig)) +endif endif # The Linux-guest test store is about guests, not the desktop's media and demo diff --git a/userland/linux_guests/go/std/execbig.c b/userland/linux_guests/go/std/execbig.c new file mode 100644 index 000000000..478210187 --- /dev/null +++ b/userland/linux_guests/go/std/execbig.c @@ -0,0 +1,31 @@ +/* + * A 12 MB program that execs itself once. The personality reads a program + * whole before it runs it, so this second read of the same 12 MB happens + * while the first program's bytes could still be held: it passes only if + * the personality let them go once the first one was running. + * + * /bin/execbig prints its first line, then execs /bin/execbig again + * /bin/execbig second prints its second line and ends with status 0 + */ +#include +#include +#include +#include + +/* Initialised, so all 12 MiB are in the file, not left for the loader. */ +static volatile char blob[12 << 20] = { 1 }; + +int main(int argc, char **argv) +{ + char *again[] = { "/bin/execbig", "second", NULL }; + + if (argc > 1 && strcmp(argv[1], "second") == 0) { + printf("[EXECBIG] second image running, first byte %d\n", blob[0]); + return 0; + } + printf("[EXECBIG] first image running, %zu bytes of data\n", sizeof(blob)); + fflush(stdout); + execve(again[0], again, NULL); + printf("[EXECBIG] execve refused: %s\n", strerror(errno)); + return 1; +} From f5b957b46a32cd34e043db72796c47c627992ec7 Mon Sep 17 00:00:00 2001 From: eKisNonos Date: Tue, 29 Sep 2026 07:00:27 +0000 Subject: [PATCH 11/11] linux: the Go suite's guests are listed in GoSuite.mk The suite's block had grown Guests.mk, the registry every lane adds to, by 37 lines, past the size a file here is kept to. It now lives in GoSuite.mk, which Guests.mk includes at the same place; nothing it builds or packs changes. A suite store for io/fs still holds 27 entries and io/fs still passes all 18 of the tests the host passes. --- userland/linux_guests/GoSuite.mk | 38 ++++++++++++++++++++++++++++++++ userland/linux_guests/Guests.mk | 38 ++------------------------------ 2 files changed, 40 insertions(+), 36 deletions(-) create mode 100644 userland/linux_guests/GoSuite.mk diff --git a/userland/linux_guests/GoSuite.mk b/userland/linux_guests/GoSuite.mk new file mode 100644 index 000000000..269a77422 --- /dev/null +++ b/userland/linux_guests/GoSuite.mk @@ -0,0 +1,38 @@ +# The Go standard-library suite's guests, included by Guests.mk. + +# Go's own standard-library tests as guests, opt in with NONOS_LINUX_GO_SUITE=1, +# so the default build does not grow: each test binary is the one `go test -c` +# makes, and the same bytes are run on the build host for comparison. +# NONOS_LINUX_GO_SUITE_PKGS names the packages enrolled; each is the guest +# gs, ids 5042 upward in list order, 29 at most in +# the 5040 to 5099 range. gostd (5040), a static C program, changes to the +# package's directory and becomes its test binary, since go test runs each one +# there, beside testdata/. +ifeq ($(NONOS_LINUX_GO_SUITE),1) +NONOS_LINUX_GO_SUITE_PKGS ?= sync time os +GO_STD_OUT := $(TARGET_DIR)/linux-guests/go-std +GO_ROOT := $(shell $(GO) env GOROOT) +ifneq ($(word 30,$(NONOS_LINUX_GO_SUITE_PKGS)),) +$(error NONOS_LINUX_GO_SUITE_PKGS names more than the 29 packages ids 5042 to 5099 hold) +endif +$(GO_STD_OUT)/%.test: $(GO) + @mkdir -p $(@D) && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + GOCACHE=$(abspath $(GO_OUT))/cache GOPATH=$(abspath $(GO_OUT))/path \ + $(GO) test -c -o $(abspath $@) $(subst _,/,$*) +$(LINUX_GUESTS_C)/gostd: $(LINUX_GUESTS_DIR)/go/std/gostd.c + @mkdir -p $(@D) && musl-gcc -O2 -static -o $@ $< +$(eval $(call LINUX_GUEST,gostd,5040,5041,$(LINUX_GUESTS_C)/gostd)) +$(foreach i,$(shell seq 1 $(words $(NONOS_LINUX_GO_SUITE_PKGS))),$(eval $(call LINUX_GUEST,gs$(subst /,,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))),$(shell expr 5040 + 2 \* $(i)),$(shell expr 5041 + 2 \* $(i)),$(GO_STD_OUT)/$(subst /,_,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))).test))) +# A 12 MB program that execs itself: it runs only if the personality lets the +# first program's bytes go once it is running. It takes the ids after the +# packages, so the list is one shorter when it is asked for. +ifeq ($(NONOS_LINUX_GO_SUITE_EXECBIG),1) +GO_SUITE_EXECBIG_ID := $(shell expr 5042 + 2 \* $(words $(NONOS_LINUX_GO_SUITE_PKGS))) +ifneq ($(shell test $(GO_SUITE_EXECBIG_ID) -le 5098 && echo ok),ok) +$(error NONOS_LINUX_GO_SUITE_EXECBIG=1 needs a free id pair; name at most 28 packages) +endif +$(LINUX_GUESTS_C)/execbig: $(LINUX_GUESTS_DIR)/go/std/execbig.c + @mkdir -p $(@D) && musl-gcc -O2 -static -o $@ $< +$(eval $(call LINUX_GUEST,execbig,$(GO_SUITE_EXECBIG_ID),$(shell expr $(GO_SUITE_EXECBIG_ID) + 1),$(LINUX_GUESTS_C)/execbig)) +endif +endif diff --git a/userland/linux_guests/Guests.mk b/userland/linux_guests/Guests.mk index de94180a4..601c60e4b 100644 --- a/userland/linux_guests/Guests.mk +++ b/userland/linux_guests/Guests.mk @@ -119,42 +119,8 @@ $(LINUX_GUESTS_C)/cwait: $(LINUX_GUESTS_DIR)/c/cwait.c @mkdir -p $(@D) && musl-gcc -O2 -static -o $@ $< $(eval $(call LINUX_GUEST,cwait,4978,4979,$(LINUX_GUESTS_C)/cwait)) -# Go's own standard-library tests as guests, opt in with NONOS_LINUX_GO_SUITE=1, -# so the default build does not grow: each test binary is the one `go test -c` -# makes, and the same bytes are run on the build host for comparison. -# NONOS_LINUX_GO_SUITE_PKGS names the packages enrolled; each is the guest -# gs, ids 5042 upward in list order, 29 at most in -# the 5040 to 5099 range. gostd (5040), a static C program, changes to the -# package's directory and becomes its test binary, since go test runs each one -# there, beside testdata/. -ifeq ($(NONOS_LINUX_GO_SUITE),1) -NONOS_LINUX_GO_SUITE_PKGS ?= sync time os -GO_STD_OUT := $(TARGET_DIR)/linux-guests/go-std -GO_ROOT := $(shell $(GO) env GOROOT) -ifneq ($(word 30,$(NONOS_LINUX_GO_SUITE_PKGS)),) -$(error NONOS_LINUX_GO_SUITE_PKGS names more than the 29 packages ids 5042 to 5099 hold) -endif -$(GO_STD_OUT)/%.test: $(GO) - @mkdir -p $(@D) && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - GOCACHE=$(abspath $(GO_OUT))/cache GOPATH=$(abspath $(GO_OUT))/path \ - $(GO) test -c -o $(abspath $@) $(subst _,/,$*) -$(LINUX_GUESTS_C)/gostd: $(LINUX_GUESTS_DIR)/go/std/gostd.c - @mkdir -p $(@D) && musl-gcc -O2 -static -o $@ $< -$(eval $(call LINUX_GUEST,gostd,5040,5041,$(LINUX_GUESTS_C)/gostd)) -$(foreach i,$(shell seq 1 $(words $(NONOS_LINUX_GO_SUITE_PKGS))),$(eval $(call LINUX_GUEST,gs$(subst /,,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))),$(shell expr 5040 + 2 \* $(i)),$(shell expr 5041 + 2 \* $(i)),$(GO_STD_OUT)/$(subst /,_,$(word $(i),$(NONOS_LINUX_GO_SUITE_PKGS))).test))) -# A 12 MB program that execs itself: it runs only if the personality lets the -# first program's bytes go once it is running. It takes the ids after the -# packages, so the list is one shorter when it is asked for. -ifeq ($(NONOS_LINUX_GO_SUITE_EXECBIG),1) -GO_SUITE_EXECBIG_ID := $(shell expr 5042 + 2 \* $(words $(NONOS_LINUX_GO_SUITE_PKGS))) -ifneq ($(shell test $(GO_SUITE_EXECBIG_ID) -le 5098 && echo ok),ok) -$(error NONOS_LINUX_GO_SUITE_EXECBIG=1 needs a free id pair; name at most 28 packages) -endif -$(LINUX_GUESTS_C)/execbig: $(LINUX_GUESTS_DIR)/go/std/execbig.c - @mkdir -p $(@D) && musl-gcc -O2 -static -o $@ $< -$(eval $(call LINUX_GUEST,execbig,$(GO_SUITE_EXECBIG_ID),$(shell expr $(GO_SUITE_EXECBIG_ID) + 1),$(LINUX_GUESTS_C)/execbig)) -endif -endif +# Go's own standard-library tests as guests, opt in (GoSuite.mk). +include $(LINUX_GUESTS_DIR)/GoSuite.mk # The Linux-guest test store is about guests, not the desktop's media and demo # capsules. Drop both so the signed guest set fits the vfs load budget; the