Files
ww/test/wcc/632_w6l_manyflags.c
Hojun-Cho 7b9488706b parse: enforce strict-package — reject package-less files (#24a)
Flip the soft-default to a hard "missing package clause" error symmetrically in
both stages (cmd/wcc/parse.c + lib/ww/syntax/parse.ww): the first real decl of a
primary section with empty pathmod/resetmod and no seen clause is now rejected.
Closes the documented soft-default divergence (the 63-wrapper carve-out).

The gate flip can't be split from the migration it breaks, so this is one atomic
commit: ~80 test/wcc wrappers gain `package main;` via a shared wwtestpkg.h
helper, 6 data fixtures plus 17 asm-grep assertions update for the bare->main.<leaf>
root-helper mangle shift, and rt/ declares `package rt;` with @symbol pinning the
bare rt_ensure/rt_malloc linker names.

Root mangling narrows: the executable entry `main` stays bare (existing
carve-out), but root helper symbols become main.X. The #84 cluster is rewritten
to assert main.run distinct from aa.run/test.run; its cgen fix and bare machinery
are retained — still load-bearing for package-less module-reset deps. New
table-driven test 782_strict_package.c (6 rows, both stages).

Retiring //ww:module-reset is deferred to #24b: it is load-bearing (clears the
.wwi pathmod so the body's package clause asserts), not a vestige; fusing its
removal here would be a silent mismatch.

All byte-id gates green; full make test reports "all 335 tests passed".
2026-06-29 03:55:26 +09:00

153 lines
5.0 KiB
C

/*
* 632_w6l_manyflags — w6l -L/-l arrays must be sized by argc, not a
* fixed 64-slot cap (drain F-C). The 65th -L used to write past the
* allocation: `libdirs[nlibdirs] = ...` with no bound check, corrupting
* the heap and dropping the flag.
*
* Behavioural discriminator: make a -L past slot 64 LOAD-BEARING. We
* build libfoo.a in a real directory and reach it ONLY via the Nth -L,
* preceded by N-1 junk dirs. w6l errors `cannot find -lfoo` unless it
* can locate the archive, so a successful link (exit 0 + ET_EXEC) proves
* the Nth -L was honoured. N is a table {1, 64, 65, 100, 128}; pre-fix,
* N=65 (the slot exactly one past the 64-element array) reliably fails
* to round-trip and the link errors — verified by rebuilding the pre-fix
* w6l_ww. Both stages are driven for parity.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
static const char *
absbin(void)
{
const char *b = getenv("BIN");
if (!b) b = "out/bin";
if (b[0] == '/') return b;
static char buf[2048];
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return NULL;
snprintf(buf, sizeof buf, "%s/%s", cwd, b);
return buf;
}
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return 1;
}
/* link `obj` with linker `lnk`, resolving -lfoo through `libdir` placed
* as the Nth (last) -L after nflags-1 junk dirs. Returns the linker's
* exit code; on success also fails (-1) if the output isn't an ET_EXEC
* ELF — proving the archive was actually located and linked. */
static int
linkwith(const char *lnk, const char *obj, const char *libdir, int nflags)
{
char exe[64];
snprintf(exe, sizeof exe, "/tmp/wwfc_%d_x", getpid());
char junk[8192];
size_t off = 0;
for (int i = 1; i < nflags; i++)
off += snprintf(junk + off, sizeof junk - off,
" -L/nonexist/wwfc_d%d", i);
char cmd[16384];
snprintf(cmd, sizeof cmd,
"%s -o %s %s%s -L%s -lfoo 2>/dev/null",
lnk, exe, obj, junk, libdir);
int rc = runwait(cmd);
if (rc != 0) { unlink(exe); return rc; }
FILE *f = fopen(exe, "rb");
if (!f) return -1;
unsigned char hdr[20];
int ok = fread(hdr, 1, sizeof hdr, f) == sizeof hdr;
fclose(f);
unlink(exe);
if (!ok || memcmp(hdr, "\x7f""ELF", 4) != 0) return -1;
unsigned short etype = (unsigned short)hdr[16]
| ((unsigned short)hdr[17] << 8);
if (etype != 2) return -1; /* ET_EXEC */
return 0;
}
int
main(void)
{
const char *bin = absbin();
if (!bin) return 1;
char src[64], asmf[64], obj[64];
char fsrc[64], fasm[64], fobj[64];
char libdir[64], lib[128], cmd[1024];
int pid = getpid();
snprintf(src, sizeof src, "/tmp/wwfc_%d_m.ww", pid);
snprintf(asmf, sizeof asmf, "/tmp/wwfc_%d_m.s", pid);
snprintf(obj, sizeof obj, "/tmp/wwfc_%d_m.o", pid);
snprintf(fsrc, sizeof fsrc, "/tmp/wwfc_%d_f.ww", pid);
snprintf(fasm, sizeof fasm, "/tmp/wwfc_%d_f.s", pid);
snprintf(fobj, sizeof fobj, "/tmp/wwfc_%d_f.o", pid);
snprintf(libdir, sizeof libdir, "/tmp/wwfc_%d_lib", pid);
snprintf(lib, sizeof lib, "%s/libfoo.a", libdir);
/* a standalone main.o (no undefs) + an unrelated archived foo.o. */
FILE *f = fopen(src, "wb");
wwtest_fputs("fn main() i32 = { return 42; };", f);
fclose(f);
f = fopen(fsrc, "wb");
wwtest_fputs("export fn foo() i32 = { return 7; };", f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, asmf, src);
if (runwait(cmd) != 0) { fprintf(stderr, "w6c main failed\n"); return 1; }
snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, obj, asmf);
if (runwait(cmd) != 0) { fprintf(stderr, "w6a main failed\n"); return 1; }
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, fasm, fsrc);
if (runwait(cmd) != 0) { fprintf(stderr, "w6c foo failed\n"); return 1; }
snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, fobj, fasm);
if (runwait(cmd) != 0) { fprintf(stderr, "w6a foo failed\n"); return 1; }
snprintf(cmd, sizeof cmd, "mkdir -p %s && ar rcs %s %s", libdir, lib, fobj);
if (runwait(cmd) != 0) { fprintf(stderr, "ar failed\n"); return 1; }
int counts[] = { 1, 64, 65, 100, 128 };
int fail = 0;
for (int i = 0; i < (int)(sizeof counts / sizeof counts[0]); i++) {
int n = counts[i];
char wlnk[2048], clnk[2048];
snprintf(wlnk, sizeof wlnk, "%s/w6l_ww", bin);
snprintf(clnk, sizeof clnk, "%s/w6l", bin);
int wrc = linkwith(wlnk, obj, libdir, n);
int crc = linkwith(clnk, obj, libdir, n);
if (wrc != 0) {
fprintf(stderr, "FAIL: w6l_ww nflags=%d link rc=%d "
"(65th-style -L not honoured → heap overflow)\n",
n, wrc);
fail++;
}
if (crc != 0) {
fprintf(stderr, "FAIL: w6l (cstage) nflags=%d link rc=%d\n",
n, crc);
fail++;
}
}
snprintf(cmd, sizeof cmd, "rm -rf %s %s %s %s %s %s %s",
src, asmf, obj, fsrc, fasm, fobj, libdir);
(void)runwait(cmd);
if (fail) {
fprintf(stderr, "w6l_manyflags: %d failure(s)\n", fail);
return 1;
}
printf("w6l_manyflags: -L past slot 64 honoured by both stages "
"(1/64/65/100/128 flags)\n");
return 0;
}