195 lines
5.0 KiB
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;
|
|
}
|