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 */