188 lines
6.3 KiB
C
188 lines
6.3 KiB
C
/*
|
|
* 989_dynentry_run (#63, F15 c6) — w6l's dynamic e_entry must track the actual
|
|
* .text file offset once the dynamic headers overflow the first page.
|
|
*
|
|
* THE BUG (BOTH stages, cat-A #263, real ELF ABI): main computed the entry VA
|
|
* as base + 0x1000 + entry->val assuming .text sits at file offset 0x1000, but
|
|
* the dynamic emitter recomputes text_off = alignup(headers_end, page) and
|
|
* wrote the stale entry into e_entry unchanged. With ~100+ dynamic symbols the
|
|
* dynsym/dynstr/hash/versym/verneed/rela.plt headers push past 0x1000, so
|
|
* .text lands at 0x2000+ while e_entry still pointed at base+0x1000+val — a
|
|
* mapped R+X page of header bytes. The linker exits 0; the binary SIGSEGVs.
|
|
* Byte-id gates can't see it (both stages identically wrong); test 992 covers
|
|
* only static links and 810_dyn uses a handful of syms. THE FIX (both stages):
|
|
* rebase e_entry = entry - 0x1000 + text_off (cmd/w6l/dynout.c +
|
|
* selfhost/cmd/w6l/dynout.ww).
|
|
*
|
|
* row | dyn syms | result (cs==ww)
|
|
* ------+----------+-------------------------------------------
|
|
* big | ~160 | exit 42 (was SIGSEGV 139 both stages), byte-id
|
|
* small | 1 | exit 42 (control, headers fit page), byte-id
|
|
*
|
|
* big was RED pre-c6 on BOTH stages (e_entry into the headers → SIGSEGV).
|
|
* small pins the headers-fit-page path stays correct and byte-identical.
|
|
*
|
|
* Generates the dyn-sym set from `nm -D <libc>` (default-versioned text
|
|
* exports) so it adapts to whatever glibc is present; reports the harness
|
|
* skip status when libc / nm are unavailable or too few symbols are available
|
|
* to overflow the first page.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.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;
|
|
}
|
|
|
|
/* Harvest up to `max` default-versioned libc text exports into syms[][].
|
|
* Returns the count, or -1 if nm/libc unavailable. */
|
|
static int
|
|
harvest_syms(const char *libc, char syms[][32], int max)
|
|
{
|
|
char cmd[1024];
|
|
snprintf(cmd, sizeof cmd,
|
|
"nm -D %s 2>/dev/null | grep ' T ' | grep '@@GLIBC_2' "
|
|
"| grep -v PRIVATE | sed 's/.* //; s/@@.*//' "
|
|
"| grep -E '^[a-z_][a-zA-Z0-9_]{2,18}$' | sort -u", libc);
|
|
FILE *p = popen(cmd, "r");
|
|
if (!p) return -1;
|
|
int n = 0;
|
|
char line[128];
|
|
while (n < max && fgets(line, sizeof line, p)) {
|
|
line[strcspn(line, "\n")] = '\0';
|
|
if (line[0] == '\0') continue;
|
|
snprintf(syms[n], 32, "%s", line);
|
|
n++;
|
|
}
|
|
pclose(p);
|
|
return n;
|
|
}
|
|
|
|
/* Build with `driver` into <tmpdir>/main, run, return exit code (or -1 on
|
|
* build failure). Sets *outbin to the produced binary path. */
|
|
static int
|
|
build_run(const char *driver, const char *src, const char *libdir,
|
|
const char *tmpdir, char *outbin, size_t obsz)
|
|
{
|
|
char cmd[1024];
|
|
snprintf(cmd, sizeof cmd, "cd %s && %s build -o %s/main %s -L %s -l c "
|
|
"2>/dev/null", tmpdir, driver, tmpdir, src, libdir);
|
|
if (runwait(cmd) != 0) return -1;
|
|
snprintf(outbin, obsz, "%s/main", tmpdir);
|
|
return runwait(outbin);
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
const char *libc = NULL, *libdir = NULL;
|
|
if (access("/usr/lib/libc.so.6", 0) == 0) { libc = "/usr/lib/libc.so.6"; libdir = "/usr/lib"; }
|
|
else if (access("/lib/x86_64-linux-gnu/libc.so.6", 0) == 0) { libc = "/lib/x86_64-linux-gnu/libc.so.6"; libdir = "/lib/x86_64-linux-gnu"; }
|
|
else if (access("/lib64/libc.so.6", 0) == 0) { libc = "/lib64/libc.so.6"; libdir = "/lib64"; }
|
|
if (!libc) { puts("dynentry: no libc.so.6 — skipping"); return 77; }
|
|
|
|
static char syms[200][32];
|
|
int ns = harvest_syms(libc, syms, 200);
|
|
if (ns < 130) {
|
|
printf("dynentry: only %d libc syms harvested (<130, can't overflow "
|
|
"first page) — skipping\n", ns);
|
|
return 77;
|
|
}
|
|
|
|
const char *bin = getenv("BIN");
|
|
if (!bin) bin = "out/bin";
|
|
char absbin[1024];
|
|
if (bin[0] != '/') {
|
|
char cwd[1024];
|
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
|
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
|
bin = absbin;
|
|
}
|
|
char cdrv[1100], wdrv[1100];
|
|
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
|
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
|
int have_ww = (access(wdrv, X_OK) == 0);
|
|
|
|
/* ---- big: ~160 gated dead PLT calls, return 42 ---- */
|
|
char bigsrc[80];
|
|
snprintf(bigsrc, sizeof bigsrc, "/tmp/dynent_big_%d.ww", getpid());
|
|
FILE *f = fopen(bigsrc, "wb");
|
|
if (!f) return 1;
|
|
fputs("package main;\n", f);
|
|
for (int i = 0; i < ns; i++)
|
|
fprintf(f, "@symbol(\"%s\") fn s%03d() void;\n", syms[i], i);
|
|
fputs("let g: i32 = 0;\nexport fn main() i32 = {\n", f);
|
|
for (int i = 0; i < ns; i++)
|
|
fprintf(f, "\tif (g != 0) { s%03d(); };\n", i);
|
|
fputs("\treturn 42;\n};\n", f);
|
|
fclose(f);
|
|
|
|
/* ---- small: 1 dyn sym, headers fit the page ---- */
|
|
char smallsrc[80];
|
|
snprintf(smallsrc, sizeof smallsrc, "/tmp/dynent_small_%d.ww", getpid());
|
|
f = fopen(smallsrc, "wb");
|
|
if (!f) return 1;
|
|
fputs("package main;\n"
|
|
"@symbol(\"getpid\") fn cgetpid() i32;\n"
|
|
"let g: i32 = 0;\n"
|
|
"export fn main() i32 = { if (g != 0) { cgetpid(); }; return 42; };\n",
|
|
f);
|
|
fclose(f);
|
|
|
|
struct { const char *label; const char *src; } cases[] = {
|
|
{ "big", bigsrc }, { "small", smallsrc },
|
|
};
|
|
|
|
int fail = 0, total = 0;
|
|
for (int ci = 0; ci < 2; ci++) {
|
|
total++;
|
|
char ctd[80], wtd[80], cob[160], wob[160];
|
|
snprintf(ctd, sizeof ctd, "/tmp/dynent_c_%d_%d", getpid(), ci);
|
|
snprintf(wtd, sizeof wtd, "/tmp/dynent_w_%d_%d", getpid(), ci);
|
|
mkdir(ctd, 0755); mkdir(wtd, 0755);
|
|
|
|
int crc = build_run(cdrv, cases[ci].src, libdir, ctd, cob, sizeof cob);
|
|
if (crc != 42) {
|
|
fprintf(stderr, "dynentry[cstage][%s]: exit %d want 42 "
|
|
"(e_entry into headers? #63)\n", cases[ci].label, crc);
|
|
fail++;
|
|
}
|
|
if (have_ww) {
|
|
int wrc = build_run(wdrv, cases[ci].src, libdir, wtd, wob, sizeof wob);
|
|
if (wrc != 42) {
|
|
fprintf(stderr, "dynentry[wwstage][%s]: exit %d want 42 "
|
|
"(e_entry into headers? #63)\n", cases[ci].label, wrc);
|
|
fail++;
|
|
}
|
|
char cmp[400];
|
|
snprintf(cmp, sizeof cmp, "cmp -s %s %s", cob, wob);
|
|
if (runwait(cmp) != 0) {
|
|
fprintf(stderr, "dynentry[%s]: cs/ww binary differ (#63)\n",
|
|
cases[ci].label);
|
|
fail++;
|
|
}
|
|
unlink(wob);
|
|
}
|
|
unlink(cob);
|
|
rmdir(ctd); rmdir(wtd);
|
|
}
|
|
|
|
unlink(bigsrc); unlink(smallsrc);
|
|
|
|
if (fail) {
|
|
fprintf(stderr, "dynentry_run: %d/%d check(s) failed\n", fail, total);
|
|
return 1;
|
|
}
|
|
printf("dynentry_run: %d/%d ok (%d dyn syms)\n", total, total, ns);
|
|
return 0;
|
|
}
|