From 4f9ba86058a01a362bc25ff700f4e4291174392d Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 14:47:02 +0900 Subject: [PATCH] test: port the w6l link-layout observers to ww link_test.ww absorbs 600_w6l and 620_data_link: ET_EXEC on the trivial pipeline, writable-global read/write runs, the exact two-PT_LOAD R+X/R+W split, the single-PT_LOAD no-DATAW invariant 992 depends on, and the BSS filesz trim (filesz 1 < memsz 32, loader zero-fill). Strengthened: the ldd staticness proxy is replaced by a direct no-PT_INTERP phdr scan. --- Makefile | 2 +- test/object/link_test.ww | 256 +++++++++++++++++++++++++++++ test/wcc/600_w6l.c | 68 -------- test/wcc/620_data_link.c | 337 --------------------------------------- 4 files changed, 257 insertions(+), 406 deletions(-) create mode 100644 test/object/link_test.ww delete mode 100644 test/wcc/600_w6l.c delete mode 100644 test/wcc/620_data_link.c diff --git a/Makefile b/Makefile index 088ffcf2..14c6994b 100644 --- a/Makefile +++ b/Makefile @@ -395,7 +395,7 @@ ASM_WW_TARGETS = $(ASM_WW_TESTS:%=wwtest/%) # layout, program-header split and BSS trim, archive selectivity, # linker flag capacity, assembler input gates). Compiler/driver gates # like test/sep: they run under test-compiler. -OBJECT_WW_TESTS = test/object/elfobj_test.ww +OBJECT_WW_TESTS = test/object/elfobj_test.ww test/object/link_test.ww OBJECT_WW_TARGETS = $(OBJECT_WW_TESTS:%=wwtest/%) BOOTSTRAP_WRAPPER_SOURCES = test/wcc/950_selfcheck.c \ test/wcc/991_w6a_ww.c \ diff --git a/test/object/link_test.ww b/test/object/link_test.ww new file mode 100644 index 00000000..667d9b07 --- /dev/null +++ b/test/object/link_test.ww @@ -0,0 +1,256 @@ +package link_test; + +// w6l LINK-level observers. Ports of the retired native carriers +// test/wcc/600_w6l.c and 620_data_link.c; every assertion preserved. +// +// staticexec (600) — w6c/w6a/w6l on a trivial program produce an +// ELF64 ET_EXEC. Strengthened over the carrier: the ldd host-tool +// proxy for staticness is replaced by a direct scan of every program +// header asserting no PT_INTERP. +// +// readglobal / writeglobal (620) — a DATAW global is readable (exit +// 42) and the R+W mapping truly writable (store 99, read back, exit +// 99). +// +// 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). +// +// nodatasingleload (620) — an input without DATAW keeps the single +// PT_LOAD layout; test 992 (selfhost w6l diff, the terminal bootstrap +// gate) depends on that invariant. +// +// bssfilesz (620) — trailing .data zeros are trimmed from the file: +// the R+W PT_LOAD has p_filesz 1 < p_memsz 32 (the trim ignores +// symbol boundaries — nz's own zero tail goes too), and the loader +// zero-fills the gap (run exit 7). +// +// Dropped C machinery, not assertions: wwtestpkg.h package-clause +// injection and per-file unlink accounting (testenv.clean). + +import os; +import os.exec; +import strings; +import testenv; +import time; + +fn fail(label: str, why: str) void = { + let m: str = strings.concat("link FAIL: ", label, " -- ", why, + "\n"); + os.write(2, m.ptr, m.len: u64); + assert(false); +}; + +fn tmo() time.duration = { + return (240i64 * (time.second: i64)): time.duration; +}; + +// -1 encodes an abnormal (non-EXIT) termination, never a valid code. +fn runcode(dir: str, name: str, argv: []str) i32 = { + let co: testenv.commandout; + testenv.runcommand(dir, dir, name, argv, tmo(), &co); + if (co.termination != exec.termination.EXIT) { return -1; }; + return co.code; +}; + +// External-ABI constants (System V gABI, ELF64 executables) — the +// layout contract w6l's exe writer encodes: e_ident magic + class at +// 0..4, Ehdr e_type at 16 (u16), e_phoff at 32 (u64), e_phnum at 56 +// (u16); Phdr entries of 56 bytes with p_type +0 (u32), p_flags +4 +// (u32), p_filesz +32 (u64), p_memsz +40 (u64). PT_LOAD == 1, +// PT_INTERP == 3; PF_X == 1, PF_W == 2, PF_R == 4. +def PHENT: i32 = 56; + +fn phnum(o: str) i32 = { return testenv.leu16(o, 56): i32; }; + +fn phoff(o: str, i: i32) i32 = { + return (testenv.leu64(o, 32): i32) + PHENT * i; +}; + +fn iself64(o: str) bool = { + if (o.len < 64) { return false; }; + return o[0] == 0x7fu8 && o[1] == 0x45u8 && o[2] == 0x4cu8 + && o[3] == 0x46u8 && o[4] == 2u8; +}; + +// assemble+link a hand-written .s; returns the exe path. +fn build(td: str, tag: str, body: str) str = { + let src: str = strings.concat(td, "/", tag, ".s"); + let obj: str = strings.concat(td, "/", tag, ".o"); + let exe: str = strings.concat(td, "/", tag, ".x"); + testenv.writefile(src, body); + let aav: []str = [testenv.driver("w6a"), "-o", obj, src]; + if (runcode(td, strings.concat("a_", tag), aav) != 0) { + fail(tag, "w6a failed"); + }; + let lav: []str = [testenv.driver("w6l"), "-o", exe, obj]; + if (runcode(td, strings.concat("l_", tag), lav) != 0) { + fail(tag, "w6l failed"); + }; + return exe; +}; + +fn runexit(td: str, tag: str, exe: str) i32 = { + let rav: []str = [exe]; + return runcode(td, strings.concat("run_", tag), rav); +}; + +// ---- staticexec (600) -------------------------------------------------- + +@test fn staticexec() void = { + let td: str = testenv.fresh(); + let src: str = strings.concat(td, "/m.ww"); + let asmf: str = strings.concat(td, "/m.s"); + let obj: str = strings.concat(td, "/m.o"); + let exe: str = strings.concat(td, "/m.x"); + testenv.writefile(src, + "package main;\nfn main() i32 = { return 42; };\n"); + let cav: []str = [testenv.driver("w6c"), "-o", asmf, src]; + if (runcode(td, "c_m", cav) != 0) { fail("staticexec", "w6c failed"); }; + let aav: []str = [testenv.driver("w6a"), "-o", obj, asmf]; + if (runcode(td, "a_m", aav) != 0) { fail("staticexec", "w6a failed"); }; + let lav: []str = [testenv.driver("w6l"), "-o", exe, obj]; + if (runcode(td, "l_m", lav) != 0) { fail("staticexec", "w6l failed"); }; + + let o: str = testenv.readfile(exe); + if (!iself64(o)) { fail("staticexec", "not an ELF64"); }; + // ET_EXEC == 2 + if (testenv.leu16(o, 16) != 2u64) { + fail("staticexec", "e_type != ET_EXEC"); + }; + // static executable: no PT_INTERP anywhere in the phdr table + let i: i32 = 0; + for (i < phnum(o)) { + if (testenv.leu32(o, phoff(o, i)) == 3u64) { + fail("staticexec", "PT_INTERP present, want static"); + }; + i += 1; + }; + testenv.clean(td); +}; + +// ---- readglobal (620 test_read) ---------------------------------------- + +@test fn readglobal() void = { + let td: str = testenv.fresh(); + let exe: str = build(td, "rd", strings.concat( + "TEXT _start,$0\n", + "\tMOVQ\tcounter(SB), DI\n", + "\tMOVQ\t$60, AX\n", + "\tSYSCALL\n", + "DATAW counter(SB),\"\\x2a\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n")); + if (runexit(td, "rd", exe) != 42) { + fail("readglobal", "exit != 42"); + }; + testenv.clean(td); +}; + +// ---- writeglobal (620 test_write) -------------------------------------- + +@test fn writeglobal() void = { + let td: str = testenv.fresh(); + let exe: str = build(td, "wr", strings.concat( + "TEXT _start,$0\n", + "\tMOVQ\t$99, AX\n", + "\tMOVQ\tAX, counter(SB)\n", + "\tMOVQ\tcounter(SB), DI\n", + "\tMOVQ\t$60, AX\n", + "\tSYSCALL\n", + "DATAW counter(SB),\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n")); + if (runexit(td, "wr", exe) != 99) { + fail("writeglobal", "exit != 99 (R+W mapping not writable?)"); + }; + testenv.clean(td); +}; + +// ---- twoloads (620 test_two_loads) ------------------------------------- + +@test fn twoloads() void = { + let td: str = testenv.fresh(); + let exe: str = build(td, "tl", strings.concat( + "TEXT _start,$0\n", + "\tMOVQ\tcounter(SB), AX\n", + "\tMOVQ\t$60, AX\n", + "\tMOVQ\t$0, DI\n", + "\tSYSCALL\n", + "DATAW counter(SB),\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n")); + let o: str = testenv.readfile(exe); + if (phnum(o) != 2) { fail("twoloads", "e_phnum != 2"); }; + let sawrx: bool = false; + let sawrw: bool = false; + let i: i32 = 0; + for (i < 2) { + let ph: i32 = phoff(o, i); + if (testenv.leu32(o, ph) != 1u64) { + 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; } + else { + fail("twoloads", "phdr flags not exactly R+X or R+W"); + }; + i += 1; + }; + if (!sawrx) { fail("twoloads", "missing R+X PT_LOAD"); }; + if (!sawrw) { fail("twoloads", "missing R+W PT_LOAD"); }; + testenv.clean(td); +}; + +// ---- nodatasingleload (620 test_no_data_single_load) ------------------- + +@test fn nodatasingleload() void = { + let td: str = testenv.fresh(); + let exe: str = build(td, "nd", strings.concat( + "TEXT _start,$0\n", + "\tMOVQ\t$60, AX\n", + "\tMOVQ\t$0, DI\n", + "\tSYSCALL\n")); + let o: str = testenv.readfile(exe); + // 992 (selfhost w6l diff) depends on the single-PT_LOAD layout. + if (phnum(o) != 1) { + fail("nodatasingleload", "e_phnum != 1 (legacy layout lost)"); + }; + testenv.clean(td); +}; + +// ---- bssfilesz (620 test_bss_filesz) ----------------------------------- + +@test fn bssfilesz() void = { + let td: str = testenv.fresh(); + // nz = \x07 + 7 zeros, zlong = 24 zeros: memsz covers all 32B, + // the file stops after the single non-zero byte. + let exe: str = build(td, "bss", strings.concat( + "TEXT _start,$0\n", + "\tMOVQ\tnz(SB), DI\n", + "\tMOVQ\t$60, AX\n", + "\tSYSCALL\n", + "DATAW nz(SB),\"\\x07\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n", + "DATAW zlong(SB),\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00", + "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00", + "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n")); + let o: str = testenv.readfile(exe); + let sawrw: bool = false; + let i: i32 = 0; + for (i < phnum(o)) { + let ph: i32 = phoff(o, i); + if (testenv.leu32(o, ph) != 1u64) { i += 1; continue; }; + if (testenv.leu32(o, ph + 4) != 6u64) { i += 1; continue; }; + sawrw = true; + let filesz: u64 = testenv.leu64(o, ph + 32); + let memsz: u64 = testenv.leu64(o, ph + 40); + if (filesz >= memsz) { + fail("bssfilesz", "want filesz < memsz"); + }; + // the trim ignores symbol boundaries: nz's zero tail goes too + if (filesz != 1u64) { fail("bssfilesz", "filesz != 1"); }; + if (memsz != 32u64) { fail("bssfilesz", "memsz != 32"); }; + i += 1; + }; + if (!sawrw) { fail("bssfilesz", "no R+W PT_LOAD found"); }; + // the loader must zero-fill the BSS gap + if (runexit(td, "bss", exe) != 7) { + fail("bssfilesz", "exit != 7"); + }; + testenv.clean(td); +}; diff --git a/test/wcc/600_w6l.c b/test/wcc/600_w6l.c deleted file mode 100644 index 743b2487..00000000 --- a/test/wcc/600_w6l.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 600_6l — linker smoke. Drive w6c → w6a → w6l on a tiny program, - * confirm the result is a static ELF executable with no PT_INTERP. - */ -#include -#include -#include -#include -#include "wwtestpkg.h" - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - - char src[64], asmf[64], obj[64], exe[64]; - snprintf(src, sizeof src, "/tmp/wwt_%d.ww", getpid()); - snprintf(asmf, sizeof asmf, "/tmp/wwt_%d.s", getpid()); - snprintf(obj, sizeof obj, "/tmp/wwt_%d.o", getpid()); - snprintf(exe, sizeof exe, "/tmp/wwt_%d.x", getpid()); - - FILE *f = fopen(src, "wb"); - wwtest_fputs("fn main() i32 = { return 42; };", f); - fclose(f); - - char cmd[1024]; - snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, asmf, src); - if (system(cmd) != 0) { fprintf(stderr, "w6c failed\n"); return 1; } - snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, obj, asmf); - if (system(cmd) != 0) { fprintf(stderr, "w6a failed\n"); return 1; } - snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s", bin, exe, obj); - if (system(cmd) != 0) { fprintf(stderr, "w6l failed\n"); return 1; } - - /* validate ELF magic + e_type=EXEC */ - FILE *of = fopen(exe, "rb"); - if (of == NULL) { fprintf(stderr, "exe missing\n"); return 1; } - unsigned char hdr[20]; - if (fread(hdr, 1, sizeof hdr, of) != sizeof hdr) { fprintf(stderr, "short exe\n"); fclose(of); return 1; } - fclose(of); - - if (memcmp(hdr, "\x7f""ELF", 4) != 0 || hdr[4] != 2) { - fprintf(stderr, "not an ELF64\n"); return 1; - } - /* e_type at offset 16, little-endian u16; ET_EXEC = 2 */ - unsigned short etype = (unsigned short)hdr[16] | ((unsigned short)hdr[17] << 8); - if (etype != 2) { - fprintf(stderr, "not ET_EXEC, got %u\n", etype); return 1; - } - - /* ldd "not a dynamic executable" — proxy: check that file is not - * dynamic by looking for PT_INTERP. We have none, so ldd reports - * "not a dynamic executable" or similar. */ - snprintf(cmd, sizeof cmd, "ldd %s 2>&1", exe); - FILE *p = popen(cmd, "r"); - char buf[256] = {0}; - fread(buf, 1, sizeof buf - 1, p); - pclose(p); - if (strstr(buf, "not a dynamic executable") == NULL - && strstr(buf, "statically linked") == NULL) { - fprintf(stderr, "ldd says not static: %s\n", buf); - return 1; - } - - unlink(src); unlink(asmf); unlink(obj); unlink(exe); - printf("w6l: ok\n"); - return 0; -} diff --git a/test/wcc/620_data_link.c b/test/wcc/620_data_link.c deleted file mode 100644 index b23e4290..00000000 --- a/test/wcc/620_data_link.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * 620_data_link — end-to-end w6l support for writable globals. - * Assemble a small .s using DATAW, link it with w6l, run the binary, - * and verify both that the read returns the global's value and that - * a store updates it. Also confirms the binary has two PT_LOADs with - * the right R+X / R+W flags. - * - * Inputs without DATAW must still produce the single-PT_LOAD layout - * — 992 (selfhost w6l .o diff) depends on that invariant. - */ -#include -#include -#include -#include -#include -#include - -#define PT_LOAD 1 -#define PF_X 1 -#define PF_W 2 -#define PF_R 4 - -#pragma pack(push, 1) -typedef struct { - uint8_t e_ident[16]; - uint16_t e_type, e_machine; - uint32_t e_version; - uint64_t e_entry, e_phoff, e_shoff; - uint32_t e_flags; - uint16_t e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx; -} Ehdr; - -typedef struct { - uint32_t p_type, p_flags; - uint64_t p_offset, p_vaddr, p_paddr; - uint64_t p_filesz, p_memsz, p_align; -} Phdr; -#pragma pack(pop) - -static int -write_file(const char *path, const char *body) -{ - FILE *f = fopen(path, "wb"); - if (!f) return -1; - fputs(body, f); - fclose(f); - return 0; -} - -static int -slurp(const char *path, uint8_t **out, size_t *outn) -{ - FILE *f = fopen(path, "rb"); - if (!f) return -1; - fseek(f, 0, SEEK_END); - long n = ftell(f); - fseek(f, 0, SEEK_SET); - uint8_t *b = malloc((size_t)n); - if (fread(b, 1, (size_t)n, f) != (size_t)n) { - free(b); - fclose(f); - return -1; - } - fclose(f); - *out = b; - *outn = (size_t)n; - return 0; -} - -static int -build(const char *bin, const char *body, const char *exe) -{ - char src[64], obj[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/wwt_dl_%d.s", getpid()); - snprintf(obj, sizeof obj, "/tmp/wwt_dl_%d.o", getpid()); - if (write_file(src, body) < 0) return -1; - - snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, obj, src); - if (system(cmd) != 0) { unlink(src); return -1; } - snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s", bin, exe, obj); - int rc = system(cmd); - unlink(src); unlink(obj); - return rc; -} - -static int -run_exit(const char *exe) -{ - int rc = system(exe); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -/* Test 1: read a writable global. _start exits with the value stored - * in `counter` (0x2a == 42). */ -static int -test_read(const char *bin) -{ - char exe[64]; - snprintf(exe, sizeof exe, "/tmp/wwt_dl_%d.x1", getpid()); - const char *body = - "TEXT _start,$0\n" - "\tMOVQ\tcounter(SB), DI\n" - "\tMOVQ\t$60, AX\n" - "\tSYSCALL\n" - "DATAW counter(SB),\"\\x2a\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n"; - if (build(bin, body, exe) != 0) { - fprintf(stderr, "test_read: build failed\n"); - return -1; - } - int rc = run_exit(exe); - unlink(exe); - if (rc != 42) { - fprintf(stderr, "test_read: exit=%d, want 42\n", rc); - return -1; - } - return 0; -} - -/* Test 2: store to a writable global, read it back, exit with the new - * value. Verifies the R+W mapping really is writable. */ -static int -test_write(const char *bin) -{ - char exe[64]; - snprintf(exe, sizeof exe, "/tmp/wwt_dl_%d.x2", getpid()); - const char *body = - "TEXT _start,$0\n" - "\tMOVQ\t$99, AX\n" - "\tMOVQ\tAX, counter(SB)\n" - "\tMOVQ\tcounter(SB), DI\n" - "\tMOVQ\t$60, AX\n" - "\tSYSCALL\n" - "DATAW counter(SB),\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n"; - if (build(bin, body, exe) != 0) { - fprintf(stderr, "test_write: build failed\n"); - return -1; - } - int rc = run_exit(exe); - unlink(exe); - if (rc != 99) { - fprintf(stderr, "test_write: exit=%d, want 99\n", rc); - return -1; - } - return 0; -} - -/* Test 3: structurally inspect the linked ELF — two PT_LOADs, the - * first R+X and the second R+W. Catches regressions where the layout - * is right but the flags or count are off. */ -static int -test_two_loads(const char *bin) -{ - char exe[64]; - snprintf(exe, sizeof exe, "/tmp/wwt_dl_%d.x3", getpid()); - const char *body = - "TEXT _start,$0\n" - "\tMOVQ\tcounter(SB), AX\n" - "\tMOVQ\t$60, AX\n" - "\tMOVQ\t$0, DI\n" - "\tSYSCALL\n" - "DATAW counter(SB),\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n"; - if (build(bin, body, exe) != 0) { - fprintf(stderr, "test_two_loads: build failed\n"); - return -1; - } - uint8_t *buf = NULL; - size_t n = 0; - if (slurp(exe, &buf, &n) < 0) { - fprintf(stderr, "test_two_loads: cannot read exe\n"); - unlink(exe); - return -1; - } - unlink(exe); - - const Ehdr *eh = (const Ehdr *)buf; - int rc = 0; - if (eh->e_phnum != 2) { - fprintf(stderr, "test_two_loads: e_phnum=%u, want 2\n", eh->e_phnum); - rc = -1; - goto out; - } - const Phdr *ph = (const Phdr *)(buf + eh->e_phoff); - int saw_rx = 0, saw_rw = 0; - for (int i = 0; i < 2; i++) { - if (ph[i].p_type != PT_LOAD) { - fprintf(stderr, "test_two_loads: ph[%d] not PT_LOAD\n", i); - rc = -1; - continue; - } - if (ph[i].p_flags == (PF_R | PF_X)) saw_rx = 1; - else if (ph[i].p_flags == (PF_R | PF_W)) saw_rw = 1; - else { - fprintf(stderr, "test_two_loads: ph[%d] flags=0x%x\n", - i, ph[i].p_flags); - rc = -1; - } - } - if (!saw_rx) { fprintf(stderr, "test_two_loads: missing R+X\n"); rc = -1; } - if (!saw_rw) { fprintf(stderr, "test_two_loads: missing R+W\n"); rc = -1; } -out: - free(buf); - return rc; -} - -/* Test 4: an input without DATAW still produces a single PT_LOAD. - * 992 (selfhost w6l .o diff) depends on this byte-level invariant. */ -static int -test_no_data_single_load(const char *bin) -{ - char exe[64]; - snprintf(exe, sizeof exe, "/tmp/wwt_dl_%d.x4", getpid()); - const char *body = - "TEXT _start,$0\n" - "\tMOVQ\t$60, AX\n" - "\tMOVQ\t$0, DI\n" - "\tSYSCALL\n"; - if (build(bin, body, exe) != 0) { - fprintf(stderr, "test_no_data_single_load: build failed\n"); - return -1; - } - uint8_t *buf = NULL; - size_t n = 0; - if (slurp(exe, &buf, &n) < 0) { - fprintf(stderr, "test_no_data_single_load: cannot read exe\n"); - unlink(exe); - return -1; - } - unlink(exe); - int rc = 0; - const Ehdr *eh = (const Ehdr *)buf; - if (eh->e_phnum != 1) { - fprintf(stderr, "test_no_data_single_load: e_phnum=%u, want 1\n", - eh->e_phnum); - rc = -1; - } - free(buf); - return rc; -} - -/* Test 5: BSS optimisation — trailing zero bytes in .data are dropped - * from the file (p_filesz < p_memsz) and the loader zero-fills the gap. - * Asserts that a trailing zero-init slot doesn't bloat the binary. */ -static int -test_bss_filesz(const char *bin) -{ - char exe[64]; - snprintf(exe, sizeof exe, "/tmp/wwt_dl_%d.x5", getpid()); - /* Two DATAW slots: a 1-byte non-zero followed by a 31-byte zero - * run. Memsz must cover all 32B; filesz should stop after the 1B - * non-zero (so file_data == 1, memsz_data == 32). */ - const char *body = - "TEXT _start,$0\n" - "\tMOVQ\tnz(SB), DI\n" - "\tMOVQ\t$60, AX\n" - "\tSYSCALL\n" - "DATAW nz(SB),\"\\x07\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n" - "DATAW zlong(SB),\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00" - "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00" - "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n"; - if (build(bin, body, exe) != 0) { - fprintf(stderr, "test_bss_filesz: build failed\n"); - return -1; - } - uint8_t *buf = NULL; - size_t n = 0; - if (slurp(exe, &buf, &n) < 0) { - fprintf(stderr, "test_bss_filesz: cannot read exe\n"); - unlink(exe); - return -1; - } - int rc = 0; - const Ehdr *eh = (const Ehdr *)buf; - const Phdr *ph = (const Phdr *)(buf + eh->e_phoff); - int saw_rw = 0; - for (int i = 0; i < eh->e_phnum; i++) { - if (ph[i].p_type != PT_LOAD) continue; - if (ph[i].p_flags != (PF_R | PF_W)) continue; - saw_rw = 1; - if (ph[i].p_filesz >= ph[i].p_memsz) { - fprintf(stderr, "test_bss_filesz: filesz=%lu, " - "memsz=%lu (want filesz