updates
ober
3bf0aeb8db51497efb3965865f921149c8a18e6b
--- a/.gitignore +++ b/.gitignore @@ -88,3 +88,4 @@ tests/vm/seed/ tests/vm/*.log tests/vm/*.pid .claude/ +*.jpkg --- a/data/cookbooks.sexp +++ b/data/cookbooks.sexp @@ -5122,4 +5122,40 @@ "chez" "gotcha") ("title" . - "Chez does not sequence (let ...) init exprs: use let* when bindings have side effects"))) + "Chez does not sequence (let ...) init exprs: use let* when bindings have side effects")) + (("code" + . + ";; Scheme code can use foreign-procedure names as usual.\n;; In an embedded/standalone binary, the C host must register any symbols that\n;; are resolved from the host process instead of a shared object.\n\n;; example.ss\n(import (jsqlite api))\n(def db (sqlite-open \"/tmp/example.sqlite\"))\n(sqlite-close db)\n\n/* support/main.c excerpt */\n#include \"scheme.h\"\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/file.h>\n#include <sys/mman.h>\n\nstatic void register_posix_symbols(void) {\n Sforeign_symbol(\"open\", (void *)open);\n Sforeign_symbol(\"close\", (void *)close);\n Sforeign_symbol(\"flock\", (void *)flock);\n Sforeign_symbol(\"usleep\", (void *)usleep);\n Sforeign_symbol(\"ftruncate\", (void *)ftruncate);\n Sforeign_symbol(\"mmap\", (void *)mmap);\n Sforeign_symbol(\"munmap\", (void *)munmap);\n}\n\nint main(int argc, const char *argv[]) {\n Sscheme_init(NULL);\n Sbuild_heap(NULL, NULL);\n register_posix_symbols();\n return Sscheme_program(\"example.ss\", argc, argv);\n}\n") ("id" . "standalone-binary-register-ffi-symbols") + ("imports") + ("notes" + . + "If the embedded program fails with `Exception in foreign-procedure: no entry for \"open\"`, the symbol was visible to dynamically loaded Scheme but not registered in the standalone host. Register libc symbols manually from C with the correct system headers instead of autogenerating declarations such as `extern void open()`, which can conflict with platform prototypes, especially on macOS.") + ("tags" "ffi" "standalone-binary" "foreign-procedure" + "Sforeign_symbol" "jsqlite" "jerbuild") + ("title" + . + "Register foreign-procedure symbols in a standalone binary")) + (("code" + . + "# Makefile\nJERBUILD ?= jerbuild\nJH := $(shell $(JERBUILD) --jerboa-home 2>/dev/null)\nBIN := mytool\n\n.PHONY: binary\n\nbinary:\n\t@: > support/ffi-symbols.gen\n\t$(JERBUILD) build\n\tsh support/gen-ffi-symbols.sh\n\t$(JERBUILD) build\n\n# support/gen-ffi-symbols.sh\n#!/bin/sh\nset -e\nJH=\"$(jerbuild --jerboa-home 2>/dev/null)\"\nA=\"${JERBOA_NATIVE_A:-$JH/jerboa-native-rs/target/release/libjerboa_native.a}\"\nOUT=\"$(dirname \"$0\")/ffi-symbols.gen\"\nif [ ! -f \"$A\" ]; then\n echo \"gen-ffi-symbols: $A not found; run jerbuild build once first\" >&2\n : > \"$OUT\"\n exit 0\nfi\nNM_TOOL=\"${NM:-nm}\"\nTMP=\"$(mktemp \"${TMPDIR:-/tmp}/jerboa-ffi-symbols.XXXXXX\")\"\ntrap 'rm -f \"$TMP\"' EXIT\n\"$NM_TOOL\" -g \"$A\" > \"$TMP\" 2>/dev/null || true\n{\n echo \"# AUTOGENERATED from libjerboa_native.a\"\n awk '{print $NF}' \"$TMP\" |\n sed 's/^_//' |\n grep -E '^jerboa_[A-Za-z0-9_]+$' |\n sort -u\n} > \"$OUT\"\n") ("id" . "jerbuild-two-pass-generated-ffi-symbols") + ("imports") + ("notes" + . + "A first `jerbuild build` pass creates or updates `libjerboa_native.a` for the current platform/features. Generate the FFI symbol list from that archive, then run `jerbuild build` again so the final binary includes the generated registrations. Keep the generated file out of source control unless the build system requires checked-in generated files. For cross builds, set `JERBOA_NATIVE_A`, `NM`, or target-specific variables so the script inspects the target archive, not the host archive.") + ("tags" "jerbuild" "ffi" "binary" "libjerboa-native" "nm" + "cross-build") + ("title" + . + "Generate jerboa-native FFI symbols with a two-pass jerbuild binary build")) + (("code" + . + ";;; Machine-readable subcommand protocol for an editor frontend.\n(import :std/sugar\n :std/format\n :std/text/json)\n\n(def (json-hash . pairs)\n (let ([h (make-hash-table)])\n (let loop ([rest pairs])\n (unless (or (null? rest) (null? (cdr rest)))\n (hash-put! h (car rest) (cadr rest))\n (loop (cddr rest))))\n h))\n\n(def (json-output obj)\n (display (json-object->string obj))\n (newline))\n\n(def (ok key value)\n (json-output (json-hash \"ok\" #t key value)))\n\n(def (fail message)\n (json-output (json-hash \"ok\" #f \"error\" message))\n (exit 1))\n\n(def (parse-options args)\n (let ([opts (make-hash-table)])\n (let loop ([rest args])\n (cond\n [(null? rest) opts]\n [else\n (let ([arg (car rest)])\n (if (and (> (string-length arg) 2)\n (string=? (substring arg 0 2) \"--\"))\n (begin\n (when (null? (cdr rest))\n (fail (format \"Option ~a requires a value\" arg)))\n (hash-put! opts\n (string->symbol (substring arg 2 (string-length arg)))\n (cadr rest))\n (loop (cddr rest)))\n (fail (format \"Unexpected argument: ~a\" arg))))]))\n opts))\n\n(def (require-option opts key name)\n (or (hash-get opts key)\n (fail (format \"~a is required\" name))))\n\n(def (run-command args)\n (let ([cmd (if (pair? args) (car args) \"help\")]\n [rest (if (pair? args) (cdr args) '())])\n (cond\n [(or (string=? cmd \"help\") (string=? cmd \"--help\"))\n (ok \"commands\" '(\"echo\" \"help\"))]\n [(string=? cmd \"echo\")\n (let* ([opts (parse-options rest)]\n [text (require-option opts 'text \"--text\")])\n (ok \"message\" (json-hash \"text\" text)))]\n [else (fail (format \"Unknown command: ~a\" cmd))])))\n\n(run-command (cdr (command-line)))\n") ("id" . "json-command-surface-for-editor-frontends") + ("imports" ":std/sugar" ":std/format" ":std/text/json") + ("notes" + . + "Return one JSON object per invocation with an explicit `ok` boolean. Emit errors as JSON before exiting non-zero so the editor can show useful messages. Use string keys in response hash tables because many JSON consumers, including Emacs Lisp, naturally decode object keys as strings or configured symbols. Keep the protocol boring and stable: small subcommands, long flags, no human-formatted success text on stdout.") + ("tags" "json" "emacs" "frontend" "cli" "hash-table" + "command-protocol") + ("title" + . + "Expose a machine-readable JSON command surface for editor frontends"))) --- a/data/features.sexp +++ b/data/features.sexp @@ -1291,4 +1291,24 @@ ("use_case" . "Gating multi-commit work where 'make test' is not green on clean master, so a raw non-zero exit can't be trusted as 'I broke something'.") + ("votes" . 0)) + (("description" + . + "Standalone binaries that use foreign-procedure bindings can fail at runtime with errors such as `no entry for \"open\"` unless the C host registers symbols via Sforeign_symbol. Projects currently need custom main.c code plus a two-pass build that first builds libjerboa_native.a, scans it with nm, generates a symbol list, and rebuilds. jerbuild should provide a first-class way to include generated native symbols and explicit libc symbols in binary builds.") + ("estimated_token_reduction" + . + "~1000 tokens per binary/FFI debugging session and eliminates 2-4 custom build files per project") + ("example_scenario" + . + "An app using jsqlite works under `jerbuild exec` but the standalone binary fails with `Exception in foreign-procedure: no entry for \"open\"`. The maintainer has to write support/main.c, support/gen-ffi-symbols.sh, and run jerbuild twice.") + ("id" . "jerbuild-binary-auto-ffi-symbol-registration") + ("impact" . "medium") + ("tags" "jerbuild" "ffi" "binary" "foreign-procedure" + "native") + ("title" + . + "Let jerbuild binary builds auto-register native FFI symbols") + ("use_case" + . + "Building standalone Jerboa applications that use jsqlite, jerboa-native crypto/tls, or other FFI-backed modules.") ("votes" . 0))) --- a/data/security-rules.sexp +++ b/data/security-rules.sexp @@ -927,4 +927,13 @@ "gunzip-bytevector / inflate-bytevector decompress with NO size limit. On attacker-controlled input a few KB can expand to gigabytes (zip/decompression bomb), exhausting memory. (std compress zlib) provides bounded variants for exactly this reason.") ("pattern" . "\\((?:gunzip|inflate)-bytevector") ("scope" . "scheme") + ("severity" . "medium")) + (("id" . "c-shim-libc-ffi-void-prototype") + ("message" + . + "C shim declares a libc function as `extern void ...` for FFI registration. This mismatches the platform prototype and can cause compile errors or undefined behavior. Include the correct system header and pass the real function pointer to Sforeign_symbol instead.") + ("pattern" + . + "extern\\s+void\\s+(open|close|flock|mmap|munmap|ftruncate|usleep)\\s*\\(") + ("scope" . "c-shim") ("severity" . "medium")))