/* * 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. The input is generated privately so the * platform gate owns its complete source and artifact lifecycle. */ #include #include #include #include #include #include static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; if (WIFEXITED(rc)) return WEXITSTATUS(rc); return 1; } 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 slurp(const char *path, char **outbuf, size_t *outlen) { FILE *f = fopen(path, "rb"); if (!f) return -1; fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET); if (n < 0) { fclose(f); return -1; } char *b = malloc((size_t)n + 1); if (!b) { fclose(f); return -1; } if (fread(b, 1, (size_t)n, f) != (size_t)n) { free(b); fclose(f); return -1; } b[n] = '\0'; fclose(f); *outbuf = b; *outlen = (size_t)n; return 0; } int main(void) { const char *bin = absbin(); if (!bin) return 1; char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return 1; 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 .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; 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/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, obj, libdir, cwd); if (runwait(cmd) != 0) { fprintf(stderr, "w6l_ww-dyn FAIL: C w6l errored\n"); rc = 1; goto cleanup; } snprintf(cmd, sizeof cmd, "%s/w6l_ww -o %s %s -L %s " "-l c %s/out/lib/libwwrt.a 2>/dev/null", bin, wo, obj, libdir, cwd); if (runwait(cmd) != 0) { fprintf(stderr, "w6l_ww-dyn FAIL: ww w6l errored\n"); rc = 1; goto cleanup; } char *bc = NULL, *bw = NULL; size_t nc = 0, nw = 0; if (slurp(co, &bc, &nc) < 0 || slurp(wo, &bw, &nw) < 0) { fprintf(stderr, "w6l_ww-dyn FAIL: cannot read outputs\n"); rc = 1; } else if (nc == 0) { fprintf(stderr, "w6l_ww-dyn FAIL: empty outputs\n"); rc = 1; } else if (nc != nw || memcmp(bc, bw, nc) != 0) { 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 generated command " "(PT_INTERP + PT_DYNAMIC + .rela.plt + .gnu.version_r, " "%zu bytes)\n", nc); } free(bc); free(bw); cleanup: /* `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; } if (unlink(wo) != 0 && errno != ENOENT) { perror(wo); cleanfail = 1; } if (rmdir(td) != 0) { perror(td); cleanfail = 1; } if (cleanfail && rc == 0) rc = 1; return rc; }