boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 3c5314adf20422fa3616120e78e0d1d23ce95d46
parent 4cd7ae1656d42c902f4e371502ea984b539bd9cf
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 29 Apr 2026 20:21:22 -0700

rename: lispcc → boot2 across the tree

The repo is named boot2 (per Makefile + container image name); a
handful of stragglers — comments, doc text, test fixtures, and one
file (lispcc-syscall.c) — still carried the old project name. Bulk
sed pass: lispcc-syscall.c → boot2-syscall.c (file rename, content
otherwise identical), and lispcc → boot2 in comments. No
behavioural changes.

Diffstat:
MP1/entry-libc.P1pp | 4++--
Mdocs/CC.md | 10+++++-----
Mdocs/LIBC.md | 18+++++++++---------
Mdocs/OS.md | 12++++++------
Mdocs/TCC-TODO.md | 6+++---
Mtests/cc-libc/01-write-syscall.c | 2+-
Mtests/cc-libc/02-write-libc.c | 2+-
Mtests/cc-libc/03-fputs-stdout.c | 2+-
Mtests/cc-libc/07-malloc-roundtrip.c | 2+-
Mtests/cc-libc/09-exit-code.c | 2+-
Mtests/cc-libc/10-file-roundtrip.c | 2+-
Mtests/cc-libc/11-fseek.c | 2+-
Mtests/cc-libc/12-unlink.c | 4++--
Avendor/mes-libc/boot2-syscall.c | 260+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dvendor/mes-libc/lispcc-syscall.c | 260-------------------------------------------------------------------------------
Mvendor/mes-libc/patches/ntoab-inline-defined.after | 2+-
Mvendor/mes-libc/unified-libc.c | 8++++----
17 files changed, 299 insertions(+), 299 deletions(-)

