65816-llvm-mos/demos/unwindStubProbe.cpp
Scott Duensing da095402ec Updated
2026-06-02 23:17:57 -05:00

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;
}