75 lines
2.6 KiB
C++
75 lines
2.6 KiB
C++
// unwindStubProbe.cpp — Phase 5.1 smoke for the `_Unwind_*` stub.
|
|
//
|
|
// Exercises the Itanium `_Unwind_*` surface from libunwindStub.o. These
|
|
// entry points are what third-party C++ libraries reference from their
|
|
// own exception-handling paths (abseil, fmt, libcxx itself); confirming
|
|
// they link AND that the cleanup callback fires at runtime proves the
|
|
// stub is functional end-to-end.
|
|
//
|
|
// Why no `throw` / `catch` in this runtime probe: SJLJ-prepared C++
|
|
// exception code is documented to crash MAME's apple2gs CPU emulation
|
|
// intermittently (smokeTest.sh:4906-4912 notes the same and runs only a
|
|
// link check there). This probe stays on the pure-C surface so we get
|
|
// a green runtime marker. The companion link check
|
|
// (scripts/smokeTest.sh) already validates that clang++ + libunwindStub
|
|
// produces a linkable C++ binary that uses throw / catch.
|
|
//
|
|
// Markers (16-bit, bank-2):
|
|
// $025000 = 0xC0DE reached main()
|
|
// $025002 = 0xBEEF _Unwind_DeleteException cleanup callback fired
|
|
// $025004 = 0x900D end of main()
|
|
|
|
extern "C" {
|
|
#include <stdint.h>
|
|
}
|
|
|
|
// Itanium ABI shapes — duplicated locally so the probe is
|
|
// self-contained (no <unwind.h> shim in our tree). Layout must match
|
|
// libunwindStub.c's _Unwind_Exception.
|
|
typedef enum {
|
|
URC_NO_REASON = 0,
|
|
URC_FOREIGN_EXCEPTION_CAUGHT = 1
|
|
} UnwindReasonE;
|
|
|
|
struct _Unwind_Exception;
|
|
typedef void (*UnwindExceptionCleanupFn)(UnwindReasonE, _Unwind_Exception *);
|
|
|
|
struct _Unwind_Exception {
|
|
uint64_t exception_class;
|
|
UnwindExceptionCleanupFn exception_cleanup;
|
|
uintptr_t private_1;
|
|
uintptr_t private_2;
|
|
};
|
|
|
|
extern "C" void _Unwind_DeleteException(_Unwind_Exception *exc);
|
|
|
|
static volatile uint16_t gCleanupFired = 0;
|
|
|
|
static void onCleanup(UnwindReasonE reason, _Unwind_Exception *exc) {
|
|
(void)reason;
|
|
(void)exc;
|
|
gCleanupFired = 0xBEEF;
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
*(volatile uint16_t *)0x025000UL = 0xC0DE;
|
|
|
|
// Stack-allocate a _Unwind_Exception, register a cleanup callback,
|
|
// hand it to _Unwind_DeleteException, and confirm the callback
|
|
// fired. This is the surface third-party code reaches when it
|
|
// owns the exception storage itself rather than going through
|
|
// __cxa_throw.
|
|
_Unwind_Exception localExc;
|
|
localExc.exception_class = 0;
|
|
localExc.exception_cleanup = &onCleanup;
|
|
localExc.private_1 = 0;
|
|
localExc.private_2 = 0;
|
|
_Unwind_DeleteException(&localExc);
|
|
*(volatile uint16_t *)0x025002UL = gCleanupFired;
|
|
|
|
*(volatile uint16_t *)0x025004UL = 0x900D;
|
|
|
|
// GNO commands return to gsh after main().
|
|
return 0;
|
|
}
|