Skip to content

vms-599: DEC C exit()/__posix_exit() record the VMS completion status (oracle-grounded) - #1839

Merged
baron-3dl merged 5 commits into
mainfrom
work/vms-599-decc-exit
Oct 21, 2026
Merged

baron-3dl merged 5 commits into
mainfrom
work/vms-599-decc-exit

Conversation

@baron-3dl

Copy link
Copy Markdown
Contributor

Wall (blocks #1810). The GCC port's `ar` ends with `exit(system(...))`. On VMS, `exit(1)` is a success. OVMX's DECC$SHR `exit()` was musl's: it ran atexit, flushed stdio and `exit_group()`ed a POSIX code, with nothing recorded. DCL therefore reported `%DCL-E-ABORT, image SYS$SYSTEM:AR.EXE exited with error status %X00000001` even though LIBRARY had built the archive.

Oracle. Measured today on OpenVMS Alpha V8.4 with MACRO-32 probes calling the CRTL directly. The capture is in `tests/lab/captures/decc-exit-alpha-20261021/`.

  • `DECC$EXIT`: 0 becomes `%X1`. Every other value passes through unchanged: 1, 2, 3, 4 and `%X1000002C`.
  • `DECC$__POSIX_EXIT`: works on n&255. 0 becomes `%X1` and 1 becomes `%X1035A00A`. Any other n becomes `%X0035A001+(n<<3)`: 2 → `%X35A011`, 255 → `%X35A7F9`, 256 → `%X1`.

Fix.

  • `decc$exit` is rebound to `ovmx_crtl_exit`, and `decc$__posix_exit` is added at the tail. Both record the status, then run musl's exit.
  • The CRTL syscall hook ends the image with `SYS$EXIT(status)` at the final `exit_group`, which is after the handlers and the flush.
  • `<stdlib.h>`: `_POSIX_EXIT` now maps `exit` to `__posix_exit`. GCC's `xm-vms.h` defines it, so cc1 and xgcc keep POSIX semantics. Otherwise `EXIT_FAILURE` is 2, per the CRTL doc ("2 or EXIT_FAILURE") and the measured `%X2`.

Test. The crtl-fd gate now runs 11 exit and `__posix_exit` cases as foreign commands. Each `$STATUS` must equal the oracle value, and an unflushed line must appear. Three new selftest negatives check that the assertion fails on musl's exit, a raw `__posix_exit`, and a skipped flush. All selftests passed locally.

Not in this PR. `decc$_exit` is unchanged. Neither entry point is added to `DECC$SHR_EV56.vec` for natively-linked images.

🤖 Generated with Claude Code

…atus

OVMX's DECC$SHR exit() was musl's. It ran the atexit handlers, flushed stdio
and exit_group()ed a POSIX code, but nothing recorded the status. DCL then
read the image's exit(1), which is a success on VMS, as "%DCL-E-ABORT ...
exited with error status %X00000001". That was the wall on #1810: the GCC
port's ar ends with exit(system(...)).

Measured on OpenVMS Alpha V8.4 with MACRO-32 probes that call the C RTL
directly (tests/lab/captures/decc-exit-alpha-20261021):
- DECC$EXIT maps 0 to %X00000001 and passes every other value through.
- DECC$__POSIX_EXIT keeps n&255. It maps 0 to %X00000001, 1 to %X1035A00A,
  and any other n to %X0035A001+(n<<3).

What changed:
- crtl_process.c: decc$exit is rebound to ovmx_crtl_exit, and
  decc$__posix_exit (ovmx_crtl_posix_exit) is a new tail universal. Both
  record the status and then run musl's exit.
- crtl_rms_fd.c: the system-call hook ends the image with SYS$EXIT of that
  status at the final exit_group, after the handlers and the stdio flush.
- build-musl.sh: <stdlib.h> follows DEC C. _POSIX_EXIT makes exit
  __posix_exit, which is what the GCC port's xm-vms.h asks for. Otherwise
  EXIT_FAILURE is 2.

Test: the crtl-fd gate runs the image as a foreign command, through 11
exit and __posix_exit cases, and checks each $STATUS against the oracle. It
also checks that an unflushed line is printed. Three new selftest negative
controls cover musl's exit, a raw __posix_exit and a skipped flush.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@baron-3dl
baron-3dl enabled auto-merge October 21, 2026 17:38
baron-3dl and others added 4 commits October 21, 2026 17:40
…int)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ec-vms

The crtl-fd gate's new check found this on the qemu-system-alpha boot: a line
printed without a newline and then exit()ed never reached the console.

Generic musl exit.c reaches __funcs_on_exit and __stdio_exit through local
weak aliases of a no-op. The alpha-dec-vms back end binds same-TU calls to
those aliases section-relatively (the vms-032 lite_malloc case), so the
strong definitions in atexit.c and __stdio_exit.c never ran.

An arch override (src/exit/alpha-dec-vms/exit.c) now calls both by name.
A build-time gate in build-musl.sh fails if exit.o defines either symbol or
does not reference it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(musl-internal headers), like lite_malloc.c and _Fork.c

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ec-vms

The exit() override ran __stdio_exit, but the crtl-fd gate's unflushed line was
still lost. musl's __stdio_exit.c reaches the three standard FILEs through weak
aliases of a local NULL. The back end self-binds those aliases, so the flush
saw no FILE. This adds an arch override that names __stdin_used,
__stdout_used and __stderr_used, and extends build-musl.sh's nm gate to check
it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@baron-3dl
baron-3dl added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit f7756c1 Oct 21, 2026
175 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant