boot2

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

lispcc-syscall.c (6743B)


      1 /* lispcc-syscall.c — replaces mes's per-arch inline-asm syscall wrappers
      2  * with C wrappers that call P1pp's labelled syscall entry points.
      3  *
      4  * The P1pp labels (sys_read / sys_write / sys_open / sys_close /
      5  * sys_lseek / sys_brk / sys_unlink / sys_exit) are defined in
      6  * P1/P1pp.P1pp and resolved at the linker stage thanks to cc.scm's
      7  * external-linkage rule (commit 6488cca).
      8  *
      9  * Layering:
     10  *   public libc (read, write, open, …) — comes from the posix layer
     11  *      └─ low-level _read, _write, _open3 — provided here
     12  *           └─ named sys_* labels — provided by P1pp.P1pp
     13  *
     14  * mes's linux/{brk,close,lseek,_open3,_read,unlink}.c are NOT vendored;
     15  * the equivalents live here and bypass mes's _sys_callN indirection.
     16  * mes's linux/<arch>-mes-{mescc,gcc}/{syscall,_exit,_write,…}.c are
     17  * also unused — those are the inline-asm files we replace.
     18  */
     19 
     20 #include <mes/lib.h>
     21 #include <fcntl.h>
     22 #include <errno.h>
     23 #include <stdio.h>
     24 #include <unistd.h>
     25 #include <sys/stat.h>
     26 
     27 /* mes's <stdio.h> defines stdin/stdout/stderr as macros expanding to
     28  * (FILE*)0/1/2. That works for libc's own .c files, but client code
     29  * (cc-libc tests, eventually tcc-boot2's compiled output) that uses
     30  * `extern FILE *stdout;` needs a real linkage symbol. Drop the macros
     31  * after libc's headers have set their guards, then define globals
     32  * with the same initializer values. mes's libc bodies that already
     33  * referenced `stdout` had the macro substitution happen on their
     34  * #include path; subsequent .c files re-include <stdio.h> but hit
     35  * the include guard and so see the now-undef'd identifiers, which
     36  * resolve to our globals at link time (same numeric value, same fd). */
     37 #undef stdin
     38 #undef stdout
     39 #undef stderr
     40 FILE *stdin  = (FILE *) 0;
     41 FILE *stdout = (FILE *) 1;
     42 FILE *stderr = (FILE *) 2;
     43 
     44 /* `__stdout` and friends (the int-typed fd globals oputs / fdputs read)
     45  * are file-scope initialized in mes/mes_open.c — but that initializer
     46  * is gated behind `#if SYSTEM_LIBC`, which we don't define. Without
     47  * SYSTEM_LIBC we'd be left with only mes/globals.c's tentative
     48  * `int __stdout;`, which zero-inits — meaning oputs("ok") writes to
     49  * fd 0 (read-only stdin) and silently no-ops. unified-libc.c includes
     50  * lispcc-syscall.c before globals.c, so cc.scm's redecl-merge sees
     51  * the proper definition first and absorbs the later tentative. */
     52 int __stdin  = 0;
     53 int __stdout = 1;
     54 int __stderr = 2;
     55 
     56 extern long sys_read   (long fd, long buf, long n);
     57 extern long sys_write  (long fd, long buf, long n);
     58 extern long sys_open   (long path, long flags, long mode);
     59 extern long sys_close  (long fd);
     60 extern long sys_lseek  (long fd, long off, long whence);
     61 extern long sys_brk    (long addr);
     62 extern long sys_unlink (long path);
     63 extern long sys_exit   (long code);
     64 
     65 /* mes/globals.c already provides `int errno;` and the FILE-helper
     66  * indirections (__buffered_read_clear, __ungetc_clear, __ungetc_init).
     67  * mes/__buffered_read.c and mes/fdgetc.c implement them. */
     68 
     69 ssize_t
     70 _read (int filedes, void *buffer, size_t size)
     71 {
     72   return sys_read (filedes, (long) buffer, (long) size);
     73 }
     74 
     75 ssize_t
     76 _write (int filedes, void const *buffer, size_t size)
     77 {
     78   return sys_write (filedes, (long) buffer, (long) size);
     79 }
     80 
     81 int
     82 _open3 (char const *file_name, int flags, int mask)
     83 {
     84   int r = sys_open ((long) file_name, flags, mask);
     85   __ungetc_init ();
     86   if (r > 2)
     87     {
     88       if (r >= __FILEDES_MAX)
     89         {
     90           errno = EMFILE;
     91           return -1;
     92         }
     93       __ungetc_clear (r);
     94       __buffered_read_clear (r);
     95     }
     96   return r;
     97 }
     98 
     99 int
    100 close (int filedes)
    101 {
    102   long r = sys_close (filedes);
    103   if (r < 0)
    104     {
    105       errno = -r;
    106       return -1;
    107     }
    108   errno = 0;
    109   return (int) r;
    110 }
    111 
    112 off_t
    113 _lseek (int filedes, off_t offset, int whence)
    114 {
    115   return sys_lseek (filedes, offset, whence);
    116 }
    117 
    118 off_t
    119 lseek (int filedes, off_t offset, int whence)
    120 {
    121   /* mirrors mes's linux/lseek.c: drain the per-fd read buffer before
    122    * letting the kernel see a positional move; SEEK_CUR offsets must
    123    * back out the buffered-but-not-yet-consumed bytes. */
    124   size_t skip = __buffered_read_clear (filedes);
    125   if (whence == SEEK_CUR)
    126     offset -= skip;
    127   return sys_lseek (filedes, offset, whence);
    128 }
    129 
    130 long
    131 brk (void *addr)
    132 {
    133   return sys_brk ((long) addr);
    134 }
    135 
    136 int
    137 unlink (char const *file_name)
    138 {
    139   long r = sys_unlink ((long) file_name);
    140   if (r < 0)
    141     {
    142       errno = -r;
    143       return -1;
    144     }
    145   errno = 0;
    146   return 0;
    147 }
    148 
    149 void
    150 _exit (int status)
    151 {
    152   sys_exit (status);
    153 }
    154 
    155 /* Linux aarch64/amd64/riscv64 stack at exec entry:
    156  *   [sp]               = argc
    157  *   [sp + 8 * (1)]     = argv[0]
    158  *   ...
    159  *   [sp + 8 * (argc+1)] = NULL  (terminates argv)
    160  *   [sp + 8 * (argc+2)] = envp[0]
    161  * Our :_start passes (a0=argc, a1=argv) to :p1_main. envp follows
    162  * argv's NULL terminator, so we compute it from argv. tcc's getenv
    163  * walks the resulting environ; without initialization it dereferences
    164  * NULL on the first call and segfaults during tcc_new(). */
    165 extern char **environ;
    166 
    167 void
    168 __libc_init (int argc, char **argv)
    169 {
    170   (void) argc;
    171   char **p = argv;
    172   while (*p)
    173     p++;
    174   environ = p + 1;
    175 }
    176 
    177 /* ---- ENOSYS stubs for libc-internal references not exercised by ----
    178  * tcc-boot2's golden path. The mes libc transitively pulls these in
    179  * (posix/getcwd.c → _getcwd, posix/execvp.c → execve, stdlib/abort.c
    180  * → raise, …). Providing -ENOSYS stubs keeps the link clean; if any
    181  * of these surfaces in a real workload, replace the stub with a P1pp
    182  * label and a thin wrapper above. */
    183 
    184 #define ENOSYS 38
    185 
    186 char *
    187 _getcwd (char *buffer, size_t size)
    188 {
    189   (void) buffer; (void) size;
    190   errno = ENOSYS;
    191   return 0;
    192 }
    193 
    194 int
    195 access (char const *path, int mode)
    196 {
    197   (void) path; (void) mode;
    198   errno = ENOSYS;
    199   return -1;
    200 }
    201 
    202 void
    203 assert_msg (int check, char *msg)
    204 {
    205   if (!check)
    206     {
    207       sys_write (2, (long) msg, 0);
    208       sys_exit (1);
    209     }
    210 }
    211 
    212 int
    213 execve (char const *file, char *const argv[], char *const envp[])
    214 {
    215   (void) file; (void) argv; (void) envp;
    216   errno = ENOSYS;
    217   return -1;
    218 }
    219 
    220 int
    221 fsync (int filedes)
    222 {
    223   (void) filedes;
    224   return 0;
    225 }
    226 
    227 int
    228 raise (int sig)
    229 {
    230   /* abort() calls raise(SIGABRT); take that as a hard exit. */
    231   sys_exit (128 + sig);
    232   return -1;
    233 }
    234 
    235 int
    236 rmdir (char const *file_name)
    237 {
    238   (void) file_name;
    239   errno = ENOSYS;
    240   return -1;
    241 }
    242 
    243 int
    244 stat (char const *file_name, struct stat *buf)
    245 {
    246   (void) file_name; (void) buf;
    247   errno = ENOSYS;
    248   return -1;
    249 }
    250 
    251 double
    252 strtod (char const *string, char **tailptr)
    253 {
    254   /* tcc.flat.c never reaches strtod under our defines (HAVE_FLOAT off),
    255    * and stdlib/strtof.c only forwards. cc.scm rejects FP literals, so
    256    * we cast a zero int — same numeric value, no `0.0` token. */
    257   if (tailptr)
    258     *tailptr = (char *) string;
    259   return (double) 0;
    260 }