boot2

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

commit 27daf68ca620a6ad909d6abe17c49cf8e2468e0a
parent 5b5ed0309cc714b0e05fd444067adafc6265d892
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 29 Apr 2026 16:31:32 -0700

tests/cc: tentative bss sizing

Diffstat:
Atests/cc/132-tentative-bss-sizing.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/cc/132-tentative-bss-sizing.expected-exit | 1+
2 files changed, 77 insertions(+), 0 deletions(-)

diff --git a/tests/cc/132-tentative-bss-sizing.c b/tests/cc/132-tentative-bss-sizing.c @@ -0,0 +1,76 @@ +/* C 6.9.2 — tentative defs reserve sizeof(T) bytes of zero-init + * storage. Companion to 124-tentative-static.c, which only exercised + * `int`. tcc-boot2 needs the wide-pointer / array cases too: + * mes/globals.c declares `char **environ;`, `int errno;`, etc. + * cc.scm has been emitting too few or too many bytes for these + * (see :environ → 1 byte, :errno → 16 bytes in libc.P1pp). + * + * Each global is sized differently and probed via address-of so the + * compiler can't fold the read. Wrong sizing manifests as either: + * - reads past the slot return 0xff... or stale data → mismatch + * - 8-byte stores to a 1-byte slot scribble adjacent globals + * (so writing g_pp = X then reading g_int near it shows X + * bleeding through). */ + +extern unsigned long strlen (const char *); +extern long sys_write (long fd, long buf, long len); + +char g_char; /* 1 byte */ +short g_short; /* 2 bytes */ +int g_int; /* 4 bytes */ +long g_long; /* 8 bytes */ +char *g_p; /* 8 bytes */ +char **g_pp; /* 8 bytes — the environ shape */ +int g_arr[4]; /* 16 bytes */ +char *g_parr[3]; /* 24 bytes */ + +static long +fail (const char *msg) +{ + sys_write (2, (long) msg, (long) strlen (msg)); + return 1; +} + +int +main (void) +{ + /* Tentative defs must zero-init. */ + if (g_char != 0) return fail ("g_char not zero\n"); + if (g_short != 0) return fail ("g_short not zero\n"); + if (g_int != 0) return fail ("g_int not zero\n"); + if (g_long != 0) return fail ("g_long not zero\n"); + if (g_p != 0) return fail ("g_p not zero\n"); + if (g_pp != 0) return fail ("g_pp not zero\n"); + if (g_arr[0] || g_arr[1] || g_arr[2] || g_arr[3]) + return fail ("g_arr not zero\n"); + if (g_parr[0] || g_parr[1] || g_parr[2]) + return fail ("g_parr not zero\n"); + + /* Each slot must be addressable for its full sizeof(T). + * Write a sentinel through the address, read it back. If the + * slot is undersized, the high bytes spill into the next global + * and the read returns a truncated value. */ + long *pl = &g_long; + *pl = 0x1122334455667788L; + if (g_long != 0x1122334455667788L) + return fail ("g_long roundtrip\n"); + + g_pp = (char **) 0xdeadbeef00112233L; + if ((long) g_pp != (long) 0xdeadbeef00112233L) + return fail ("g_pp roundtrip\n"); + + /* Array element: write the last slot — only valid if the full + * 16-byte / 24-byte allocation actually exists. */ + g_arr[3] = 42; + if (g_arr[3] != 42) return fail ("g_arr[3] roundtrip\n"); + g_parr[2] = (char *) 0x99; + if ((long) g_parr[2] != 0x99) return fail ("g_parr[2] roundtrip\n"); + + /* And no spill: writing g_pp must NOT change g_int next door. + * (Slot order in memory follows declaration order in the TU.) */ + g_int = 0; + g_pp = (char **) 0xfffffffffffffffeL; + if (g_int != 0) return fail ("g_pp store spilled into g_int\n"); + + return 0; +} diff --git a/tests/cc/132-tentative-bss-sizing.expected-exit b/tests/cc/132-tentative-bss-sizing.expected-exit @@ -0,0 +1 @@ +0