From 9f48d08deef070dacf26d63114917179abad0982 Mon Sep 17 00:00:00 2001 From: alice Date: Fri, 28 Aug 2026 20:35:25 +0000 Subject: [PATCH] alpha-crtl: 3 rungs so REAL zlib 1.3.1 links zero-deferred out-of-box Close the three CRTL residuals a prior probe measured on the alpha-dec-vms port (all 15 zlib TUs already compile clean at harness flags; these were the LINK/header-surface gaps that kept it from linking without manual symbol adds or labelled probes): 1. unixio.h on the port CRTL include surface. zconf.h does `#ifdef VMS #include ` -- the alpha-dec-vms port predefines VMS (correctly; it IS a VMS toolchain), but the musl-alpha CRTL header surface had no . Add a faithful, DECLARE-ONLY to the alpha-dec-vms arch overlay (open/close/read/write/lseek/dup/dup2/isatty/ unlink/access/ftruncate/fsync/chdir/getcwd) -- every one already IMPLEMENTED by the port musl; signatures track musl's own unistd.h/fcntl.h so a TU that includes both sees identical prototypes. VMS-target-scoped (arch overlay), never surfaced for a non-VMS target. 2. Export the errno accessor from the alpha DECC$SHR. musl's defines `errno` as `(*__errno_location())`; that plain, musl-internal name is not a DEC C RTL surface name, so the alpha cc1 does NOT decorate it to decc$* and the linker-view decc$ enumeration misses it. LINK.EXE's EVAX reader names the external C symbol with one leading underscore prepended (uniformly for the musl definition and every consumer reference), so at the vector level it is `___errno_location` (three underscores) -- the exact name gzread.obj deferred. Add `___errno_location=PROCEDURE` to the alpha-branch symbol vector, bringing alpha to parity with the generic (x86_64/aarch64) branch, which already exports the errno accessor. Alpha-branch only; the generic branch is untouched. 3. --use LIBOTS$SHR.EXE on the canonical consumer link. The port lowers every integer divide/remainder to OTS$DIV_*/OTS$REM_* (Alpha has no integer-divide instruction); those universals live in the SEPARATE LIBOTS$ shareable. DECC$SHR imports OTS$ for its own use but does not transitively re-export it, so a consumer that divides (zlib, and most real C) defers OTS$DIV_UL/ OTS$REM_UI against DECC$SHR alone. Add `--use LIBOTS$SHR.EXE` alongside `--use DECC$SHR.EXE` in build-joint-image.sh's link recipe. LINK binds only REFERENCED imports, so this is inert for programs (like the default joint_main) that emit no OTS$ call. VERIFIED (containerized, build-to-/tmp, Rule-9-clean; NOT run under qemu-user): REAL zlib 1.3.1 (pinned sha256 9a93b2b7...df23) -- all 15 TUs + a compress/ uncompress round-trip main cross-compiled at harness flags (-mpointer-size=64 -g0) + -DZ_HAVE_UNISTD_H, linked against the FIXED DECC$SHR/LIBOTS$ via the FIXED recipe -> 0 %LINK-F-UNDEF OUT-OF-BOX (no manual adds), EM_ALPHA ET_DYN. No regression: default joint_e2e (crt0 + joint_main, DECC$SHR only) still links clean; the errno edit is inside the alpha branch of mk_decc_shr.sh so the aarch64 crtl_rms / generic branch is byte-unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/vmslink/mk_decc_shr.sh | 22 ++++++- .../joint-e2e/build-joint-image.sh | 16 ++++- .../musl-arch/arch/alpha-dec-vms/unixio.h | 59 +++++++++++++++++++ 3 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 tools/cross-alpha-vms/musl-arch/arch/alpha-dec-vms/unixio.h diff --git a/src/vmslink/mk_decc_shr.sh b/src/vmslink/mk_decc_shr.sh index 9f0778d07..2c680d74b 100755 --- a/src/vmslink/mk_decc_shr.sh +++ b/src/vmslink/mk_decc_shr.sh @@ -246,7 +246,27 @@ if [ "$OVMX_DECC_ARCH" = alpha ]; then # (x86_64/aarch64) DECC$SHR. # ovmx_get_libc -- OVMX loader glue (ovmx_libc_stub.c), likewise a # plain OVMX-original name never decorated. - VEC="$VEC,__init_libc=PROCEDURE,__copy_tls=PROCEDURE,__init_tp=PROCEDURE,ovmx_get_libc=PROCEDURE" + # ___errno_location -- the port's errno ACCESSOR. musl's defines + # `errno` as `(*__errno_location())` (a plain, undecorated C name), so + # EVERY consumer TU that touches errno emits a reference to it. It is + # "musl-internal", NOT a recognized DEC C RTL surface name, so the alpha + # cc1 does NOT decorate it to decc$* and the ^decc$ enumeration above + # misses it -- exactly as it misses __init_tp/__copy_tls. (The DEC-C- + # decorated decc$get_errno_addr IS exported via the bootstrap surface + # above, but that is DEC C's own errno ABI, not the accessor musl- + # compiled consumer code references.) + # NAME FORM: the C symbol is `__errno_location` (two leading + # underscores; that is how GNU nm reports it in libc.a). LINK.EXE's EVAX + # symbol reader represents an external C symbol with ONE leading + # underscore prepended, uniformly for the musl DEFINITION and for every + # consumer REFERENCE -- so at the link/vector level the name is + # `___errno_location` (THREE underscores). A real port consumer that uses + # errno (gzread.c in zlib, and most real C) references `___errno_location` + # and fails %LINK-F-UNDEF without this export; the two-underscore C form + # does NOT match it. Defined in musl-alpha libc.a + # (src/errno/__errno_location.c), resolved by the strict whole-archive + # link below. + VEC="$VEC,__init_libc=PROCEDURE,__copy_tls=PROCEDURE,__init_tp=PROCEDURE,ovmx_get_libc=PROCEDURE,___errno_location=PROCEDURE" # stdin/stdout/stderr -- the C stdio stream FILE* objects, plain DATA names # the decc$ filter above cannot catch. The alpha cc1 does NOT decorate the diff --git a/tools/cross-alpha-vms/joint-e2e/build-joint-image.sh b/tools/cross-alpha-vms/joint-e2e/build-joint-image.sh index 29e1f50c4..4e599254f 100755 --- a/tools/cross-alpha-vms/joint-e2e/build-joint-image.sh +++ b/tools/cross-alpha-vms/joint-e2e/build-joint-image.sh @@ -135,9 +135,19 @@ echo "-- compiling joint_main.obj (cross cc1, -mpointer-size=64) --" "$ALPHA_CC" -mpointer-size=64 -g0 -c /joint/joint_main.c -o "$OUT/joint_main.obj" # ---- 6. the JOINT-E2E IMAGE: real crt0 + real main, --use the genuine -# alpha DECC$SHR, STRICT (no --allow-undefined; expect zero deferred) ---- -echo "== linking joint-e2e image (strict, expect zero deferred) ==" -"$WORK/LINK.EXE" --transfer __main --use "$WORK/DECC\$SHR.EXE" \ +# alpha DECC$SHR *and* LIBOTS$SHR, STRICT (no --allow-undefined; +# expect zero deferred) ---- +# LIBOTS$SHR.EXE is added to the canonical consumer link recipe alongside +# DECC$SHR.EXE (zlib-crtl-rungs). The alpha-dec-vms port compiler lowers every +# integer divide/remainder to an OTS$DIV_*/OTS$REM_* call (Alpha has no integer- +# divide instruction), and those universals live in the SEPARATE LIBOTS$ +# shareable -- DECC$SHR imports OTS$ for its OWN use but does NOT transitively +# re-export it, so a consumer that divides (zlib, and most real C) would defer +# OTS$DIV_UL/OTS$REM_UI against DECC$SHR alone. LINK binds only REFERENCED +# imports, so adding --use LIBOTS$ is inert for programs (like this joint_main) +# that emit no OTS$ call, and closes the gap for those that do. +"$WORK/LINK.EXE" --transfer __main \ + --use "$WORK/DECC\$SHR.EXE" --use "$WORK/libots/LIBOTS_SHR.EXE" \ -o "$OUT/joint_e2e.exe" "$OUT/crt0.obj" "$OUT/joint_main.obj" cp "$WORK/LINK.EXE" "$WORK/DECC\$SHR.EXE" "$WORK/libots/LIBOTS_SHR.EXE" "$OUT/" diff --git a/tools/cross-alpha-vms/musl-arch/arch/alpha-dec-vms/unixio.h b/tools/cross-alpha-vms/musl-arch/arch/alpha-dec-vms/unixio.h new file mode 100644 index 000000000..7cb8b48e5 --- /dev/null +++ b/tools/cross-alpha-vms/musl-arch/arch/alpha-dec-vms/unixio.h @@ -0,0 +1,59 @@ +/* + * unixio.h -- DEC C UNIX-I/O emulation declarations for the OVMX + * alpha-dec-vms musl port CRTL include surface (bead: zlib-crtl-rungs). + * + * WHAT THIS IS. On a genuine OpenVMS DEC C toolchain, is the + * CRTL header that declares the UNIX-style low-level I/O emulation entry + * points (open/close/read/write/lseek/... on RMS underneath). Portable C + * that targets VMS routinely does `#ifdef VMS #include ` (zlib's + * zconf.h is the canonical example) because on VMS these decls do NOT come + * from . The alpha-dec-vms GCC port predefines VMS/__VMS, so that + * `#ifdef VMS` fires -- correctly, it IS a VMS toolchain -- and the compile + * then needs on the include path. + * + * WHY IT LIVES HERE. The OVMX alpha-dec-vms port's C run-time is musl + * (whole-archived into DECC$SHR.EXE). musl already IMPLEMENTS every one of + * these UNIX-I/O entry points -- this header only DECLARES them (no bodies), + * so it is a faithful, minimal shim: it makes the VMS-only header name + * resolvable without adding or faking any functionality. It is scoped to the + * alpha-dec-vms arch overlay (a VMS-target-only header, exactly as on a real + * DEC C kit), never surfaced for a non-VMS target. + * + * Signatures track musl's own / declarations exactly, so a + * translation unit that includes BOTH and sees identical + * prototypes (no conflicting-declaration diagnostic). Types come from + * (size_t/ssize_t/off_t/mode_t), same as the real header. + * + * CLEAN-ROOM (Rule 8): declarations derived from the public DEC C RTL + * Reference Manual's surface + musl's own prototypes. No VSI/HPE + * header source was read or copied. + */ +#ifndef _OVMX_UNIXIO_H +#define _OVMX_UNIXIO_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int open(const char *, int, ...); +int close(int); +ssize_t read(int, void *, size_t); +ssize_t write(int, const void *, size_t); +off_t lseek(int, off_t, int); +int dup(int); +int dup2(int, int); +int isatty(int); +int unlink(const char *); +int access(const char *, int); +int ftruncate(int, off_t); +int fsync(int); +int chdir(const char *); +char *getcwd(char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _OVMX_UNIXIO_H */