Concerning just the level 1 base [ABI-EH]. I haven't consulted the [psABI] directly, and perhaps it offers clarifications when interpreted together with [ABI-EH], but [ABI-EH] should ideally be sufficient on its own to use the unwinding runtime.
§1.2 Data Structures; Exception Header; exception_cleanup
_URC_FOREIGN_EXCEPTION_CAUGHT = 1: This indicates that a different runtime caught this exception. Nested foreign exceptions, or rethrowing a foreign exception, result in undefined behaviour. [source; emphasis mine]
§1.6.4 Rules for Correct Inter-Language Operation
The behavior is undefined in the following cases:
A __foreign_exception is active at the same time as another exception (either there is a nested exception while catching the foreign exception, or the foreign exception was itself nested). [source; emphasis mine]
This is unfortunately insufficiently clear at defining when an exception is considered nested, and at which operation behavior becomes undefined. There are at least three different scenarios:
A personality routine is running for an exception of class $A$ and an exception of class $B$ is raised.
A _URC_CONTINUE_UNWIND frame landing pad is running for an exception of class $A$ and an exception of class $B$ is raised.
A _URC_HANDLER_FOUND frame landing pad is running for an exception of class $A$ and an exception of class $B$ is raised.
All three cases have the additional variable of if any intervening frame would register a handler for the newly raised exception, as well as if that handler is native or foreign to exception class $B$.
Case (1) of a personality routine raising an exception itself is likely problematic for other reasons.
In C++, case (2) results from a throw within a destructor where that destructor is run by a cleanup landing pad for the prior exception, and case (3) results from a throw within a catch clause where $A$ is foreign to the C++ runtime (and thus not yet handled).
In Rust, case (2) results from a panic! within a Drop::drop run by a cleanup landing pad where $A$ is not a Rust panic (that case would abort). Case (3) is impossible by construction; the catch equivalent does not run user code inside the handler landing pad.
I have one concrete ask for the specification: directly define when _Unwind_RaiseException is sound to call. By my reading, I think the answer is that _Unwind_RaiseException causes undefined behavior when
some exception $e$ of class $E$ has previously been raised; where
$e$ has not been passed to e->exception_cleanup or otherwise cleaned up by the defining runtime; and
the newly raised exception is not of class $E$.
_Unwind_RaiseException is sound to call when
The _Unwind_Exception fields have been initialized as described; and
the above is not the case.
This operationally defines the nesting of foreign exceptions which produces undefined behavior in a way such that the undefined behavior can be prevented.
Concerning just the level 1 base [ABI-EH]. I haven't consulted the [psABI] directly, and perhaps it offers clarifications when interpreted together with [ABI-EH], but [ABI-EH] should ideally be sufficient on its own to use the unwinding runtime.
§1.2 Data Structures; Exception Header;
exception_cleanup
§1.6.4 Rules for Correct Inter-Language Operation
This is unfortunately insufficiently clear at defining when an exception is considered nested, and at which operation behavior becomes undefined. There are at least three different scenarios:
_URC_CONTINUE_UNWIND
frame landing pad is running for an exception of class $A$ and an exception of class $B$ is raised._URC_HANDLER_FOUND
frame landing pad is running for an exception of class $A$ and an exception of class $B$ is raised.All three cases have the additional variable of if any intervening frame would register a handler for the newly raised exception, as well as if that handler is native or foreign to exception class $B$.
Case (1) of a personality routine raising an exception itself is likely problematic for other reasons. In C++, case (2) results from a
throw
within a destructor where that destructor is run by a cleanup landing pad for the prior exception, and case (3) results from athrow
within acatch
clause where $A$ is foreign to the C++ runtime (and thus not yet handled). In Rust, case (2) results from apanic!
within aDrop::drop
run by a cleanup landing pad where $A$ is not a Rust panic (that case would abort). Case (3) is impossible by construction; thecatch
equivalent does not run user code inside the handler landing pad.I have one concrete ask for the specification: directly define when
_Unwind_RaiseException
is sound to call. By my reading, I think the answer is that_Unwind_RaiseException
causes undefined behavior whene->exception_cleanup
or otherwise cleaned up by the defining runtime; and_Unwind_RaiseException
is sound to call when_Unwind_Exception
fields have been initialized as described; andThis operationally defines the nesting of foreign exceptions which produces undefined behavior in a way such that the undefined behavior can be prevented.