D473 - Without an unwinder, a C++ throw is a named stop rather than a return
assumed - 2026-09-02 (user-directed bulk port, batch 3)
The C++ runtime a guest carries asks for a family of functions whose
whole purpose is to throw: _Xlength_error,
_Xout_of_range, _Xinvalid_argument,
_Xbad_alloc, _Xbad_function_call,
_Throw_C_error, _Throw_Cpp_error, plus
std::terminate, __cxa_pure_virtual and
_Unwind_Resume. All of them are declared
[[noreturn]], and orbistoun has no stack
unwinder.
Two possible answers, one of them honest
Returning is the tempting one, because it is what a stub does and it keeps the guest alive. It is also wrong in the specific way this project exists to avoid. The caller's code was generated on the promise that the call does not come back: the instruction after it is unreachable, no landing pad runs, and whatever half-built object provoked the throw is still half-built. The guest continues from a point its own compiler proved it could not reach. That is a silent wrong continuation, and the wall it eventually produces has no visible relationship to the throw that caused it.
Stopping cannot deliver the exception to a
catch the guest may have had, and this entry does not
pretend otherwise. What it does is say which exception was thrown, with
its message, and end the run there. A guest that would have caught it is
now reported as stopping at a throw it could have handled - a legible
gap, and one that names the next piece of work. The alternative is an
illegible one.
So every never-returning entry point goes through one
stop_for in orbistoun-libc/src/cxx.rs, which
reports to stderr and the kernel log and then stops. One place, so none
of them can quietly grow a return.
What this is not
Not a claim that exceptions are unimplementable
here, and not a reason to avoid them later. A real unwinder
would make all of these ordinary, and this decision is what should be
revisited first when one exists - at which point the throw helpers
become thin wrappers over __cxa_throw and this file
shrinks. It is recorded as assumed rather than
measured because no guest has yet been observed reaching
one of these: the reasoning is from the ABI's own
[[noreturn]], not from a run.
Not applied to __cxa_throw and its
family. __cxa_allocate_exception,
__cxa_begin_catch, __cxa_end_catch,
__cxa_rethrow, __gxx_personality_v0 and the
_ZTV* vtables are left unimplemented
rather than stopped, because they are not all [[noreturn]]
and pretending otherwise would be inventing a contract. An unimplemented
function is already loud.
Reference: the Itanium C++ ABI (https://itanium-cxx-abi.github.io/cxx-abi/abi.html)
for _Unwind_Resume and the __cxa_* names;
ISO/IEC 14882 for std::terminate and the allocation
functions. See worklog 304.