Files
ww/test/wcc/996_dyn_ww.c

195 lines
5.0 KiB
C

/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
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 <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;
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;
}