source: doc/working/exception/impl/except.c @ e4e9173

ADTaaron-thesisarm-ehast-experimentalcleanup-dtorsdeferred_resndemanglerenumforall-pointer-decayjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprnew-envno_listpersistent-indexerpthread-emulationqualifiedEnumresolv-newwith_gc
Last change on this file since e4e9173 was 6b72040, checked in by Andrew Beach <ajbeach@…>, 7 years ago

The try exception handler function is now generic.

  • Property mode set to 100644
File size: 8.0 KB
Line 
1#include <stdlib.h>
2#include <stdio.h>
3#include <unwind.h>
4
5#include "lsda.h"
6
7// This macro should be the only thing that needs to change across machines.
8// struct _Unwind_Context * -> _Unwind_Reason_Code(*)()
9#define MATCHER_FROM_CONTEXT(ptr_to_context) \
10        (*(_Unwind_Reason_Code(**)())(_Unwind_GetCFA(ptr_to_context) + 8))
11
12
13//Global which defines the current exception
14//Currently an int just to make matching easier
15int this_exception;
16
17//This is our personality routine
18//For every stack frame anotated with ".cfi_personality 0x3,__gcfa_personality_v0"
19//This function will be called twice when unwinding
20//Once in the search phased and once in the cleanup phase
21_Unwind_Reason_Code __gcfa_personality_v0 (
22                     int version, _Unwind_Action actions, unsigned long long exceptionClass,
23                     struct _Unwind_Exception* unwind_exception, struct _Unwind_Context* context)
24{
25        printf("CFA: 0x%lx\n", _Unwind_GetCFA(context));
26
27        //DEBUG
28        printf("Personality function (%d, %x, %llu, %p, %p):", version, actions, exceptionClass, unwind_exception, context);
29
30        //If we've reached the end of the stack then there is nothing much we can do...
31        if( actions & _UA_END_OF_STACK ) return _URC_END_OF_STACK;
32       
33        //DEBUG
34        if (actions & _UA_SEARCH_PHASE) {
35                printf(" lookup phase");
36        } 
37        //DEBUG
38        else if (actions & _UA_CLEANUP_PHASE) {
39                printf(" cleanup phase");
40        }
41        //Just in case, probably can't actually happen
42        else {
43                printf(" error\n");
44                return _URC_FATAL_PHASE1_ERROR;
45        }
46       
47        //Get a pointer to the language specific data from which we will read what we need
48        const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData( context );
49
50        if( !lsd ) {    //Nothing to do, keep unwinding
51                printf(" no LSD");
52                goto UNWIND;
53        }
54
55        //Get the instuction pointer and a reading pointer into the exception table
56        lsda_header_info lsd_info;
57        const unsigned char * cur_ptr = parse_lsda_header( context, lsd, &lsd_info);
58        _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context );
59
60        //Linearly search the table for stuff to do
61        while( cur_ptr < lsd_info.action_table ) {
62                _Unwind_Ptr callsite_start;
63                _Unwind_Ptr callsite_len;
64                _Unwind_Ptr callsite_landing_pad;
65                _uleb128_t  callsite_action;
66
67                //Decode the common stuff we have in here
68                cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_start);
69                cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_len);
70                cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_landing_pad);
71                cur_ptr = read_uleb128 (cur_ptr, &callsite_action);
72
73                //Have we reach the correct frame info yet?
74                if( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {
75                        //DEBUG BEGIN
76                        void * ls = (void*)lsd_info.Start;
77                        void * cs = (void*)callsite_start;
78                        void * cl = (void*)callsite_len;
79                        void * bp = (void*)lsd_info.Start + callsite_start;
80                        void * ep = (void*)lsd_info.Start + callsite_start + callsite_len;
81                        void * ip = (void*)instruction_ptr;
82                        printf("\nfound %p - %p (%p, %p, %p), looking for %p\n", bp, ep, ls, cs, cl, ip);
83                        //DEBUG END
84                        continue;
85                }
86               
87                //Have we gone too far
88                if( lsd_info.Start + callsite_start > instruction_ptr ) {
89                        printf(" gone too far");
90                        break;
91                }
92
93                //Something to do?
94                if( callsite_landing_pad ) {
95                        //Which phase are we in
96                        if (actions & _UA_SEARCH_PHASE) {
97                                //Search phase, this means we probably found a potential handler and must check if it is a match
98
99                                //If we have arbitrarily decided that 0 means nothing to do and 1 means there is a potential handler
100                                //This doesn't seem to conflict the gcc default behavior
101                                if (callsite_action != 0) {
102                                        //Now we want to run some code to see if the handler matches
103                                        //This is the tricky part where we want to the power to run arbitrary code
104                                        //However, generating a new exception table entry and try routine every time
105                                        //is way more expansive than we might like
106                                        //The information we have is :
107                                        //  - The GR (Series of registers)
108                                        //    GR1=GP Global Pointer of frame ref by context
109                                        //  - The instruction pointer
110                                        //  - The instruction pointer info (???)
111                                        //  - The CFA (Canonical Frame Address)
112                                        //  - The BSP (Probably the base stack pointer)
113
114
115                                        //The current apprach uses one exception table entry per try block
116                                        _uleb128_t imatcher;
117                                        //Get the relative offset to the
118                                        cur_ptr = read_uleb128 (cur_ptr, &imatcher);
119
120                                        //Get a function pointer from the relative offset and call it
121                                        // _Unwind_Reason_Code (*matcher)() = (_Unwind_Reason_Code (*)())lsd_info.LPStart + imatcher;                                   
122
123                                        _Unwind_Reason_Code (*matcher)() =
124                                                MATCHER_FROM_CONTEXT(context);
125                                        _Unwind_Reason_Code ret = matcher();
126
127                                        //Based on the return value, check if we matched the exception
128                                        if( ret == _URC_HANDLER_FOUND) printf(" handler found\n");
129                                        else printf(" no handler\n");
130                                        return ret;
131                                }
132
133                                //This is only a cleanup handler, ignore it
134                                printf(" no action");
135                        } 
136                        else if (actions & _UA_CLEANUP_PHASE) {
137
138                                if( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){
139                                        //If this is a potential exception handler
140                                        //but not the one that matched the exception in the seach phase,
141                                        //just ignore it
142                                        goto UNWIND;
143                                }
144
145                                //We need to run some clean-up or a handler
146                                //These statment do the right thing but I don't know any specifics at all
147                                _Unwind_SetGR( context, __builtin_eh_return_data_regno(0), (_Unwind_Ptr) unwind_exception );
148                                _Unwind_SetGR( context, __builtin_eh_return_data_regno(1), 0 );
149
150                                //I assume this sets the instruction pointer to the adress of the landing pad
151                                //It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT
152                                _Unwind_SetIP( context, lsd_info.LPStart + callsite_landing_pad );
153
154                                //DEBUG
155                                printf(" action\n");
156
157                                //Return have some action to run
158                                return _URC_INSTALL_CONTEXT;
159                        }
160                }
161
162                //Nothing to do, move along
163                printf(" no landing pad");
164        }
165        //No handling found
166        printf(" table end reached\n");
167
168        //DEBUG
169        UNWIND:
170        printf(" unwind\n");
171
172        //Keep unwinding the stack
173        return _URC_CONTINUE_UNWIND;
174}
175
176//We need a piece of storage to raise the exception
177struct _Unwind_Exception this_exception_storage;
178
179//Function needed by force unwind
180//It basically says to unwind the whole stack and then exit when we reach the end of the stack
181static _Unwind_Reason_Code _Stop_Fn(   
182        int version, 
183        _Unwind_Action actions, 
184        _Unwind_Exception_Class exceptionClass, 
185        struct _Unwind_Exception * unwind_exception, 
186        struct _Unwind_Context * context, 
187        void * some_param
188) {
189        if( actions & _UA_END_OF_STACK  ) exit(1);
190        if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON;
191
192        return _URC_FATAL_PHASE2_ERROR;
193}
194
195//Example throw routine
196void throw( int val ) {
197        //Store the current exception
198        this_exception = val;
199
200        //DEBUG
201        printf("Throwing exception %d\n", this_exception);
202
203        //Call stdlibc to raise the exception
204        _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage );
205
206        //If we reach here it means something happened
207        //For resumption to work we need to find a way to return back to here
208        //Most of them will probably boil down to setting a global flag and making the phase 1 either stop or fail.
209        //Causing an error on purpose may help avoiding unnecessary work but it might have some weird side effects.
210        //If we just pretend no handler was found that would work but may be expensive for no reason since we will always
211        //search the whole stack
212
213        if( ret == _URC_END_OF_STACK ) {
214                //No proper handler was found
215                //This can be handled in several way
216                //C++ calls std::terminate
217                //Here we force unwind the stack, basically raising a cancellation
218                printf("Uncaught exception %p\n", &this_exception_storage);
219               
220                ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 );
221                printf("UNWIND ERROR %d after force unwind\n", ret);
222                abort();
223        }
224
225        //We did not simply reach the end of the stack without finding a handler,
226        //Something wen't wrong
227        printf("UNWIND ERROR %d after raise exception\n", ret);
228        abort();
229}
Note: See TracBrowser for help on using the repository browser.