diff --git a/P1/entry-libc.P1pp b/P1/entry-libc.P1pp @@ -1,8 +1,8 @@ # P1/entry-libc.P1pp — executable entry stub for links that include -# our libc (vendor/mes-libc + lispcc-syscall.c). +# our libc (vendor/mes-libc + boot2-syscall.c). # # Same shape as entry-plain.P1pp but threads __libc_init in front of -# main. __libc_init (lispcc-syscall.c) reads argv's NULL terminator +# main. __libc_init (boot2-syscall.c) reads argv's NULL terminator # to populate `environ` so getenv()/etc don't dereference a NULL # environment pointer on the first call. argc/argv arrive in a0/a1 # from the bootstrap _start; %call doesn't clobber them, so the diff --git a/docs/CC.md b/docs/CC.md @@ -1,4 +1,4 @@ -# Minimal C subset (lispcc) +# Minimal C subset (boot2) Working doc. Baseline is C99; everything here is a delta against it. The target is **just enough C** to compile @@ -450,10 +450,10 @@ Status legend: `[x]` done · `[~]` in progress · `[ ]` not started. ELF. 3. [ ] Compile the mes libc unified-libc.c (the same file MesCC builds into libc.a). -4. [ ] Compile tcc.c (under the tcc-mes defines) → tcc-lispcc; verify - `tcc-lispcc -version` runs. -5. [ ] Use tcc-lispcc to build tcc-boot0; verify checksum matches the +4. [ ] Compile tcc.c (under the tcc-mes defines) → tcc-boot2; verify + `tcc-boot2 -version` runs. +5. [ ] Use tcc-boot2 to build tcc-boot0; verify checksum matches the live-bootstrap reference. -Hitting (5) is the bootstrap milestone — at that point lispcc has +Hitting (5) is the bootstrap milestone — at that point boot2 has fully replaced MesCC in the chain. diff --git a/docs/LIBC.md b/docs/LIBC.md @@ -1,4 +1,4 @@ -# lispcc libc +# boot2 libc Goal: a `tcc-boot2` that runs and produces working binaries. Three phases: @@ -18,7 +18,7 @@ link external archives — `-version`, parse-only smokes. Strategy in one sentence: **vendor a curated subset of mes libc as source, patch a handful of small things, replace mes's per-arch inline-asm syscall wrappers with one hand-written file -(`lispcc-syscall.c`) that calls our P1pp labelled `sys_*` entry +(`boot2-syscall.c`) that calls our P1pp labelled `sys_*` entry points, then build it three different ways: as P1pp linked into tcc-boot2 (Phase A), as ELF object files via tcc-boot2 itself (Phase B1), and tcc's own `lib/libtcc1.c` via tcc-boot2 (Phase B2).** @@ -38,11 +38,11 @@ vendor/mes-libc/ │ └── *.c ../mes/lib/) ├── linux/ │ └── malloc.c (only file kept; the others are replaced -│ by lispcc-syscall.c) +│ by boot2-syscall.c) ├── include/ (verbatim copy of ../mes/include/, plus an │ empty mes/config.h shim) ├── patches/ (literal-block .before/.after pairs) -├── lispcc-syscall.c (our hand-written replacement for mes's +├── boot2-syscall.c (our hand-written replacement for mes's │ per-arch inline-asm syscall.c + glue) ├── unified-libc.c (#include's every .c above; host -E flattens) └── LICENSE (mes's COPYING; libc subset is GPLv3+) @@ -118,7 +118,7 @@ mes/ abtol.c __assert_fail.c __buffered_read.c cast.c dtoab.c ``` Linux syscall-touching files (`brk`, `close`, `lseek`, `_open3`, -`_read`, `unlink`) are replaced by `lispcc-syscall.c` directly so we +`_read`, `unlink`) are replaced by `boot2-syscall.c` directly so we don't drag in mes's `_sys_callN` indirection. `linux/malloc.c` stays — it's a free-list allocator on top of `brk()`, no syscall plumbing of its own. @@ -149,7 +149,7 @@ int globals (no TLS). && __x86_64__ && !SYSTEM_LIBC` blocks evaluate to zero under our defines (no `__GNUC__`); confirm by grep on `libc.flat.c` if in doubt. -### lispcc-syscall.c +### boot2-syscall.c The only file we author. Provides: @@ -186,7 +186,7 @@ The only file we author. Provides: `MES_ARCH` mapping is `aarch64→riscv64`, `amd64→x86_64`, `riscv64→riscv64`. mes ships no aarch64 headers; the riscv64 set suffices because nothing in our flatten ends up referencing arch- -specific syscall numbers or struct stat layouts (lispcc-syscall.c +specific syscall numbers or struct stat layouts (boot2-syscall.c goes around them). `-I` order is **`include` before `include/linux/$MES_ARCH`** — @@ -225,7 +225,7 @@ arguments. Both the tcc-boot2 link rule (Makefile) and the cc-libc test suite (boot-run-tests.sh) compose this way; the tcc-boot2 client uses prefix `tcc__`, every cc-libc fixture uses `app__`. -`__libc_init` (`vendor/mes-libc/lispcc-syscall.c`) walks argv's +`__libc_init` (`vendor/mes-libc/boot2-syscall.c`) walks argv's NULL terminator to populate `environ`; it must run before any libc function that reads the environment. That's why the entry fragment calls it ahead of `main`. @@ -451,5 +451,5 @@ flag, see §Linking). Remaining issues: 100 instead of 42 from `va_arg`. Symptom is cc-libc/05-printf-int; not yet diagnosed. - **Floating-point literals.** cc.scm rejects `0.0`. Use `(double) 0` - or similar non-literal forms (one site in lispcc-syscall.c's + or similar non-literal forms (one site in boot2-syscall.c's `strtod` stub). diff --git a/docs/OS.md b/docs/OS.md @@ -1,6 +1,6 @@ # Minimal OS contract -The lispcc bootstrap depends on a small, well-bounded set of OS +The boot2 bootstrap depends on a small, well-bounded set of OS capabilities. This document specifies that contract so a minimal OS kernel can be implemented (and verified) against it. The rest of the chain — `M0`, `hex2`, `cc.scm`, `tcc-boot2`, libc — assumes nothing @@ -79,7 +79,7 @@ sp + 8 argv[0] (pointer) [argv/envp string bytes follow, anywhere in image] ``` -`__libc_init` (`vendor/mes-libc/lispcc-syscall.c`) walks past argv's +`__libc_init` (`vendor/mes-libc/boot2-syscall.c`) walks past argv's NULL to find `environ`. **auxv is not required** — nothing in the chain reads it. @@ -113,7 +113,7 @@ chain reads it. No signal-handler installation is required. Default actions (SIGSEGV → terminate, SIGPIPE → terminate, etc.) are sufficient. The -chain installs zero handlers; `lispcc-syscall.c` stubs `raise` to +chain installs zero handlers; `boot2-syscall.c` stubs `raise` to ENOSYS. ## Filesystem @@ -158,7 +158,7 @@ Eight calls. Wired in `P1/P1pp.P1pp:986-1055`. Errors are returned as negative errno (`-EBADF`, `-ENOENT`, …) in the result register, per the standard Linux convention. The libc errno -layer (`vendor/mes-libc/lispcc-syscall.c`) negates and stores into a +layer (`vendor/mes-libc/boot2-syscall.c`) negates and stores into a single global `errno` int. Everything in `docs/LIBC.txt`'s "syscall-using" column reduces to @@ -242,5 +242,5 @@ A minimal-OS implementation is compliant when: layer) can invoke `tcc-boot2` on a `.c` source, wait for it to exit, and read the resulting ELF back from disk. -Both acceptance suites run end-to-end in the lispcc tree; an OS -reaching Tier 2 needs no lispcc-side changes. +Both acceptance suites run end-to-end in the boot2 tree; an OS +reaching Tier 2 needs no boot2-side changes. diff --git a/docs/TCC-TODO.md b/docs/TCC-TODO.md @@ -170,7 +170,7 @@ Done. `parse_number` declares `double d; d = 0;` which triggered the `cg-cast` FP rejection. `%cg-fp-reject!` is now a named no-op so fp ctypes flow through size-dispatched load/store and same-size casts as raw bit patterns. Real FP arithmetic is still wrong (binops emit -integer ALU ops on the underlying bits), but tcc-lispcc's runtime +integer ALU ops on the underlying bits), but tcc-boot2's runtime never executes its own float code paths when compiling float-free programs, so producing valid-but-semantically-wrong P1pp here is sufficient. Comment in cc.scm flags the call sites for any future @@ -309,8 +309,8 @@ likely walls live in the assembly side and at runtime: unmet — there is no libc in the link today. See §libc strategy below. The end goal is milestone 4 in [CC.md §Validation milestones](CC.md) -— "Compile tcc.c (under the tcc-mes defines) → tcc-lispcc; verify -`tcc-lispcc -version` runs." +— "Compile tcc.c (under the tcc-mes defines) → tcc-boot2; verify +`tcc-boot2 -version` runs." ## libc — see [LIBC.md](LIBC.md) diff --git a/tests/cc-libc/01-write-syscall.c b/tests/cc-libc/01-write-syscall.c @@ -1,5 +1,5 @@ /* Confirms our P1pp sys_write -> _write path works end-to-end through - * lispcc-syscall.c. No buffering, no FILE struct, no varargs. */ + * boot2-syscall.c. No buffering, no FILE struct, no varargs. */ extern long sys_write (long fd, long buf, long len); int diff --git a/tests/cc-libc/02-write-libc.c b/tests/cc-libc/02-write-libc.c @@ -1,4 +1,4 @@ -/* posix/write.c -> lispcc-syscall.c::_write -> P1pp sys_write. +/* posix/write.c -> boot2-syscall.c::_write -> P1pp sys_write. * Adds the public `write(int, const void *, size_t)` layer with errno * handling on top of 01-write-syscall. cc.scm rejects #include * (file inclusion is upstream of cc.scm via host -E pre-flatten); use diff --git a/tests/cc-libc/03-fputs-stdout.c b/tests/cc-libc/03-fputs-stdout.c @@ -1,6 +1,6 @@ /* fputs(s, stdout) — canonical libc usage. fputs casts stream to long * (the fd) and forwards to write. stdout is a global (FILE*)1 that - * lispcc-syscall.c provides as a real symbol (mes's #define is undef'd + * boot2-syscall.c provides as a real symbol (mes's #define is undef'd * post-include). */ typedef long FILE; extern FILE *stdout; diff --git a/tests/cc-libc/07-malloc-roundtrip.c b/tests/cc-libc/07-malloc-roundtrip.c @@ -1,4 +1,4 @@ -/* Allocator smoke: malloc -> brk syscall (via lispcc-syscall.c::brk). +/* Allocator smoke: malloc -> brk syscall (via boot2-syscall.c::brk). * Writes a sentinel through the returned pointer and reads it back so * a successful malloc that returned a bad address is still caught. */ typedef unsigned long size_t; diff --git a/tests/cc-libc/09-exit-code.c b/tests/cc-libc/09-exit-code.c @@ -1,4 +1,4 @@ -/* exit(N) -> stdlib/exit.c -> stdlib/__exit.c -> lispcc-syscall.c::_exit +/* exit(N) -> stdlib/exit.c -> stdlib/__exit.c -> boot2-syscall.c::_exit * -> P1pp sys_exit. 00-exit covers the implicit return-from-main path; * this fixture exercises the explicit terminator that abort() / errors * out of main use. The literal `return 99;` after the exit call must diff --git a/tests/cc-libc/10-file-roundtrip.c b/tests/cc-libc/10-file-roundtrip.c @@ -24,7 +24,7 @@ extern ssize_t write (int fd, void const *buf, size_t n); int main (void) { - char const *path = "/tmp/lispcc-cclibc-10"; + char const *path = "/tmp/boot2-cclibc-10"; FILE *w = fopen (path, "w"); if (!w) diff --git a/tests/cc-libc/11-fseek.c b/tests/cc-libc/11-fseek.c @@ -19,7 +19,7 @@ extern ssize_t write (int fd, void const *buf, size_t n); int main (void) { - char const *path = "/tmp/lispcc-cclibc-11"; + char const *path = "/tmp/boot2-cclibc-11"; FILE *w = fopen (path, "w"); if (!w) diff --git a/tests/cc-libc/12-unlink.c b/tests/cc-libc/12-unlink.c @@ -1,4 +1,4 @@ -/* unlink -> lispcc-syscall.c::unlink -> P1pp sys_unlink (= unlinkat +/* unlink -> boot2-syscall.c::unlink -> P1pp sys_unlink (= unlinkat * with AT_FDCWD). Drives the syscall by creating a file, removing it, * then re-opening it for read and asserting fopen now returns NULL. * The first fopen("w") must succeed, the second fopen("r") after @@ -17,7 +17,7 @@ extern ssize_t write (int fd, void const *buf, size_t n); int main (void) { - char const *path = "/tmp/lispcc-cclibc-12"; + char const *path = "/tmp/boot2-cclibc-12"; FILE *w = fopen (path, "w"); if (!w) diff --git a/vendor/mes-libc/boot2-syscall.c b/vendor/mes-libc/boot2-syscall.c @@ -0,0 +1,260 @@ +/* boot2-syscall.c — replaces mes's per-arch inline-asm syscall wrappers + * with C wrappers that call P1pp's labelled syscall entry points. + * + * The P1pp labels (sys_read / sys_write / sys_open / sys_close / + * sys_lseek / sys_brk / sys_unlink / sys_exit) are defined in + * P1/P1pp.P1pp and resolved at the linker stage thanks to cc.scm's + * external-linkage rule (commit 6488cca). + * + * Layering: + * public libc (read, write, open, …) — comes from the posix layer + * └─ low-level _read, _write, _open3 — provided here + * └─ named sys_* labels — provided by P1pp.P1pp + * + * mes's linux/{brk,close,lseek,_open3,_read,unlink}.c are NOT vendored; + * the equivalents live here and bypass mes's _sys_callN indirection. + * mes's linux/<arch>-mes-{mescc,gcc}/{syscall,_exit,_write,…}.c are + * also unused — those are the inline-asm files we replace. + */ + +#include <mes/lib.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h> + +/* mes's <stdio.h> defines stdin/stdout/stderr as macros expanding to + * (FILE*)0/1/2. That works for libc's own .c files, but client code + * (cc-libc tests, eventually tcc-boot2's compiled output) that uses + * `extern FILE *stdout;` needs a real linkage symbol. Drop the macros + * after libc's headers have set their guards, then define globals + * with the same initializer values. mes's libc bodies that already + * referenced `stdout` had the macro substitution happen on their + * #include path; subsequent .c files re-include <stdio.h> but hit + * the include guard and so see the now-undef'd identifiers, which + * resolve to our globals at link time (same numeric value, same fd). */ +#undef stdin +#undef stdout +#undef stderr +FILE *stdin = (FILE *) 0; +FILE *stdout = (FILE *) 1; +FILE *stderr = (FILE *) 2; + +/* `__stdout` and friends (the int-typed fd globals oputs / fdputs read) + * are file-scope initialized in mes/mes_open.c — but that initializer + * is gated behind `#if SYSTEM_LIBC`, which we don't define. Without + * SYSTEM_LIBC we'd be left with only mes/globals.c's tentative + * `int __stdout;`, which zero-inits — meaning oputs("ok") writes to + * fd 0 (read-only stdin) and silently no-ops. unified-libc.c includes + * boot2-syscall.c before globals.c, so cc.scm's redecl-merge sees + * the proper definition first and absorbs the later tentative. */ +int __stdin = 0; +int __stdout = 1; +int __stderr = 2; + +extern long sys_read (long fd, long buf, long n); +extern long sys_write (long fd, long buf, long n); +extern long sys_open (long path, long flags, long mode); +extern long sys_close (long fd); +extern long sys_lseek (long fd, long off, long whence); +extern long sys_brk (long addr); +extern long sys_unlink (long path); +extern long sys_exit (long code); + +/* mes/globals.c already provides `int errno;` and the FILE-helper + * indirections (__buffered_read_clear, __ungetc_clear, __ungetc_init). + * mes/__buffered_read.c and mes/fdgetc.c implement them. */ + +ssize_t +_read (int filedes, void *buffer, size_t size) +{ + return sys_read (filedes, (long) buffer, (long) size); +} + +ssize_t +_write (int filedes, void const *buffer, size_t size) +{ + return sys_write (filedes, (long) buffer, (long) size); +} + +int +_open3 (char const *file_name, int flags, int mask) +{ + int r = sys_open ((long) file_name, flags, mask); + __ungetc_init (); + if (r > 2) + { + if (r >= __FILEDES_MAX) + { + errno = EMFILE; + return -1; + } + __ungetc_clear (r); + __buffered_read_clear (r); + } + return r; +} + +int +close (int filedes) +{ + long r = sys_close (filedes); + if (r < 0) + { + errno = -r; + return -1; + } + errno = 0; + return (int) r; +} + +off_t +_lseek (int filedes, off_t offset, int whence) +{ + return sys_lseek (filedes, offset, whence); +} + +off_t +lseek (int filedes, off_t offset, int whence) +{ + /* mirrors mes's linux/lseek.c: drain the per-fd read buffer before + * letting the kernel see a positional move; SEEK_CUR offsets must + * back out the buffered-but-not-yet-consumed bytes. */ + size_t skip = __buffered_read_clear (filedes); + if (whence == SEEK_CUR) + offset -= skip; + return sys_lseek (filedes, offset, whence); +} + +long +brk (void *addr) +{ + return sys_brk ((long) addr); +} + +int +unlink (char const *file_name) +{ + long r = sys_unlink ((long) file_name); + if (r < 0) + { + errno = -r; + return -1; + } + errno = 0; + return 0; +} + +void +_exit (int status) +{ + sys_exit (status); +} + +/* Linux aarch64/amd64/riscv64 stack at exec entry: + * [sp] = argc + * [sp + 8 * (1)] = argv[0] + * ... + * [sp + 8 * (argc+1)] = NULL (terminates argv) + * [sp + 8 * (argc+2)] = envp[0] + * Our :_start passes (a0=argc, a1=argv) to :p1_main. envp follows + * argv's NULL terminator, so we compute it from argv. tcc's getenv + * walks the resulting environ; without initialization it dereferences + * NULL on the first call and segfaults during tcc_new(). */ +extern char **environ; + +void +__libc_init (int argc, char **argv) +{ + (void) argc; + char **p = argv; + while (*p) + p++; + environ = p + 1; +} + +/* ---- ENOSYS stubs for libc-internal references not exercised by ---- + * tcc-boot2's golden path. The mes libc transitively pulls these in + * (posix/getcwd.c → _getcwd, posix/execvp.c → execve, stdlib/abort.c + * → raise, …). Providing -ENOSYS stubs keeps the link clean; if any + * of these surfaces in a real workload, replace the stub with a P1pp + * label and a thin wrapper above. */ + +#define ENOSYS 38 + +char * +_getcwd (char *buffer, size_t size) +{ + (void) buffer; (void) size; + errno = ENOSYS; + return 0; +} + +int +access (char const *path, int mode) +{ + (void) path; (void) mode; + errno = ENOSYS; + return -1; +} + +void +assert_msg (int check, char *msg) +{ + if (!check) + { + sys_write (2, (long) msg, 0); + sys_exit (1); + } +} + +int +execve (char const *file, char *const argv[], char *const envp[]) +{ + (void) file; (void) argv; (void) envp; + errno = ENOSYS; + return -1; +} + +int +fsync (int filedes) +{ + (void) filedes; + return 0; +} + +int +raise (int sig) +{ + /* abort() calls raise(SIGABRT); take that as a hard exit. */ + sys_exit (128 + sig); + return -1; +} + +int +rmdir (char const *file_name) +{ + (void) file_name; + errno = ENOSYS; + return -1; +} + +int +stat (char const *file_name, struct stat *buf) +{ + (void) file_name; (void) buf; + errno = ENOSYS; + return -1; +} + +double +strtod (char const *string, char **tailptr) +{ + /* tcc.flat.c never reaches strtod under our defines (HAVE_FLOAT off), + * and stdlib/strtof.c only forwards. cc.scm rejects FP literals, so + * we cast a zero int — same numeric value, no `0.0` token. */ + if (tailptr) + *tailptr = (char *) string; + return (double) 0; +} diff --git a/vendor/mes-libc/lispcc-syscall.c b/vendor/mes-libc/lispcc-syscall.c @@ -1,260 +0,0 @@ -/* lispcc-syscall.c — replaces mes's per-arch inline-asm syscall wrappers - * with C wrappers that call P1pp's labelled syscall entry points. - * - * The P1pp labels (sys_read / sys_write / sys_open / sys_close / - * sys_lseek / sys_brk / sys_unlink / sys_exit) are defined in - * P1/P1pp.P1pp and resolved at the linker stage thanks to cc.scm's - * external-linkage rule (commit 6488cca). - * - * Layering: - * public libc (read, write, open, …) — comes from the posix layer - * └─ low-level _read, _write, _open3 — provided here - * └─ named sys_* labels — provided by P1pp.P1pp - * - * mes's linux/{brk,close,lseek,_open3,_read,unlink}.c are NOT vendored; - * the equivalents live here and bypass mes's _sys_callN indirection. - * mes's linux/<arch>-mes-{mescc,gcc}/{syscall,_exit,_write,…}.c are - * also unused — those are the inline-asm files we replace. - */ - -#include <mes/lib.h> -#include <fcntl.h> -#include <errno.h> -#include <stdio.h> -#include <unistd.h> -#include <sys/stat.h> - -/* mes's <stdio.h> defines stdin/stdout/stderr as macros expanding to - * (FILE*)0/1/2. That works for libc's own .c files, but client code - * (cc-libc tests, eventually tcc-boot2's compiled output) that uses - * `extern FILE *stdout;` needs a real linkage symbol. Drop the macros - * after libc's headers have set their guards, then define globals - * with the same initializer values. mes's libc bodies that already - * referenced `stdout` had the macro substitution happen on their - * #include path; subsequent .c files re-include <stdio.h> but hit - * the include guard and so see the now-undef'd identifiers, which - * resolve to our globals at link time (same numeric value, same fd). */ -#undef stdin -#undef stdout -#undef stderr -FILE *stdin = (FILE *) 0; -FILE *stdout = (FILE *) 1; -FILE *stderr = (FILE *) 2; - -/* `__stdout` and friends (the int-typed fd globals oputs / fdputs read) - * are file-scope initialized in mes/mes_open.c — but that initializer - * is gated behind `#if SYSTEM_LIBC`, which we don't define. Without - * SYSTEM_LIBC we'd be left with only mes/globals.c's tentative - * `int __stdout;`, which zero-inits — meaning oputs("ok") writes to - * fd 0 (read-only stdin) and silently no-ops. unified-libc.c includes - * lispcc-syscall.c before globals.c, so cc.scm's redecl-merge sees - * the proper definition first and absorbs the later tentative. */ -int __stdin = 0; -int __stdout = 1; -int __stderr = 2; - -extern long sys_read (long fd, long buf, long n); -extern long sys_write (long fd, long buf, long n); -extern long sys_open (long path, long flags, long mode); -extern long sys_close (long fd); -extern long sys_lseek (long fd, long off, long whence); -extern long sys_brk (long addr); -extern long sys_unlink (long path); -extern long sys_exit (long code); - -/* mes/globals.c already provides `int errno;` and the FILE-helper - * indirections (__buffered_read_clear, __ungetc_clear, __ungetc_init). - * mes/__buffered_read.c and mes/fdgetc.c implement them. */ - -ssize_t -_read (int filedes, void *buffer, size_t size) -{ - return sys_read (filedes, (long) buffer, (long) size); -} - -ssize_t -_write (int filedes, void const *buffer, size_t size) -{ - return sys_write (filedes, (long) buffer, (long) size); -} - -int -_open3 (char const *file_name, int flags, int mask) -{ - int r = sys_open ((long) file_name, flags, mask); - __ungetc_init (); - if (r > 2) - { - if (r >= __FILEDES_MAX) - { - errno = EMFILE; - return -1; - } - __ungetc_clear (r); - __buffered_read_clear (r); - } - return r; -} - -int -close (int filedes) -{ - long r = sys_close (filedes); - if (r < 0) - { - errno = -r; - return -1; - } - errno = 0; - return (int) r; -} - -off_t -_lseek (int filedes, off_t offset, int whence) -{ - return sys_lseek (filedes, offset, whence); -} - -off_t -lseek (int filedes, off_t offset, int whence) -{ - /* mirrors mes's linux/lseek.c: drain the per-fd read buffer before - * letting the kernel see a positional move; SEEK_CUR offsets must - * back out the buffered-but-not-yet-consumed bytes. */ - size_t skip = __buffered_read_clear (filedes); - if (whence == SEEK_CUR) - offset -= skip; - return sys_lseek (filedes, offset, whence); -} - -long -brk (void *addr) -{ - return sys_brk ((long) addr); -} - -int -unlink (char const *file_name) -{ - long r = sys_unlink ((long) file_name); - if (r < 0) - { - errno = -r; - return -1; - } - errno = 0; - return 0; -} - -void -_exit (int status) -{ - sys_exit (status); -} - -/* Linux aarch64/amd64/riscv64 stack at exec entry: - * [sp] = argc - * [sp + 8 * (1)] = argv[0] - * ... - * [sp + 8 * (argc+1)] = NULL (terminates argv) - * [sp + 8 * (argc+2)] = envp[0] - * Our :_start passes (a0=argc, a1=argv) to :p1_main. envp follows - * argv's NULL terminator, so we compute it from argv. tcc's getenv - * walks the resulting environ; without initialization it dereferences - * NULL on the first call and segfaults during tcc_new(). */ -extern char **environ; - -void -__libc_init (int argc, char **argv) -{ - (void) argc; - char **p = argv; - while (*p) - p++; - environ = p + 1; -} - -/* ---- ENOSYS stubs for libc-internal references not exercised by ---- - * tcc-boot2's golden path. The mes libc transitively pulls these in - * (posix/getcwd.c → _getcwd, posix/execvp.c → execve, stdlib/abort.c - * → raise, …). Providing -ENOSYS stubs keeps the link clean; if any - * of these surfaces in a real workload, replace the stub with a P1pp - * label and a thin wrapper above. */ - -#define ENOSYS 38 - -char * -_getcwd (char *buffer, size_t size) -{ - (void) buffer; (void) size; - errno = ENOSYS; - return 0; -} - -int -access (char const *path, int mode) -{ - (void) path; (void) mode; - errno = ENOSYS; - return -1; -} - -void -assert_msg (int check, char *msg) -{ - if (!check) - { - sys_write (2, (long) msg, 0); - sys_exit (1); - } -} - -int -execve (char const *file, char *const argv[], char *const envp[]) -{ - (void) file; (void) argv; (void) envp; - errno = ENOSYS; - return -1; -} - -int -fsync (int filedes) -{ - (void) filedes; - return 0; -} - -int -raise (int sig) -{ - /* abort() calls raise(SIGABRT); take that as a hard exit. */ - sys_exit (128 + sig); - return -1; -} - -int -rmdir (char const *file_name) -{ - (void) file_name; - errno = ENOSYS; - return -1; -} - -int -stat (char const *file_name, struct stat *buf) -{ - (void) file_name; (void) buf; - errno = ENOSYS; - return -1; -} - -double -strtod (char const *string, char **tailptr) -{ - /* tcc.flat.c never reaches strtod under our defines (HAVE_FLOAT off), - * and stdlib/strtof.c only forwards. cc.scm rejects FP literals, so - * we cast a zero int — same numeric value, no `0.0` token. */ - if (tailptr) - *tailptr = (char *) string; - return (double) 0; -} diff --git a/vendor/mes-libc/patches/ntoab-inline-defined.after b/vendor/mes-libc/patches/ntoab-inline-defined.after @@ -1,4 +1,4 @@ -/* lispcc: drop the helper macro — wrapping `defined()` in a #define body +/* boot2: drop the helper macro — wrapping `defined()` in a #define body * is UB per ISO C and triggers -Wexpansion-to-defined. The mes author's * FIXME comment already had the inlined form; we just promote it. */ #if __M2__ || (!defined (__MESC__and__arm__) && !defined (__TINYC__and__arm__and__BOOTSTRAP)) diff --git a/vendor/mes-libc/unified-libc.c b/vendor/mes-libc/unified-libc.c @@ -1,9 +1,9 @@ /* unified-libc.c — single translation unit gathering every vendored - * mes-libc .c plus lispcc-syscall.c. The host preprocessor flattens + * mes-libc .c plus boot2-syscall.c. The host preprocessor flattens * the whole thing to libc.flat.c, which cc.scm compiles to libc.P1pp. * * Order matters in a few specific places: - * 1. lispcc-syscall.c declares the extern sys_* P1pp labels and + * 1. boot2-syscall.c declares the extern sys_* P1pp labels and * defines _read / _write / _open3 / close / lseek / brk / unlink / * _exit. It comes early so later layers see the prototypes. * 2. mes/globals.c defines errno / __stdin etc. as tentative @@ -20,7 +20,7 @@ #include <mes/lib.h> /* ---- low-level: P1pp syscall wrappers + thin posix glue --------- */ -#include "lispcc-syscall.c" +#include "boot2-syscall.c" /* ---- ctype ------------------------------------------------------ */ #include "ctype/isalnum.c" @@ -107,7 +107,7 @@ #include "stdlib/strtoul.c" #include "stdlib/strtoull.c" -/* ---- linux (allocator only; the rest replaced by lispcc-syscall.c) */ +/* ---- linux (allocator only; the rest replaced by boot2-syscall.c) */ #include "linux/malloc.c" /* ---- posix ------------------------------------------------------ */