1 #include <cstdio> 2 #include <cstdlib> 3 #include "unwind.h" 4 5 #define EXCEPTION_CLASS(a,b,c,d,e,f,g,h) \ 6 ((static_cast<uint64_t>(a) << 56) +\ 7 (static_cast<uint64_t>(b) << 48) +\ 8 (static_cast<uint64_t>(c) << 40) +\ 9 (static_cast<uint64_t>(d) << 32) +\ 10 (static_cast<uint64_t>(e) << 24) +\ 11 (static_cast<uint64_t>(f) << 16) +\ 12 (static_cast<uint64_t>(g) << 8) +\ 13 (static_cast<uint64_t>(h))) 14 15 // using ld --wrap=_Unwind_RaiseException hook feature 16 extern "C" _Unwind_Reason_Code __real__Unwind_RaiseException (_Unwind_Exception *e); 17 extern "C" _Unwind_Reason_Code __wrap__Unwind_RaiseException (_Unwind_Exception *e); 18 19 extern "C" _Unwind_Reason_Code __wrap__Unwind_RaiseException (_Unwind_Exception *e) 20 { 21 // clobber exception class forcing libcxx own exceptions to be treated 22 // as foreign exception within libcxx itself 23 e->exception_class = EXCEPTION_CLASS('F','O','R','E','I','G','N','\0'); 24 __real__Unwind_RaiseException(e); 25 } 26 27 _Unwind_Exception global_e; 28 29 enum test_status { 30 PENDING, PASSED, FAILED 31 }; 32 33 const char test_status_str[][8] = { 34 "PENDING", "PASSED", "FAILED" 35 }; 36 37 test_status test1_status = PENDING; 38 test_status test2_status = PENDING; 39 test_status test3_status = PENDING; 40 41 void test2_exception_cleanup(_Unwind_Reason_Code code, _Unwind_Exception *e) 42 { 43 fputs("(2) exception_cleanup called\n", stderr); 44 if (e != &global_e) { 45 fprintf(stderr, "(2) ERROR: unexpected ptr: expecting %p, got %p\n", &global_e, e); 46 test2_status = FAILED; 47 } 48 if (test2_status == PENDING) 49 test2_status = PASSED; 50 } 51 52 struct test3_exception 53 { 54 static int counter; 55 ~test3_exception() 56 { 57 counter++; 58 fputs("(3) exception dtor\n", stderr); 59 } 60 }; 61 int test3_exception::counter = 0; 62 63 int main() 64 { 65 /////////////////////////////////////////////////////////////// 66 fputs("(1) foreign exception, exception_cleanup=nullptr\n", stderr); 67 try 68 { 69 global_e.exception_class = 0; 70 global_e.exception_cleanup = 0; 71 __real__Unwind_RaiseException(&global_e); 72 } 73 catch (...) 74 { 75 } 76 test1_status = PASSED; 77 fputs("(1) PASS\n", stderr); 78 79 /////////////////////////////////////////////////////////////// 80 fputs("(2) foreign exception, exception_cleanup present\n", stderr); 81 try 82 { 83 global_e.exception_class = 0; 84 global_e.exception_cleanup = test2_exception_cleanup; 85 __real__Unwind_RaiseException(&global_e); 86 } 87 catch (...) 88 { 89 } 90 fprintf(stderr, "(2) %s\n", test_status_str[test2_status]); 91 92 /////////////////////////////////////////////////////////////// 93 fputs("(3) C++ exception in foreign environment\n", stderr); 94 int counter_expected; 95 try 96 { 97 // throw was rigged such that the runtime treats C++ exceptions 98 // as foreign ones 99 throw test3_exception(); 100 } 101 catch (test3_exception&) 102 { 103 fputs("(3) ERROR: wrong catch\n", stderr); 104 test3_status = FAILED; 105 } 106 catch (...) 107 { 108 fputs("(3) catch(...)\n", stderr); 109 counter_expected = test3_exception::counter + 1; 110 // one more dtor immediately after we leave catch 111 } 112 if (test3_status == PENDING && test3_exception::counter != counter_expected) { 113 fputs("(3) ERROR: exception dtor didn't run\n", stderr); 114 test3_status = FAILED; 115 } 116 if (test3_status == PENDING) 117 test3_status = PASSED; 118 fprintf(stderr, "(3) %s\n", test_status_str[test3_status]); 119 120 /////////////////////////////////////////////////////////////// 121 if (test1_status == PASSED && test2_status == PASSED && test3_status == PASSED) 122 return EXIT_SUCCESS; 123 else 124 return EXIT_FAILURE; 125 } 126