Fix Linux cross-build to use the rustls backend (musl)
ober
20979c71f47cf99f8c299fa4f85a2ed4316b6420
--- a/.gitignore +++ b/.gitignore @@ -6,9 +6,13 @@ *.so *.wpo *.boot +# Host-side emit artifacts from the cross-build (ta6le emit mode) +*.tarm64osx +*.ta6le # Generated by the build (see support/gen-ffi-symbols.sh and cross scripts) support/ffi-symbols.gen +support/ffi-symbols-linux.gen jgl-main.c jgl-*-main.c *_boot.h --- a/Makefile +++ b/Makefile @@ -16,12 +16,21 @@ BIN_DIR := $(HOME)/.local/bin # loader at jerbuild's cache for `make run`/`make test`. DYLD := DYLD_FALLBACK_LIBRARY_PATH="$(NATIVE_DIR)" LD_LIBRARY_PATH="$(NATIVE_DIR):$$LD_LIBRARY_PATH" -# Cross-compilation prerequisites (shared across the jerboa-* family). +# --- Cross-compilation (macOS -> Linux x86_64 musl) --- +# jgl's HTTPS uses rustls (jerboa-native), so the cross build links a musl-cross +# build of that crate (not the OpenSSL shim). Requires: a full Chez 'scheme' +# (falls back to the jerboa build tree), the cross-built Chez + xpatch under +# JERBOA_HOME, x86_64-linux-musl-gcc, and a rustup toolchain with the +# x86_64-unknown-linux-musl target (the recipe cargo-builds the crate). JERBOA_HOME ?= $(HOME)/mine/jerboa -SCHEME ?= scheme -CROSS_LIBDIRS ?= lib:$(JERBOA_HOME)/lib:$(HOME)/mine/jerboa-https/src:$(HOME)/mine/jerboa-ssl/src +SCHEME ?= $(shell command -v scheme 2>/dev/null || echo $(JERBOA_HOME)/build/chez/tarm64osx/bin/tarm64osx/scheme) +CROSS_LIBDIRS ?= lib:$(JERBOA_HOME)/lib +RUSTUP_TC ?= stable +MUSL_TARGET := x86_64-unknown-linux-musl +MUSL_CC ?= x86_64-linux-musl-gcc +NATIVE_MUSL_A := $(JH)/jerboa-native-rs/target/$(MUSL_TARGET)/release/libjerboa_native.a -.PHONY: all build binary run test install clean help cross-linux cross-freebsd +.PHONY: all build binary run test install clean help cross-linux .DEFAULT_GOAL := help all: binary @@ -48,17 +57,25 @@ install: binary install -m 0755 $(BIN) $(BIN_DIR)/$(BIN) @echo "Installed $(BIN) to $(BIN_DIR)/$(BIN)" -# Cross-compile from macOS to Linux x86_64 musl (static) / FreeBSD 14 amd64. -# See build-jgl-*-cross.ss headers for the required cross toolchain. +# Cross-compile macOS -> Linux x86_64 musl (static). Builds the rustls native +# crate for musl, lists its FFI symbols, then cross-links jgl-linux-amd64. cross-linux: - JERBOA_HOME=$(JERBOA_HOME) $(SCHEME) -q --libdirs $(CROSS_LIBDIRS) --script build-jgl-cross.ss - -cross-freebsd: - JERBOA_HOME=$(JERBOA_HOME) $(SCHEME) -q --libdirs $(CROSS_LIBDIRS) --script build-jgl-freebsd-cross.ss + cd $(JH)/jerboa-native-rs && \ + RUSTC="$$(rustup which --toolchain $(RUSTUP_TC) rustc)" \ + CC_x86_64_unknown_linux_musl=$(MUSL_CC) \ + AR_x86_64_unknown_linux_musl=x86_64-linux-musl-ar \ + CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=$(MUSL_CC) \ + "$$(rustup which --toolchain $(RUSTUP_TC) cargo)" build --release \ + --target $(MUSL_TARGET) --no-default-features --features tls,crypto + x86_64-linux-musl-nm -g --defined-only $(NATIVE_MUSL_A) | awk '{print $$NF}' \ + | grep -E '^jerboa_[A-Za-z0-9_]+$$' | sort -u > support/ffi-symbols-linux.gen + JERBOA_HOME=$(JERBOA_HOME) JGL_NATIVE_A=$(NATIVE_MUSL_A) \ + JGL_FFI_SYMBOLS=support/ffi-symbols-linux.gen \ + $(SCHEME) -q --libdirs $(CROSS_LIBDIRS) --script build-jgl-cross.ss clean: - rm -f $(BIN) jgl-linux-amd64 jgl-freebsd-amd64 jgl-*main.c *.h *.boot *.wp.so - find . \( -name '*.so' -o -name '*.wpo' \) -delete 2>/dev/null || true + rm -f $(BIN) jgl-linux-amd64 jgl-*main.c *.h *.boot *.wp.so + find . \( -name '*.so' -o -name '*.wpo' -o -name '*.tarm64osx' -o -name '*.ta6le' \) -delete 2>/dev/null || true help: @echo "jerboa-gitlab -- the jgl GitLab CLI (jerbuild + cargo + cc)" @@ -67,6 +84,5 @@ help: @echo " make run ARGS='...' Build + run ./jgl" @echo " make test Run the test suite" @echo " make install Build + install to ~/.local/bin/jgl" - @echo " make cross-linux Cross-build jgl-linux-amd64 (musl static)" - @echo " make cross-freebsd Cross-build jgl-freebsd-amd64" + @echo " make cross-linux Cross-build jgl-linux-amd64 (musl static, rustls)" @echo " make clean Remove build artifacts" --- a/README.md +++ b/README.md @@ -112,19 +112,29 @@ download fails the original link is left intact. ## Cross-compilation -The `make binary` path is portable (it runs natively on each OS). To build -Linux/FreeBSD binaries *from macOS*, two scripts adapt the proven -`jerboa-aws` cross pattern (which uses the `jerboa-ssl`/OpenSSL static path): +`make binary` is portable — it runs natively on macOS, Linux, and FreeBSD. To +build a Linux x86_64 binary *from macOS*: ```sh -make cross-linux # -> jgl-linux-amd64 (x86_64 musl, static) -make cross-freebsd # -> jgl-freebsd-amd64 (x86_64, dynamic) +make cross-linux # -> jgl-linux-amd64 (static musl ELF) ``` -These require the shared cross toolchain used across the `jerboa-*` family — -a cross-built Chez install, the `jerboa-ssl` shim object, and musl-OpenSSL / -a FreeBSD sysroot. See the headers of `build-jgl-cross.ss` and -`build-jgl-freebsd-cross.ss` for the exact prerequisites and env overrides. +jgl's HTTPS uses rustls (the `jerboa-native` crate), so this cross-builds that +crate for `x86_64-unknown-linux-musl` and static-links it — the same TLS +backend as the native build (not the OpenSSL shim some older `jerboa-*` cross +scripts use). It needs: + +- a full Chez `scheme` compiler — the Makefile falls back to + `$JERBOA_HOME/build/chez/tarm64osx/bin/tarm64osx/scheme` if none is on PATH; +- the cross-built Chez + xpatch under `$JERBOA_HOME` (`.chez-cross-ta6le`, + `build/chez/xc-ta6le/s/xpatch`); +- `x86_64-linux-musl-gcc`; +- a rustup toolchain with the musl target (`rustup target add x86_64-unknown-linux-musl`). + +Override `JERBOA_HOME`, `SCHEME`, `RUSTUP_TC`, or `MUSL_CC` on the make line as +needed; see `build-jgl-cross.ss` for the details. (A FreeBSD cross would follow +the same shape with the `x86_64-unknown-freebsd` Rust target + a sysroot; not +wired up yet.) ## Layout --- a/build-jgl-cross.ss +++ b/build-jgl-cross.ss @@ -1,46 +1,45 @@ #!chezscheme ;;; build-jgl-cross.ss — Cross-compile jgl from macOS arm64 to Linux x86_64 musl. ;;; -;;; Usage (from the repo root): -;;; JERBOA_HOME=$HOME/mine/jerboa scheme -q \ -;;; --libdirs lib:$HOME/mine/jerboa/lib:$HOME/mine/jerboa-https/src:$HOME/mine/jerboa-ssl/src \ -;;; --script build-jgl-cross.ss -;;; (or: make cross-linux) +;;; jgl's HTTPS client uses (std net request) -> (std net tls-rustls), i.e. the +;;; Rust `jerboa-native` crate (rustls). So the cross binary must link a +;;; musl-cross build of that crate and register its jerboa_* FFI symbols — the +;;; same backend as the native `jerbuild build`. (The jerboa-ssl/OpenSSL shim +;;; path used by older jerboa-* cross scripts only provides jerboa_ssl_*/ +;;; jerboa_tcp_* and would fail at runtime on jerboa_tls_connect.) ;;; -;;; Adapted from jerboa-aws/build-jaws-cross.ss — jgl uses the identical -;;; (std net request) networking stack, so the same SSL-shim static-link path -;;; applies. Prerequisites (built once, shared across the jerboa-* family): -;;; - $JERBOA_HOME/.chez-cross-ta6le/ — cross-built Chez install (ta6le) -;;; - $JERBOA_HOME/build/chez/xc-ta6le/s/xpatch — host -> ta6le emit-mode loader -;;; - x86_64-linux-musl-gcc — C compile + final static link -;;; - ~/mine/jerboa-ssl/jerboa_ssl_shim-linux-musl.o (built by jerboa-ssl Makefile) -;;; - ~/musl-openssl/usr/{lib,include} — Alpine static libssl.a + libcrypto.a +;;; Driven by `make cross-linux`, which first cargo-builds the crate for +;;; x86_64-unknown-linux-musl and writes the symbol list. Inputs via env: +;;; JGL_NATIVE_A path to musl libjerboa_native.a +;;; JGL_FFI_SYMBOLS file of jerboa_* symbols (one per line) to register +;;; JERBOA_HOME jerboa source checkout (cross Chez + stdlib) +;;; CROSS_CC musl gcc (default x86_64-linux-musl-gcc) +;;; +;;; Prerequisites (shared across the jerboa-* family): +;;; - $JERBOA_HOME/.chez-cross-ta6le/ cross-built Chez (ta6le) +;;; - $JERBOA_HOME/build/chez/xc-ta6le/s/xpatch host -> ta6le emit loader +;;; - x86_64-linux-musl-gcc C compile + static link +;;; - rustup target x86_64-unknown-linux-musl (the Makefile builds the .a) ;;; ;;; Produces: jgl-linux-amd64 (static Linux x86_64 ELF). (import (chezscheme)) ;; ── Params ────────────────────────────────────────────────────────────────── -(define jerboa-home - (or (getenv "JERBOA_HOME") "/Users/user/mine/jerboa")) - +(define jerboa-home (or (getenv "JERBOA_HOME") "/Users/user/mine/jerboa")) (define cross-prefix (format "~a/.chez-cross-ta6le" jerboa-home)) (define xpatch (format "~a/build/chez/xc-ta6le/s/xpatch" jerboa-home)) (define cross-cc (or (getenv "CROSS_CC") "x86_64-linux-musl-gcc")) - (define output "jgl-linux-amd64") (define entry-script "jgl.ss") +(define jgl-repo (current-directory)) -(define jgl-repo (current-directory)) -(define jerboa-ssl-repo - (or (getenv "JERBOASSL_REPO") (format "~a/mine/jerboa-ssl" (getenv "HOME")))) -(define jerboa-https-repo - (or (getenv "JERBOAHTTPS_REPO") (format "~a/mine/jerboa-https" (getenv "HOME")))) -(define jerboa-ssl-shim-o - (or (getenv "JERBOA_SSL_SHIM_LINUX_O") - (format "~a/jerboa_ssl_shim-linux-musl.o" jerboa-ssl-repo))) -(define musl-openssl-dir - (or (getenv "MUSL_OPENSSL_DIR") (format "~a/musl-openssl" (getenv "HOME")))) +(define native-a + (or (getenv "JGL_NATIVE_A") + (error 'build-jgl-cross "set JGL_NATIVE_A to the musl libjerboa_native.a (use 'make cross-linux')"))) +(define ffi-symbols-file + (or (getenv "JGL_FFI_SYMBOLS") + (error 'build-jgl-cross "set JGL_FFI_SYMBOLS to the jerboa_* symbol list (use 'make cross-linux')"))) (define cross-csv-dir (let ([lib (format "~a/lib" cross-prefix)]) @@ -56,40 +55,52 @@ (format "~a/~a/ta6le" lib (car csvs))))) (define (require-file p) - (unless (file-exists? p) - (error 'build-jgl-cross "missing file" p))) + (unless (file-exists? p) (error 'build-jgl-cross "missing file" p))) (require-file xpatch) -(require-file (format "~a/libkernel.a" cross-csv-dir)) -(require-file (format "~a/scheme.h" cross-csv-dir)) -(require-file (format "~a/petite.boot" cross-csv-dir)) -(require-file (format "~a/scheme.boot" cross-csv-dir)) +(require-file (format "~a/libkernel.a" cross-csv-dir)) +(require-file (format "~a/scheme.h" cross-csv-dir)) +(require-file (format "~a/petite.boot" cross-csv-dir)) +(require-file (format "~a/scheme.boot" cross-csv-dir)) (require-file entry-script) -(require-file jerboa-ssl-shim-o) -(require-file (format "~a/usr/lib/libssl.a" musl-openssl-dir)) -(require-file (format "~a/usr/lib/libcrypto.a" musl-openssl-dir)) - -(printf "==> build-jgl-cross~n") -(printf " JERBOA_HOME: ~a~n" jerboa-home) -(printf " cross csv-dir: ~a~n" cross-csv-dir) -(printf " xpatch: ~a~n" xpatch) -(printf " cross-cc: ~a~n" cross-cc) -(printf " jerboa-ssl shim: ~a~n" jerboa-ssl-shim-o) -(printf " musl openssl: ~a~n" musl-openssl-dir) -(printf " output: ~a~n" output) -(printf "~n") +(require-file native-a) +(require-file ffi-symbols-file) + +;; Read the jerboa_* symbols to register with Sforeign_symbol. +(define jerboa-symbols + (call-with-input-file ffi-symbols-file + (lambda (p) + (let loop ([acc '()]) + (let ([line (get-line p)]) + (if (eof-object? line) + (reverse acc) + (let ([s (let trim ([i 0]) + (if (and (< i (string-length line)) + (memv (string-ref line i) '(#\space #\tab))) + (trim (+ i 1)) + (substring line i (string-length line))))]) + (if (and (>= (string-length s) 7) (string=? (substring s 0 7) "jerboa_")) + (loop (cons s acc)) + (loop acc))))))))) + +(printf "==> build-jgl-cross (rustls/musl)~n") +(printf " JERBOA_HOME: ~a~n" jerboa-home) +(printf " cross csv-dir: ~a~n" cross-csv-dir) +(printf " cross-cc: ~a~n" cross-cc) +(printf " native .a: ~a~n" native-a) +(printf " ffi symbols: ~a (~a symbols)~n" ffi-symbols-file (length jerboa-symbols)) +(printf " output: ~a~n~n" output) +(when (null? jerboa-symbols) + (error 'build-jgl-cross "no jerboa_* symbols found in" ffi-symbols-file)) ;; ── Stage 1: load xpatch (target=ta6le emit mode) ────────────────────────── -;; xpatch resets library-directories; restore + extend with our source libdirs. (define orig-libdirs (library-directories)) (printf "==> [1/6] loading xpatch (compiler -> ta6le emit mode)~n") (load xpatch) (library-directories (append - (list (cons (format "~a/lib" jgl-repo) (format "~a/lib" jgl-repo)) - (cons (format "~a/lib" jerboa-home) (format "~a/lib" jerboa-home)) - (cons (format "~a/src" jerboa-https-repo) (format "~a/src" jerboa-https-repo)) - (cons (format "~a/src" jerboa-ssl-repo) (format "~a/src" jerboa-ssl-repo))) + (list (cons (format "~a/lib" jgl-repo) (format "~a/lib" jgl-repo)) + (cons (format "~a/lib" jerboa-home) (format "~a/lib" jerboa-home))) orig-libdirs)) (compile-imported-libraries #t) @@ -98,7 +109,6 @@ ;; ── Stage 2: compile-program jgl.ss ───────────────────────────────────────── (printf "==> [2/6] compile-program ~a~n" entry-script) (compile-program entry-script) - (define entry-wpo (let ([n (string-length entry-script)]) (string-append (substring entry-script 0 (- n 3)) ".wpo"))) @@ -113,9 +123,7 @@ (let* ([bv (call-with-port (open-file-input-port in-path) get-bytevector-all)] [n (bytevector-length bv)]) (call-with-port (open-file-output-port out-path - (file-options no-fail) - (buffer-mode block) - (native-transcoder)) + (file-options no-fail) (buffer-mode block) (native-transcoder)) (lambda (out) (display (format "static const unsigned char ~a[] = {\n" var-name) out) (let loop ([i 0]) @@ -125,9 +133,7 @@ (loop (+ i 1)))) (when (positive? n) (newline out)) (display "};\n" out) - (display (format "static const unsigned int ~a_size = sizeof(~a);\n" - var-name var-name) - out))) + (display (format "static const unsigned int ~a_size = sizeof(~a);\n" var-name var-name) out))) (printf " embed ~a (~a bytes) -> ~a~n" in-path n out-path))) (printf "==> [4/6] embed boot files + program as C arrays~n") @@ -135,26 +141,9 @@ (embed-as-c-array (format "~a/scheme.boot" cross-csv-dir) "scheme_boot" "scheme_boot.h") (embed-as-c-array wpo-output "jgl_program" "jgl_program.h") -;; ── Stage 5: generate main.c ─────────────────────────────────────────────── +;; ── Stage 5: generate main.c (registers the crate's jerboa_* symbols) ─────── (define main-c-path (string-append output "-main.c")) - -;; jerboa-ssl/jerboa_ssl_shim.c symbol surface — must match the foreign-procedure -;; declarations in jerboa-ssl.sls. -(define jerboa-ssl-symbols - '("jerboa_ssl_init" "jerboa_ssl_cleanup" - "jerboa_ssl_connect" "jerboa_ssl_write" "jerboa_ssl_read" - "jerboa_ssl_read_all" "jerboa_ssl_free_buf" "jerboa_ssl_close" - "jerboa_ssl_memcpy" - "jerboa_ssl_server_ctx" "jerboa_ssl_server_accept" "jerboa_ssl_server_ctx_free" - "jerboa_tcp_listen" "jerboa_tcp_accept" - "jerboa_tcp_connect" "jerboa_tcp_close" - "jerboa_tcp_read" "jerboa_tcp_write" "jerboa_tcp_read_all" - "jerboa_tcp_set_timeout" - "jerboa_tcp_conn_wrap" - "jerboa_conn_write" "jerboa_conn_read")) - -(printf "==> [5/6] generate ~a (~a jerboa-ssl symbols)~n" - main-c-path (length jerboa-ssl-symbols)) +(printf "==> [5/6] generate ~a (~a jerboa_* symbols)~n" main-c-path (length jerboa-symbols)) (define (write-main-c) (call-with-port @@ -163,36 +152,24 @@ (lambda (out) (display "/* Auto-generated by build-jgl-cross.ss. Do not edit. */\n" out) (display "#define _GNU_SOURCE\n" out) - (display "#include <stdio.h>\n" out) - (display "#include <stdlib.h>\n" out) - (display "#include <string.h>\n" out) - (display "#include <unistd.h>\n" out) - (display "#include <sys/mman.h>\n" out) - (display "#include <fcntl.h>\n" out) - (display "#include \"scheme.h\"\n" out) - (display "#include \"petite_boot.h\"\n" out) - (display "#include \"scheme_boot.h\"\n" out) - (display "#include \"jgl_program.h\"\n\n" out) - - ;; dlopen stubs (musl-static has no dlopen) - (display "/* dlopen stubs for static linking. Returning non-NULL for any\n" out) - (display " * named load makes (load-shared-object \"...\") succeed silently;\n" out) - (display " * foreign-procedure lookup uses Sforeign_symbol-registered entries. */\n" out) - (display "void *dlopen(const char *f, int flags) { (void)f; (void)flags; return (void*)1; }\n" out) - (display "void *dlsym(void *h, const char *s) { (void)h; (void)s; return NULL; }\n" out) - (display "int dlclose(void *h) { (void)h; return 0; }\n" out) - (display "static char dlerror_msg[] = \"static binary: dlopen stubbed\";\n" out) - (display "char *dlerror(void) { return dlerror_msg; }\n\n" out) - - ;; extern decls for jerboa-ssl symbols - (display "/* jerboa_ssl_shim.o exports — registered with Sforeign_symbol below. */\n" out) - (for-each - (lambda (sym) (display (format "extern void ~a(void);\n" sym) out)) - jerboa-ssl-symbols) + (display "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n" out) + (display "#include <unistd.h>\n#include <sys/mman.h>\n#include <fcntl.h>\n" out) + (display "#include \"scheme.h\"\n#include \"petite_boot.h\"\n" out) + (display "#include \"scheme_boot.h\"\n#include \"jgl_program.h\"\n\n" out) + + ;; dlopen stubs (musl-static has no dlopen); FFI uses the Sforeign_symbol table. + (display "void *dlopen(const char *f, int g){ (void)f;(void)g; return (void*)1; }\n" out) + (display "void *dlsym(void *h, const char *s){ (void)h;(void)s; return NULL; }\n" out) + (display "int dlclose(void *h){ (void)h; return 0; }\n" out) + (display "static char dlerr[] = \"static binary: dlopen stubbed\";\n" out) + (display "char *dlerror(void){ return dlerr; }\n\n" out) + + (display "/* jerboa-native (rustls/crypto) exports — registered below. */\n" out) + (for-each (lambda (sym) (display (format "extern void ~a(void);\n" sym) out)) + jerboa-symbols) (newline out) (display "int main(int argc, char *argv[]) {\n" out) - (display " /* Argv passthrough via env vars (matches the native jgl main). */\n" out) (display " char countbuf[32];\n" out) (display " snprintf(countbuf, sizeof(countbuf), \"%d\", argc - 1);\n" out) (display " setenv(\"JGL_ARGC\", countbuf, 1);\n" out) @@ -202,7 +179,6 @@ (display " setenv(name, argv[i], 1);\n" out) (display " }\n\n" out) - ;; Memfd-backed program path (display " int fd = memfd_create(\"jgl-program\", MFD_CLOEXEC);\n" out) (display " if (fd < 0) { perror(\"memfd_create\"); return 1; }\n" out) (display " if (write(fd, jgl_program, jgl_program_size) != (ssize_t)jgl_program_size) {\n" out) @@ -215,45 +191,32 @@ (display " Sregister_boot_file_bytes(\"petite\", (void*)petite_boot, petite_boot_size);\n" out) (display " Sregister_boot_file_bytes(\"scheme\", (void*)scheme_boot, scheme_boot_size);\n\n" out) - (display " /* Register jerboa_ssl_shim symbols so a -static binary resolves\n" out) - (display " * (foreign-procedure \"jerboa_ssl_*\" ...) without dlsym. */\n" out) - (for-each - (lambda (sym) - (display (format " Sforeign_symbol(\"~a\", (void*)~a);\n" sym sym) out)) - jerboa-ssl-symbols) + (display " /* Register jerboa-native symbols so a -static binary resolves\n" out) + (display " * (foreign-procedure \"jerboa_*\" ...) without dlsym. */\n" out) + (for-each (lambda (sym) + (display (format " Sforeign_symbol(\"~a\", (void*)~a);\n" sym sym) out)) + jerboa-symbols) (newline out) (display " Sbuild_heap(NULL, NULL);\n" out) (display " const char *script_args[] = { argv[0] };\n" out) - (display " int status = Sscheme_program(prog_path, 1, script_args);\n\n" out) - (display " close(fd);\n" out) - (display " Sscheme_deinit();\n" out) - (display " return status;\n" out) - (display "}\n" out)))) + (display " int status = Sscheme_program(prog_path, 1, script_args);\n" out) + (display " close(fd);\n Sscheme_deinit();\n return status;\n}\n" out)))) (write-main-c) ;; ── Stage 6: compile + link with cross-cc ────────────────────────────────── (printf "==> [6/6] compile + link with ~a~n" cross-cc) - (define link-cmd (format (string-append - "~a -static -O2 " - "-I~a -I~a/usr/include " - "~a ~a " + "~a -static -O2 -I~a ~a " "-Wl,--start-group " - "~a/libkernel.a ~a/libz.a ~a/liblz4.a " - "~a/usr/lib/libssl.a ~a/usr/lib/libcrypto.a " + "~a ~a/libkernel.a ~a/libz.a ~a/liblz4.a " "-lpthread -lm -ldl " - "-Wl,--end-group " - "-o ~a") - cross-cc - cross-csv-dir musl-openssl-dir - main-c-path jerboa-ssl-shim-o - cross-csv-dir cross-csv-dir cross-csv-dir - musl-openssl-dir musl-openssl-dir + "-Wl,--end-group -o ~a") + cross-cc cross-csv-dir main-c-path + native-a cross-csv-dir cross-csv-dir cross-csv-dir output)) - (printf " ~a~n" link-cmd) (unless (zero? (system link-cmd)) (error 'build-jgl-cross "link failed")) deleted file mode 100644 --- a/build-jgl-freebsd-cross.ss +++ /dev/null @@ -1,252 +0,0 @@ -#!chezscheme -;;; build-jgl-freebsd-cross.ss — Cross-compile jgl from macOS to FreeBSD 14 amd64. -;;; -;;; Usage (from the repo root): -;;; JERBOA_HOME=$HOME/mine/jerboa scheme -q \ -;;; --libdirs lib:$HOME/mine/jerboa/lib:$HOME/mine/jerboa-https/src:$HOME/mine/jerboa-ssl/src \ -;;; --script build-jgl-freebsd-cross.ss -;;; (or: make cross-freebsd) -;;; -;;; Adapted from jerboa-aws/build-jaws-freebsd-cross.ss. Prerequisites: -;;; - $JERBOA_HOME/.chez-cross-ta6fb/ — cross-built Chez install (ta6fb) -;;; - $JERBOA_HOME/build/chez/xc-ta6fb/s/xpatch — host -> ta6fb emit-mode loader -;;; - x86_64-unknown-freebsd14-clang — wrapper with sysroot + lld + libpath -;;; - ~/mine/jerboa-ssl/jerboa_ssl_shim-freebsd-amd64.o -;;; - ~/freebsd-sysroot/usr/lib/{libssl,libcrypto,...}.so (dynamic link) -;;; -;;; Produces: jgl-freebsd-amd64 (dynamic FreeBSD x86_64 ELF). Dynamic because -;;; FreeBSD libc.so uses symbol versioning that libc.a can't satisfy; libssl/ -;;; libcrypto are resolved from FreeBSD base at run time. - -(import (chezscheme)) - -;; ── Params ────────────────────────────────────────────────────────────────── -(define jerboa-home - (or (getenv "JERBOA_HOME") "/Users/user/mine/jerboa")) - -(define cross-prefix (format "~a/.chez-cross-ta6fb" jerboa-home)) -(define xpatch (format "~a/build/chez/xc-ta6fb/s/xpatch" jerboa-home)) -(define cross-cc (or (getenv "CROSS_CC") "x86_64-unknown-freebsd14-clang")) - -(define output "jgl-freebsd-amd64") -(define entry-script "jgl.ss") - -(define jgl-repo (current-directory)) -(define jerboa-ssl-repo - (or (getenv "JERBOASSL_REPO") (format "~a/mine/jerboa-ssl" (getenv "HOME")))) -(define jerboa-https-repo - (or (getenv "JERBOAHTTPS_REPO") (format "~a/mine/jerboa-https" (getenv "HOME")))) -(define jerboa-ssl-shim-o - (or (getenv "JERBOA_SSL_SHIM_FREEBSD_O") - (format "~a/jerboa_ssl_shim-freebsd-amd64.o" jerboa-ssl-repo))) -(define freebsd-sysroot - (or (getenv "FREEBSD_SYSROOT") (format "~a/freebsd-sysroot" (getenv "HOME")))) - -(define cross-csv-dir - (let ([lib (format "~a/lib" cross-prefix)]) - (unless (file-directory? lib) - (error 'build-jgl-freebsd-cross "cross prefix lib dir missing" lib)) - (let* ([entries (directory-list lib)] - [csvs (filter (lambda (e) - (and (>= (string-length e) 3) - (string=? (substring e 0 3) "csv"))) - entries)]) - (when (null? csvs) - (error 'build-jgl-freebsd-cross "no csv* in cross lib" lib)) - (format "~a/~a/ta6fb" lib (car csvs))))) - -(define (require-file p) - (unless (file-exists? p) - (error 'build-jgl-freebsd-cross "missing file" p))) - -(require-file xpatch) -(require-file (format "~a/libkernel.a" cross-csv-dir)) -(require-file (format "~a/scheme.h" cross-csv-dir)) -(require-file (format "~a/petite.boot" cross-csv-dir)) -(require-file (format "~a/scheme.boot" cross-csv-dir)) -(require-file entry-script) -(require-file jerboa-ssl-shim-o) -(require-file (format "~a/usr/lib/libssl.so" freebsd-sysroot)) - -(printf "==> build-jgl-freebsd-cross~n") -(printf " JERBOA_HOME: ~a~n" jerboa-home) -(printf " cross csv-dir: ~a~n" cross-csv-dir) -(printf " xpatch: ~a~n" xpatch) -(printf " cross-cc: ~a~n" cross-cc) -(printf " jerboa-ssl shim: ~a~n" jerboa-ssl-shim-o) -(printf " freebsd sysroot: ~a~n" freebsd-sysroot) -(printf " output: ~a~n" output) -(printf "~n") - -;; ── Stage 1: load xpatch (target=ta6fb emit mode) ────────────────────────── -(define orig-libdirs (library-directories)) -(printf "==> [1/6] loading xpatch (compiler -> ta6fb emit mode)~n") -(load xpatch) -(library-directories - (append - (list (cons (format "~a/lib" jgl-repo) (format "~a/lib" jgl-repo)) - (cons (format "~a/lib" jerboa-home) (format "~a/lib" jerboa-home)) - (cons (format "~a/src" jerboa-https-repo) (format "~a/src" jerboa-https-repo)) - (cons (format "~a/src" jerboa-ssl-repo) (format "~a/src" jerboa-ssl-repo))) - orig-libdirs)) - -(compile-imported-libraries #t) -(generate-wpo-files #t) - -;; ── Stage 2: compile-program jgl.ss ───────────────────────────────────────── -(printf "==> [2/6] compile-program ~a~n" entry-script) -(compile-program entry-script) - -(define entry-wpo - (let ([n (string-length entry-script)]) - (string-append (substring entry-script 0 (- n 3)) ".wpo"))) - -;; ── Stage 3: compile-whole-program → wpo .so ─────────────────────────────── -(define wpo-output (string-append output ".wp.so")) -(printf "==> [3/6] compile-whole-program ~a -> ~a~n" entry-wpo wpo-output) -(compile-whole-program entry-wpo wpo-output #t) - -;; ── Stage 4: embed boot files + program as C arrays ──────────────────────── -(define (embed-as-c-array in-path var-name out-path) - (let* ([bv (call-with-port (open-file-input-port in-path) get-bytevector-all)] - [n (bytevector-length bv)]) - (call-with-port (open-file-output-port out-path - (file-options no-fail) - (buffer-mode block) - (native-transcoder)) - (lambda (out) - (display (format "static const unsigned char ~a[] = {\n" var-name) out) - (let loop ([i 0]) - (when (< i n) - (display (format "0x~2,'0x," (bytevector-u8-ref bv i)) out) - (when (= (mod (+ i 1) 16) 0) (newline out)) - (loop (+ i 1)))) - (when (positive? n) (newline out)) - (display "};\n" out) - (display (format "static const unsigned int ~a_size = sizeof(~a);\n" - var-name var-name) - out))) - (printf " embed ~a (~a bytes) -> ~a~n" in-path n out-path))) - -(printf "==> [4/6] embed boot files + program as C arrays~n") -(embed-as-c-array (format "~a/petite.boot" cross-csv-dir) "petite_boot" "petite_boot.h") -(embed-as-c-array (format "~a/scheme.boot" cross-csv-dir) "scheme_boot" "scheme_boot.h") -(embed-as-c-array wpo-output "jgl_program" "jgl_program.h") - -;; ── Stage 5: generate main.c ─────────────────────────────────────────────── -(define main-c-path (string-append output "-main.c")) - -(define jerboa-ssl-symbols - '("jerboa_ssl_init" "jerboa_ssl_cleanup" - "jerboa_ssl_connect" "jerboa_ssl_write" "jerboa_ssl_read" - "jerboa_ssl_read_all" "jerboa_ssl_free_buf" "jerboa_ssl_close" - "jerboa_ssl_memcpy" - "jerboa_ssl_server_ctx" "jerboa_ssl_server_accept" "jerboa_ssl_server_ctx_free" - "jerboa_tcp_listen" "jerboa_tcp_accept" - "jerboa_tcp_connect" "jerboa_tcp_close" - "jerboa_tcp_read" "jerboa_tcp_write" "jerboa_tcp_read_all" - "jerboa_tcp_set_timeout" - "jerboa_tcp_conn_wrap" - "jerboa_conn_write" "jerboa_conn_read")) - -(printf "==> [5/6] generate ~a (~a jerboa-ssl symbols)~n" - main-c-path (length jerboa-ssl-symbols)) - -(define (write-main-c) - (call-with-port - (open-file-output-port main-c-path - (file-options no-fail) (buffer-mode block) (native-transcoder)) - (lambda (out) - (display "/* Auto-generated by build-jgl-freebsd-cross.ss. Do not edit. */\n" out) - (display "#include <stdio.h>\n" out) - (display "#include <stdlib.h>\n" out) - (display "#include <string.h>\n" out) - (display "#include <unistd.h>\n" out) - (display "#include <fcntl.h>\n" out) - (display "#include <sys/types.h>\n" out) - (display "#include <sys/sysctl.h>\n" out) - (display "#include \"scheme.h\"\n" out) - (display "#include \"petite_boot.h\"\n" out) - (display "#include \"scheme_boot.h\"\n" out) - (display "#include \"jgl_program.h\"\n\n" out) - - ;; FreeBSD: __errno_location alias (glibc/musl name) -> __error (FreeBSD). - (display "/* glibc/musl name for errno location -> FreeBSD __error. */\n" out) - (display "extern int *__error(void);\n" out) - (display "int *__errno_location(void) { return __error(); }\n\n" out) - - ;; extern decls for jerboa-ssl symbols - (display "/* jerboa_ssl_shim.o exports — registered with Sforeign_symbol below. */\n" out) - (for-each - (lambda (sym) (display (format "extern void ~a(void);\n" sym) out)) - jerboa-ssl-symbols) - (newline out) - - (display "int main(int argc, char *argv[]) {\n" out) - (display " /* Argv passthrough via env vars (matches the native jgl main). */\n" out) - (display " char countbuf[32];\n" out) - (display " snprintf(countbuf, sizeof(countbuf), \"%d\", argc - 1);\n" out) - (display " setenv(\"JGL_ARGC\", countbuf, 1);\n" out) - (display " for (int i = 1; i < argc; i++) {\n" out) - (display " char name[32];\n" out) - (display " snprintf(name, sizeof(name), \"JGL_ARG%d\", i - 1);\n" out) - (display " setenv(name, argv[i], 1);\n" out) - (display " }\n\n" out) - - ;; FreeBSD: no /proc/self/fd/N (without fdescfs), write to a tmpfile. - (display " /* FreeBSD has no /proc/self/fd/N (without fdescfs mounted), so write the\n" out) - (display " * embedded program to a tmpfile that we unlink after Sscheme_program loads it. */\n" out) - (display " char tmpl[] = \"/tmp/jgl-XXXXXX\";\n" out) - (display " int fd = mkstemp(tmpl);\n" out) - (display " if (fd < 0) { perror(\"mkstemp\"); return 1; }\n" out) - (display " if (write(fd, jgl_program, jgl_program_size) != (ssize_t)jgl_program_size) {\n" out) - (display " perror(\"write tmpfile\"); close(fd); unlink(tmpl); return 1;\n" out) - (display " }\n" out) - (display " fsync(fd);\n\n" out) - - (display " Sscheme_init(NULL);\n" out) - (display " Sregister_boot_file_bytes(\"petite\", (void*)petite_boot, petite_boot_size);\n" out) - (display " Sregister_boot_file_bytes(\"scheme\", (void*)scheme_boot, scheme_boot_size);\n\n" out) - - (display " /* Register jerboa_ssl_shim symbols so the WPO resolves them at runtime. */\n" out) - (for-each - (lambda (sym) - (display (format " Sforeign_symbol(\"~a\", (void*)~a);\n" sym sym) out)) - jerboa-ssl-symbols) - (newline out) - - (display " Sbuild_heap(NULL, NULL);\n" out) - (display " const char *script_args[] = { argv[0] };\n" out) - (display " int status = Sscheme_program(tmpl, 1, script_args);\n\n" out) - (display " close(fd);\n" out) - (display " unlink(tmpl);\n" out) - (display " Sscheme_deinit();\n" out) - (display " return status;\n" out) - (display "}\n" out)))) -(write-main-c) - -;; ── Stage 6: compile + link with cross-cc ────────────────────────────────── -;; FreeBSD dynamic link: libssl/libcrypto from base, no -static (libc symbol -;; versioning), libutil for openpty. -(printf "==> [6/6] compile + link with ~a~n" cross-cc) - -(define link-cmd - (format - (string-append - "~a -O2 -Wl,--export-dynamic " - "-I~a " - "~a ~a " - "~a/libkernel.a ~a/libz.a ~a/liblz4.a " - "-lssl -lcrypto -lpthread -lm -lutil " - "-o ~a") - cross-cc - cross-csv-dir - main-c-path jerboa-ssl-shim-o - cross-csv-dir cross-csv-dir cross-csv-dir - output)) - -(printf " ~a~n" link-cmd) -(unless (zero? (system link-cmd)) - (error 'build-jgl-freebsd-cross "link failed")) - -(printf "~n==> done: ~a~n" output)