test: make dynamic linker parity observer self-contained

This commit is contained in:
2026-08-13 13:36:20 +09:00
parent dab3cb4998
commit af0f56dfe5

View File

@@ -2,7 +2,8 @@
* Phase-8 marker for ET_DYN linking on the ww side: a green run means
* the ww-side linker emits PT_INTERP/PT_DYNAMIC binaries byte-for-byte
* identical to the C linker on the same inputs — i.e. the dynamic
* linking story is fully ported.
* linking story is fully ported. The input is generated privately so the
* platform gate owns its complete source and artifact lifecycle.
*/
#include <stdio.h>
#include <stdlib.h>
@@ -61,31 +62,70 @@ main(void)
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
/* #93 sep layout: build the example via `ww build` into a
* private /tmp stem (NOT next to the tracked example source), then
* link <stem>.sepwork/__root.o — the self-contained single-package
* object the flip emits in place of the old next-to-source .o. */
char cmd[4096], mbstem[256], mbobj[320], co[64], wo[64];
snprintf(mbstem, sizeof mbstem, "/tmp/wwld_%d_mb", getpid());
snprintf(mbobj, sizeof mbobj, "%s.sepwork/__root.o", mbstem);
snprintf(co, sizeof co, "/tmp/wwld_%d_c", getpid());
snprintf(wo, sizeof wo, "/tmp/wwld_%d_w", getpid());
const char *libdir = NULL;
if (access("/usr/lib/libc.so.6", 0) == 0)
libdir = "/usr/lib";
else if (access("/lib/x86_64-linux-gnu/libc.so.6", 0) == 0)
libdir = "/lib/x86_64-linux-gnu";
else if (access("/lib64/libc.so.6", 0) == 0)
libdir = "/lib64";
if (libdir == NULL) {
puts("w6l_ww-dyn: no libc.so.6 on this system — skipping");
return 77;
}
char td[] = "/tmp/wwld_XXXXXX";
if (mkdtemp(td) == NULL) {
perror("w6l_ww-dyn mkdtemp");
return 1;
}
/* #93 sep layout: build an explicit command source via `ww build`,
* then independently link <stem>.sepwork/__root.o with both linkers.
* The direct getpid call guarantees one dynamic libc relocation. */
char cmd[4096], src[256], stem[256], scratch[320];
char obj[384], co[256], wo[256];
snprintf(src, sizeof src, "%s/dyn.ww", td);
snprintf(stem, sizeof stem, "%s/built", td);
snprintf(scratch, sizeof scratch, "%s.sepwork", stem);
snprintf(obj, sizeof obj, "%s/__root.o", scratch);
snprintf(co, sizeof co, "%s/c", td);
snprintf(wo, sizeof wo, "%s/w", td);
int rc = 0;
snprintf(cmd, sizeof cmd,
"cd %s/examples/mandelbrot && %s/ww build "
"-o %s mandelbrot.ww -L /usr/lib -l c >/dev/null 2>&1",
cwd, bin, mbstem);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: cannot build mandelbrot.o via C driver\n");
FILE *f = fopen(src, "wb");
if (f == NULL) {
fprintf(stderr, "w6l_ww-dyn FAIL: cannot create generated source\n");
rc = 1;
goto cleanup;
}
fputs("package main;\n"
"@symbol(\"getpid\") fn cgetpid() i32;\n"
"export fn main() i32 = {\n"
"\tif (cgetpid() < 0) { return 1; };\n"
"\treturn 0;\n"
"};\n", f);
int writebad = ferror(f);
if (fclose(f) != 0) writebad = 1;
if (writebad) {
fprintf(stderr, "w6l_ww-dyn FAIL: cannot write generated source\n");
rc = 1;
goto cleanup;
}
snprintf(cmd, sizeof cmd,
"%s/w6l -o %s %s -L /usr/lib "
"%s/ww build -o %s %s -L %s -l c >/dev/null 2>&1",
bin, stem, src, libdir);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: cannot build generated command\n");
rc = 1;
goto cleanup;
}
snprintf(cmd, sizeof cmd,
"%s/w6l -o %s %s -L %s "
"-l c %s/out/lib/libwwrt.a 2>/dev/null",
bin, co, mbobj, cwd);
bin, co, obj, libdir, cwd);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: C w6l errored\n");
rc = 1;
@@ -93,9 +133,9 @@ main(void)
}
snprintf(cmd, sizeof cmd,
"%s/w6l_ww -o %s %s -L /usr/lib "
"%s/w6l_ww -o %s %s -L %s "
"-l c %s/out/lib/libwwrt.a 2>/dev/null",
bin, wo, mbobj, cwd);
bin, wo, obj, libdir, cwd);
if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: ww w6l errored\n");
rc = 1;
@@ -114,17 +154,28 @@ main(void)
fprintf(stderr, "w6l_ww-dyn FAIL: C %zu vs ww %zu bytes\n",
nc, nw);
rc = 1;
} else if (runwait(co) != 0 || runwait(wo) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: linked command did not run\n");
rc = 1;
} else {
printf("w6l_ww-dyn: byte-identical to C w6l on mandelbrot "
printf("w6l_ww-dyn: byte-identical to C w6l on generated command "
"(PT_INTERP + PT_DYNAMIC + .rela.plt + .gnu.version_r, "
"%zu bytes)\n", nc);
}
free(bc); free(bw);
cleanup:
/* #93: `ww build` retains this exact caller-owned scratch tree. */
snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork", mbstem);
/* `ww build` retains this exact mkdtemp-owned scratch child. */
snprintf(cmd, sizeof cmd, "rm -rf -- %s", scratch);
int cleanfail = runwait(cmd) != 0;
if (unlink(src) != 0 && errno != ENOENT) {
perror(src);
cleanfail = 1;
}
if (unlink(stem) != 0 && errno != ENOENT) {
perror(stem);
cleanfail = 1;
}
if (unlink(co) != 0 && errno != ENOENT) {
perror(co);
cleanfail = 1;
@@ -133,8 +184,8 @@ cleanup:
perror(wo);
cleanfail = 1;
}
if (unlink(mbstem) != 0 && errno != ENOENT) {
perror(mbstem);
if (rmdir(td) != 0) {
perror(td);
cleanfail = 1;
}
if (cleanfail && rc == 0)