Usermode/libc++ - Exception handling implemented (partially)
[tpg/acess2.git] / Usermode / Libraries / libc++.so_src / gxx_personality.cc
1 /*
2  * Acess2 C++ Support Library
3  * - By John Hodge (thePowersGang)
4  *
5  * gxx_personality.cc
6  * - Implementation of GNU C++ Exception handling "Personality"
7  */
8 #include <typeinfo>
9 #include <cstdint>      // [u]intN_t
10 #include <cstdlib>      // abort
11 #include <cstring>      // memcmp
12 #include "exception_handling_acxx.h"
13 #include "exception_handling_cxxabi.h"
14 #include <acess/sys.h>
15 #include <cassert>
16 #include <cxxabi.h>     // __dynamic_cast
17
18 // === PROTOTYPES ===
19 extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass,
20                 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context);
21 static int get_frame_action(const sLSDA_Header &header, _Unwind_Context *context, const std::type_info *throw_type,
22         uint64_t &landingpad, int64_t &switch_value);
23 static bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index);
24 static bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index);
25 static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header);
26 static size_t _get_encoded_size(int encoding);
27 static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context);
28 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base);
29 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding);
30 template <typename T> static T read(const void *&ptr);
31 static uint64_t _read_leb128u(const void *&ptr);
32 static int64_t _read_leb128s(const void *&ptr);
33
34 // === CODE ===
35 extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass,
36                 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
37 {
38         //::_SysDebug("__gxx_personality_v0(%i, 0x%x, 0x%llx, ...)", version, actions, exceptionClass);
39         //::_SysDebug("__gxx_personality_v0(..., %p, %p)", exceptionObject, context);
40         
41         //if( exceptionObject ) {
42         //      ::_SysDebug("exception_object: Class 0x%llx", exceptionObject->exception_class);
43         //}
44         //if( context )
45         //{
46         //      ::_SysDebug("context: IP 0x%llx", _Unwind_GetIP(context));
47         //      ::_SysDebug("context: RegionStart 0x%llx", _Unwind_GetRegionStart(context));
48         //      ::_SysDebug("context: LangSpecData 0x%llx", _Unwind_GetLanguageSpecificData(context));
49         //}
50         
51         if( version != 1 ) {
52                 ::_SysDebug("%s: Unexpected version %i != exp 1", __func__, version);
53                 return _URC_FATAL_PHASE1_ERROR;
54         }
55         
56         const void *lsda_ptr = (const void*)_Unwind_GetLanguageSpecificData(context);
57         struct sLSDA_Header     header;
58         read_lsda_header(lsda_ptr, context, &header);
59
60         const void *exception_object = exceptionObject + 1;
61         const __cxa_exception *exception_info = static_cast<const __cxa_exception*>(exception_object) - 1;
62         std::type_info *throw_type = (
63                 memcmp(&exceptionClass,EXCEPTION_CLASS_ACESS,8) == 0 ? exception_info->exceptionType : NULL
64                 );
65
66         uint64_t        landingpad = 0;
67          int64_t        switch_value = 0;
68         int frame_action = get_frame_action(header, context, throw_type,  landingpad, switch_value);
69
70         if( (actions & _UA_SEARCH_PHASE) && (actions & _UA_CLEANUP_PHASE) )
71         {
72                 _SysDebug("__gxx_personality_v0: ERROR Both _UA_SEARCH_PHASE and _UA_CLEANUP_PHASE set");
73                 abort();
74         }       
75
76         // Search
77         if( actions & _UA_SEARCH_PHASE )
78         {
79                 // If a handler was found
80                 if( frame_action == 2 ) {
81                         // - return _URC_HANDLER_FOUND
82                         _SysDebug("SEARCH: 0x%llx Handler %p(%i)",
83                                 _Unwind_GetIP(context), landingpad, switch_value);
84                         return _URC_HANDLER_FOUND;
85                 }
86                 // - If no handler (either nothing, or cleanups), return _URC_CONTINUE_UNWIND
87                 _SysDebug("SEARCH: 0x%llx no handler %p(%i)",
88                         _Unwind_GetIP(context), landingpad, switch_value);
89                 return _URC_CONTINUE_UNWIND;
90         }
91
92         // Cleanup      
93         if( actions & _UA_CLEANUP_PHASE )
94         {
95                 // No action, continue
96                 if( frame_action == 0 ) {
97                         return _URC_CONTINUE_UNWIND;
98                 }
99                 assert(landingpad);
100         
101                 if( actions & _UA_FORCE_UNWIND )
102                 {
103                         // Unwind forced, override switch_value to 0
104                         // - Disables any attempt to handle exception
105                         switch_value = 0;
106                 }
107         
108                 _SysDebug("Install context IP=0x%x, R%i=%p/R%i=%i",
109                         (uintptr_t)landingpad,
110                         __builtin_eh_return_data_regno(0), exceptionObject,
111                         __builtin_eh_return_data_regno(1), switch_value
112                         );
113                 _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject);
114                 _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), switch_value);
115                 _Unwind_SetIP(context, landingpad);
116                 return _URC_INSTALL_CONTEXT;
117         }
118         
119         _SysDebug("__gxx_personality_v0: Neither _UA_SEARCH_PHASE or _UA_CLEANUP_PHASE set");
120         return _URC_FATAL_PHASE1_ERROR;
121 }
122
123 int get_frame_action(const sLSDA_Header &header, _Unwind_Context *context, const std::type_info* throw_type,
124         uint64_t &landingpad, int64_t &switch_value)
125 {
126         uint64_t ip = _Unwind_GetIP(context) - _Unwind_GetRegionStart(context);
127         _SysDebug("IP = 0x%llx + 0x%llx", _Unwind_GetRegionStart(context), ip);
128         // Check if there is a handler for this exception in this frame
129         // - Search call site table for this return address (corresponds to a try block)
130         uintptr_t       cs_ldgpad;
131         uint64_t        cs_action;
132         const void *lsda_ptr = header.CallSiteTable;
133         while( lsda_ptr < header.ActionTable )
134         {
135                 uintptr_t cs_start  = _read_encoded(lsda_ptr, context, header.CallSiteEncoding);
136                 uintptr_t cs_len    = _read_encoded(lsda_ptr, context, header.CallSiteEncoding);
137                 cs_ldgpad = _read_encoded(lsda_ptr, context, header.CallSiteEncoding);
138                 cs_action = _read_leb128u(lsda_ptr);
139                 
140                 // If IP falls below this entry, it's not on the list
141                 if( ip <= cs_start ) {
142                         lsda_ptr = header.ActionTable;
143                         break ;
144                 }
145                 if( ip > cs_start + cs_len )
146                         continue;
147                 
148                 break;
149         }
150         if( lsda_ptr > header.ActionTable ) {
151                 _SysDebug("__gxx_personality_v0: Malformed Call Site Table, reading overran the end (%p>%p)",
152                         lsda_ptr, header.ActionTable);
153         }
154         if( lsda_ptr >= header.ActionTable ) {
155                 // No match!
156                 _SysDebug("__gxx_personality_v0: No entry for IP 0x%x", ip);
157                 return 0;
158         }
159         
160         // Found it
161         if( cs_ldgpad == 0 ) {
162                 _SysDebug("No landingpad, hence no action");
163                 if( cs_action != 0 ) {
164                         _SysDebug("%s: NOTICE cs_ldgpad==0 but cs_action(0x%llx)!=0",
165                                 __func__, cs_action);
166                 }
167                 return 0;
168         }
169         else if( cs_action == 0 ) {
170                 _SysDebug("No action, cleanups only");
171                 switch_value = 0;
172                 landingpad = header.LPStart + cs_ldgpad;
173                 return 1;       // 1 = cleanup only
174         }
175         else {
176                 switch_value = 0;
177                 landingpad = header.LPStart + cs_ldgpad;
178                 // catch() handler
179                 bool    has_cleanups = false;   // set to true to override return value
180                 const void *action_list = (const char*)header.ActionTable + (cs_action-1);
181                 for(;;) // broken by 0 length entry
182                 {
183                         leb128s_t filter = _read_leb128s(action_list);
184                         leb128s_t disp = _read_leb128s(action_list);
185                         _SysDebug("filter=%lli,disp=%lli", filter, disp);
186                         if( filter == 0 )
187                         {
188                                 // Cleanup
189                                 has_cleanups = true;
190                         }
191                         else if( filter < 0 )
192                         {
193                                 // Exception specifcation
194                                 if( !exception_matches_list(throw_type, header, -filter) )
195                                 {
196                                         switch_value = filter;
197                                         return 2;
198                                 }
199                         }
200                         else
201                         {
202                                 // Catch
203                                 if( exception_matches_single(throw_type, header, filter) )
204                                 {
205                                         switch_value = filter;
206                                         return 2;
207                                 }
208                         }
209                         
210                         if( disp == 0 )
211                                 break;
212                         action_list = (const char*)action_list + disp-1;
213                 }
214                 
215                 // If a cleanup request was seen, tell the caller that we want to handle it
216                 if( has_cleanups ) {
217                         return 1;       // 1 = cleanup only
218                 }
219                 // Else, there's nothing here to handle
220                 return 0;
221         }
222 }
223
224 const ::std::type_info *get_type_info(const struct sLSDA_Header &header, int type_index)
225 {
226         size_t  encoded_size = _get_encoded_size(header.TTEncoding);
227         assert(encoded_size > 0);
228         const void *ptr = (const void*)(header.TTBase - encoded_size * type_index);
229         assert( header.TTBase );
230         assert( ptr > header.ActionTable );
231         
232         uintptr_t type_ptr = _read_encoded(ptr, NULL, header.TTEncoding, header.TypePtrBase);
233         _SysDebug("typeinfo_ptr = %p", type_ptr);
234         return reinterpret_cast< ::std::type_info* >(type_ptr);
235 }
236
237 const ::std::type_info *get_exception_type(const void *exception_object)
238 {
239         if( !exception_object )
240                 return NULL;
241         const struct _Unwind_Exception  *u_execept = (const struct _Unwind_Exception*)exception_object;
242         const struct __cxa_exception *except = (const struct __cxa_exception*)(u_execept + 1) - 1;
243         if( memcmp(&except->unwindHeader.exception_class, EXCEPTION_CLASS_ACESS, 8) != 0 )
244                 return NULL;
245         
246         return except->exceptionType;
247 }
248
249 bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index)
250 {
251         const ::std::type_info *catch_type = get_type_info(header, type_index);
252
253         if( !catch_type )
254         {
255                 _SysDebug("catch(...)");
256                 return true;
257         }
258         else if( !throw_type )
259         {
260                 _SysDebug("threw UNK");
261                 return false;
262         }
263         else
264         {
265                 _SysDebug("catch(%s), throw %s", catch_type->name(), throw_type->name());
266                 size_t ofs = 0;
267                 if( !catch_type->__is_child(*throw_type, ofs) ) {
268                         _SysDebug("> No match");
269                         return false;
270                 }
271                 
272                 return true;
273         }
274 }
275 bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index)
276 {
277         _SysDebug("TODO: exception_matches_list %i", list_index);
278         (void)throw_type;
279         (void)header;
280         return true;
281 }
282
283 template <typename T> T read(const void *&ptr)
284 {
285         T rv = *reinterpret_cast<const T*>(ptr);
286         ptr = (const char*)ptr + sizeof(T);
287         return rv;
288 }
289
290 static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header)
291 {
292         header->Base = ptr;
293         
294         uint8_t start_encoding = read<uint8_t>(ptr);
295         if( start_encoding == DW_EH_PE_omit )
296                 header->LPStart = _Unwind_GetRegionStart(context);
297         else
298                 header->LPStart = _read_encoded(ptr, context, start_encoding);
299         
300         header->TTEncoding = read<uint8_t>(ptr);
301         if( header->TTEncoding == DW_EH_PE_omit )
302                 header->TTBase = 0;
303         else {
304                 ptrdiff_t       tt_ofs = _read_leb128u(ptr);
305                 header->TTBase = (uintptr_t)ptr + tt_ofs;
306         }
307         header->TypePtrBase = _get_base(header->TTEncoding, context);
308
309         header->CallSiteEncoding = read<uint8_t>(ptr);
310         uint64_t call_site_size = _read_leb128u(ptr);
311         header->CallSiteTable = ptr;
312         header->ActionTable = (const void*)( (uintptr_t)ptr + call_site_size );
313         
314         #if 0
315         ::_SysDebug("LSDA header:");
316         ::_SysDebug("->LPStart = 0x%lx", header->LPStart);
317         ::_SysDebug("->TTEncoding = 0x%02x", header->TTEncoding);
318         ::_SysDebug("->TTBase = 0x%lx", header->TTBase);
319         ::_SysDebug("->CallSiteEncoding = 0x%02x", header->CallSiteEncoding);
320         ::_SysDebug("->CallSiteTable = %p", header->CallSiteTable);
321         ::_SysDebug("->ActionTable = %p", header->ActionTable);
322         #endif
323 }
324
325 static size_t _get_encoded_size(int encoding)
326 {
327         switch(encoding & DW_EH_PE_fmtmask)
328         {
329         case DW_EH_PE_absptr:   // absolute
330                 return sizeof(void*);
331         default:
332                 _SysDebug("_get_encoded_size: Unknown encoding 0x%02x", encoding);
333                 return 0;
334         }
335 }
336
337 /*
338  * \brief Read a DWARF encoded pointer from the stream
339  */
340 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base)
341 {
342         (void)context;
343         if( encoding == DW_EH_PE_aligned ) {
344                 void    **aligned = (void**)( ((uintptr_t)ptr + sizeof(void*) - 1) & -(sizeof(void*)-1) );
345                 ptr = (void*)( (uintptr_t)aligned + sizeof(void*) );
346                 return (uintptr_t)*aligned;
347         }
348         uint64_t        rv;
349         uintptr_t       orig_ptr = (uintptr_t)ptr;
350         switch(encoding & DW_EH_PE_fmtmask)
351         {
352         case DW_EH_PE_absptr:   // absolute
353                 rv = (uintptr_t)read<void*>(ptr);
354                 break;
355         case DW_EH_PE_uleb128:
356                 rv = _read_leb128u(ptr);
357                 break;
358         case DW_EH_PE_sleb128:
359                 rv = _read_leb128s(ptr);
360                 break;
361         case DW_EH_PE_udata2:
362                 rv = read<uint16_t>(ptr);
363                 break;
364         case DW_EH_PE_udata4:
365                 rv = read<uint32_t>(ptr);
366                 break;
367         case DW_EH_PE_udata8:
368                 rv = read<uint64_t>(ptr);
369                 break;
370         default:
371                 ::_SysDebug("_read_encoded: Unknown encoding format 0x%x", encoding & DW_EH_PE_fmtmask);
372                 ::abort();
373         }
374         if( rv != 0 )
375         {
376                 if( (encoding & DW_EH_PE_relmask) == DW_EH_PE_pcrel ) {
377                         rv += orig_ptr;
378                 }
379                 else {
380                         rv += base;
381                 }
382                 
383                 if( encoding & DW_EH_PE_indirect ) {
384                         rv = (uintptr_t)*(void**)(uintptr_t)rv;
385                 }
386                 else {
387                         // nothing
388                 }
389         }
390         return rv;
391 }
392 static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context)
393 {
394         if( encoding == 0xFF )
395                 return 0;
396         switch(encoding & DW_EH_PE_relmask)
397         {
398         case DW_EH_PE_absptr:
399         case DW_EH_PE_pcrel:
400         case DW_EH_PE_aligned:
401                 return 0;
402         //case DW_EH_PE_textrel:
403                 //return _Unwind_GetTextRelBase(context);
404         //case DW_EH_PE_datarel:
405                 //return _Unwind_GetDataRelBase(context);
406         case DW_EH_PE_funcrel:
407                 return _Unwind_GetRegionStart(context);
408         default:
409                 ::_SysDebug("_get_base: Unknown encoding relativity 0x%x", (encoding & DW_EH_PE_relmask));
410                 ::abort();
411         }
412 }
413 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding)
414 {
415         return _read_encoded(ptr, context, encoding, _get_base(encoding, context));
416 }
417
418 static uint64_t _read_leb128_raw(const void *&ptr, int *sizep)
419 {
420          int    size = 0;
421         uint64_t val = 0;
422         const uint8_t   *ptr8 = static_cast<const uint8_t*>(ptr);
423         do
424         {
425                 val |= (*ptr8 & 0x7F) << (size*7);
426                 size ++;
427         } while( *ptr8++ & 0x80 );
428         
429         ptr = ptr8;
430         *sizep = size;
431         
432         return val;
433 }
434
435 static uint64_t _read_leb128u(const void *&ptr)
436 {
437          int    unused_size;
438         return _read_leb128_raw(ptr, &unused_size);
439 }
440 static int64_t _read_leb128s(const void *&ptr)
441 {
442          int    size;
443         uint64_t val = _read_leb128_raw(ptr, &size);
444         if( size*7 <= 64 && (val & (1 << (size*7-1))) )
445         {
446                 val |= 0xFFFFFFFFFFFFFFFF << (size*7);
447         }
448         return val;
449 }
450

UCC git Repository :: git.ucc.asn.au