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
__wrap__Unwind_RaiseException(_Unwind_Exception * e)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
test2_exception_cleanup(_Unwind_Reason_Code code,_Unwind_Exception * e)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;
~test3_exceptiontest3_exception55 ~test3_exception()
56 {
57 counter++;
58 fputs("(3) exception dtor\n", stderr);
59 }
60 };
61 int test3_exception::counter = 0;
62
main()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