From ac8ddee6c12e52f57059d02f99fbcaf54a295322 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sun, 9 Aug 2026 04:37:33 +0900 Subject: [PATCH] w6l: purge dead elf branches; pin rx extent --- cmd/w6l/dyn.c | 13 +------------ cmd/w6l/dynout.c | 3 ++- cmd/w6l/out.c | 5 ++--- selfhost/cmd/w6l/dyn.ww | 5 +---- selfhost/cmd/w6l/dynout.ww | 2 ++ selfhost/cmd/w6l/obj.ww | 10 ++++------ selfhost/cmd/w6l/out.ww | 2 ++ test/object/link_test.ww | 22 +++++++++++++++++++--- 8 files changed, 33 insertions(+), 29 deletions(-) diff --git a/cmd/w6l/dyn.c b/cmd/w6l/dyn.c index c21baccd..a502ac50 100644 --- a/cmd/w6l/dyn.c +++ b/cmd/w6l/dyn.c @@ -23,7 +23,6 @@ /* Versym special values: 0 = local, 1 = base/global. */ #define VER_NDX_LOCAL 0 #define VER_NDX_GLOBAL 1 -#define VER_FLG_BASE 1 #define VERSYM_HIDDEN 0x8000 #define VERSYM_VERSION 0x7fff @@ -208,17 +207,7 @@ l_load_so(Lnk *l, const char *path) } else if (verdef_names != NULL && (int)vidx <= verdef_max && verdef_names[vidx] != NULL) { - /* index 1 in glibc's Verdef is the SONAME with - * VER_FLG_BASE — we skip its export entries - * as a side effect of vidx==1 mapping to the - * BASE name (e.g. "libc.so.6"), which never - * appears as a reference target. Treat any - * lookup that lands on the BASE entry as - * unversioned. */ - if (vidx == 1) - vername = NULL; - else - vername = verdef_names[vidx]; + vername = verdef_names[vidx]; } } diff --git a/cmd/w6l/dynout.c b/cmd/w6l/dynout.c index f210214f..a5b47091 100644 --- a/cmd/w6l/dynout.c +++ b/cmd/w6l/dynout.c @@ -376,7 +376,6 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry) const u64 gotplt_va = base + gotplt_off; const u64 dynamic_va = base + dynamic_off; const u64 data_va = base + data_off; - (void)dynstr_va; (void)hash_va; (void)plt_va; /* Now that the dyn layout pins text_va/data_va, apply * relocations. main.c defers this; the static path runs it from @@ -587,6 +586,8 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry) ph[0].p_vaddr = base; ph[0].p_paddr = base; ph[0].p_filesz = rx_end; + /* The loader page-rounds mappings; gotplt_off independently starts + * the next page, so padding is not part of the R+X extent. */ ph[0].p_memsz = rx_end; ph[0].p_align = page; diff --git a/cmd/w6l/out.c b/cmd/w6l/out.c index fa4c17ff..c929abee 100644 --- a/cmd/w6l/out.c +++ b/cmd/w6l/out.c @@ -80,8 +80,6 @@ l_emit_elf(Lnk *l, FILE *f, u64 base, u64 entry) bsslen++; } const u64 data_file_len = l->datalen - bsslen; - const u64 file_end = has_data ? (data_off + data_file_len) : rx_end; - (void)data_va; Ehdr eh = {0}; memcpy(eh.e_ident, "\x7f""ELF", 4); @@ -104,6 +102,8 @@ l_emit_elf(Lnk *l, FILE *f, u64 base, u64 entry) phx.p_vaddr = base; phx.p_paddr = base; phx.p_filesz = rx_end; + /* The loader page-rounds mappings; when present, data_off starts + * independently on the next page, so padding is not R+X extent. */ phx.p_memsz = rx_end; phx.p_align = page; @@ -133,7 +133,6 @@ l_emit_elf(Lnk *l, FILE *f, u64 base, u64 entry) for (long i = here; i < (long)data_off; i++) fputc(0, f); fwrite(l->data, 1, data_file_len, f); } - (void)file_end; return 0; } diff --git a/selfhost/cmd/w6l/dyn.ww b/selfhost/cmd/w6l/dyn.ww index e4caa0ee..c3a24e81 100644 --- a/selfhost/cmd/w6l/dyn.ww +++ b/selfhost/cmd/w6l/dyn.ww @@ -315,10 +315,7 @@ export fn loadso(l: *lnk, path: *u8) i32 = { switch (vidx) { case VER_NDX_LOCAL_C: keep = 0; // not exported - case VER_NDX_GLOBAL_C,1u16: - // glibc's BASE entry (vidx==1): treat as - // unversioned. (The C version notes that - // vidx==1 in Verdef maps to the SONAME BASE.) + case VER_NDX_GLOBAL_C: vernamecs = nil; case: if (verstr != nil) { diff --git a/selfhost/cmd/w6l/dynout.ww b/selfhost/cmd/w6l/dynout.ww index c56c0b8c..e565f01d 100644 --- a/selfhost/cmd/w6l/dynout.ww +++ b/selfhost/cmd/w6l/dynout.ww @@ -668,6 +668,8 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { dwr64(filebuf.ptr, p0 + 16u64, base); dwr64(filebuf.ptr, p0 + 24u64, base); dwr64(filebuf.ptr, p0 + 32u64, rxend); + // The loader page-rounds mappings; gotpltoff independently starts + // the next page, so padding is not part of the R+X extent. dwr64(filebuf.ptr, p0 + 40u64, rxend); dwr64(filebuf.ptr, p0 + 48u64, PAGE); diff --git a/selfhost/cmd/w6l/obj.ww b/selfhost/cmd/w6l/obj.ww index 622827a3..735eb665 100644 --- a/selfhost/cmd/w6l/obj.ww +++ b/selfhost/cmd/w6l/obj.ww @@ -14,8 +14,8 @@ def SHT_SYMTAB: i32 = 2; def SHT_STRTAB: i32 = 3; def SHT_RELA: i32 = 4; -// w6a/w6l use straight LE on amd64. Reading via byte offsets keeps us off -// the cgen's u16 field-load story for now (MOVZBQ exists; MOVZWQ doesn't). +// ELF64/x86-64 is little-endian; bytewise decoding also avoids alignment +// and host-layout assumptions about input buffers. fn rdu16(p: *u8, off: u64) u16 = { let b0: u16 = p[off]: u16; @@ -507,9 +507,8 @@ fn loadimage(l: *lnk, path: *u8, buf: *u8, len: u64) i32 = { }; }; - // We don't keep a per-object map[] of *lsym. Instead the reloc - // loop re-walks symtab and re-interns by name. Simpler than - // dancing around the cgen's u64-shift gaps. + // Rewalking avoids retaining an nsyms-sized per-object map solely + // for relocation lookup. let si: u64 = 1u64; // skip index 0 (always undef sentinel) for (si < nsyms) { let symp: u64 = symoff + si * SYM_SIZE; @@ -526,7 +525,6 @@ fn loadimage(l: *lnk, path: *u8, buf: *u8, len: u64) i32 = { if (idxdata >= 0) { indt = (stshndx: i32) == idxdata; }; - if (intext && indt) { indt = false; }; if (intext) { if (gs.defined != 0) { let m: str = "w6l: duplicate symbol\n"; diff --git a/selfhost/cmd/w6l/out.ww b/selfhost/cmd/w6l/out.ww index 8752210a..96f9a317 100644 --- a/selfhost/cmd/w6l/out.ww +++ b/selfhost/cmd/w6l/out.ww @@ -111,6 +111,8 @@ export fn emitelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { wru64(hdr.ptr, 80u64, base); // p_vaddr wru64(hdr.ptr, 88u64, base); // p_paddr wru64(hdr.ptr, 96u64, rxend); // p_filesz + // The loader page-rounds mappings; when present, dataoff starts + // independently on the next page, so padding is not R+X extent. wru64(hdr.ptr, 104u64, rxend); // p_memsz wru64(hdr.ptr, 112u64, TEXT_OFF); // p_align diff --git a/test/object/link_test.ww b/test/object/link_test.ww index 3834231d..942601cd 100644 --- a/test/object/link_test.ww +++ b/test/object/link_test.ww @@ -14,7 +14,8 @@ package link_test; // // twoloads (620) — a DATAW input links to exactly 2 program headers, // both PT_LOAD, exactly one R+X and one R+W (exact p_flags equality; -// any other combination fails). +// any other combination fails). R+X filesz/memsz stop at content rather +// than absorbing the page padding before R+W. // // nodatasingleload (620) — an input without DATAW keeps the single // PT_LOAD layout; test 992 (selfhost w6l diff, the terminal bootstrap @@ -169,6 +170,9 @@ fn runexit(td: str, tag: str, exe: str) i32 = { if (phnum(o) != 2) { fail("twoloads", "e_phnum != 2"); }; let sawrx: bool = false; let sawrw: bool = false; + let rxfilesz: u64 = 0u64; + let rxmemsz: u64 = 0u64; + let rwoff: u64 = 0u64; let i: i32 = 0; for (i < 2) { let ph: i32 = phoff(o, i); @@ -176,8 +180,14 @@ fn runexit(td: str, tag: str, exe: str) i32 = { fail("twoloads", "phdr not PT_LOAD"); }; let flags: u64 = testenv.leu32(o, ph + 4); - if (flags == 5u64) { sawrx = true; } - else if (flags == 6u64) { sawrw = true; } + if (flags == 5u64) { + sawrx = true; + rxfilesz = testenv.leu64(o, ph + 32); + rxmemsz = testenv.leu64(o, ph + 40); + } else if (flags == 6u64) { + sawrw = true; + rwoff = testenv.leu64(o, ph + 8); + } else { fail("twoloads", "phdr flags not exactly R+X or R+W"); }; @@ -185,6 +195,12 @@ fn runexit(td: str, tag: str, exe: str) i32 = { }; if (!sawrx) { fail("twoloads", "missing R+X PT_LOAD"); }; if (!sawrw) { fail("twoloads", "missing R+W PT_LOAD"); }; + if (rxfilesz != rxmemsz) { + fail("twoloads", "R+X filesz != memsz"); + }; + if (rxmemsz >= rwoff) { + fail("twoloads", "R+X memsz includes page padding"); + }; testenv.clean(td); };