Usermode/libc++ - Fix system_error construction, silcence EH debug
[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("get_frame_action: 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                         if( filter == 0 )
186                         {
187                                 // Cleanup
188                                 has_cleanups = true;
189                         }
190                         else if( filter < 0 )
191                         {
192                                 // Exception specifcation
193                                 if( !exception_matches_list(throw_type, header, -filter) )
194                                 {
195                                         switch_value = filter;
196                                         return 2;
197                                 }
198                         }
199                         else
200                         {
201                                 // Catch
202                                 if( exception_matches_single(throw_type, header, filter) )
203                                 {
204                                         switch_value = filter;
205                                         return 2;
206                                 }
207                         }
208                         
209                         if( disp == 0 )
210                                 break;
211                         action_list = (const char*)action_list + disp-1;
212                 }
213                 
214                 // If a cleanup request was seen, tell the caller that we want to handle it
215                 if( has_cleanups ) {
216                         return 1;       // 1 = cleanup only
217                 }
218                 // Else, there's nothing here to handle
219                 return 0;
220         }
221 }
222
223 const ::std::type_info *get_type_info(const struct sLSDA_Header &header, int type_index)
224 {
225         size_t  encoded_size = _get_encoded_size(header.TTEncoding);
226         assert(encoded_size > 0);
227         const void *ptr = (const void*)(header.TTBase - encoded_size * type_index);
228         assert( header.TTBase );
229         assert( ptr > header.ActionTable );
230         
231         uintptr_t type_ptr = _read_encoded(ptr, NULL, header.TTEncoding, header.TypePtrBase);
232         return reinterpret_cast< ::std::type_info* >(type_ptr);
233 }
234
235 const ::std::type_info *get_exception_type(const void *exception_object)
236 {
237         if( !exception_object )
238                 return NULL;
239         const struct _Unwind_Exception  *u_execept = (const struct _Unwind_Exception*)exception_object;
240         const struct __cxa_exception *except = (const struct __cxa_exception*)(u_execept + 1) - 1;
241         if( memcmp(&except->unwindHeader.exception_class, EXCEPTION_CLASS_ACESS, 8) != 0 )
242                 return NULL;
243         
244         return except->exceptionType;
245 }
246
247 bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index)
248 {
249         const ::std::type_info *catch_type = get_type_info(header, type_index);
250
251         if( !catch_type )
252         {
253                 _SysDebug("catch(...)");
254                 return true;
255         }
256         else if( !throw_type )
257         {
258                 _SysDebug("threw UNK");
259                 return false;
260         }
261         else
262         {
263                 _SysDebug("catch(%s), throw %s", catch_type->name(), throw_type->name());
264                 size_t ofs = 0;
265                 if( !catch_type->__is_child(*throw_type, ofs) ) {
266                         _SysDebug("> No match");
267                         return false;
268                 }
269                 
270                 return true;
271         }
272 }
273 bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index)
274 {
275         _SysDebug("TODO: exception_matches_list %i", list_index);
276         abort();
277         (void)throw_type;
278         (void)header;
279         return true;
280 }
281
282 template <typename T> T read(const void *&ptr)
283 {
284         T rv = *reinterpret_cast<const T*>(ptr);
285         ptr = (const char*)ptr + sizeof(T);
286         return rv;
287 }
288
289 static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header)
290 {
291         header->Base = ptr;
292         
293         uint8_t start_encoding = read<uint8_t>(ptr);
294         if( start_encoding == DW_EH_PE_omit )
295                 header->LPStart = _Unwind_GetRegionStart(context);
296         else
297                 header->LPStart = _read_encoded(ptr, context, start_encoding);
298         
299         header->TTEncoding = read<uint8_t>(ptr);
300         if( header->TTEncoding == DW_EH_PE_omit )
301                 header->TTBase = 0;
302         else {
303                 ptrdiff_t       tt_ofs = _read_leb128u(ptr);
304                 header->TTBase = (uintptr_t)ptr + tt_ofs;
305         }
306         header->TypePtrBase = _get_base(header->TTEncoding, context);
307
308         header->CallSiteEncoding = read<uint8_t>(ptr);
309         uint64_t call_site_size = _read_leb128u(ptr);
310         header->CallSiteTable = ptr;
311         header->ActionTable = (const void*)( (uintptr_t)ptr + call_site_size );
312         
313         #if 0
314         ::_SysDebug("LSDA header:");
315         ::_SysDebug("->LPStart = 0x%lx", header->LPStart);
316         ::_SysDebug("->TTEncoding = 0x%02x", header->TTEncoding);
317         ::_SysDebug("->TTBase = 0x%lx", header->TTBase);
318         ::_SysDebug("->CallSiteEncoding = 0x%02x", header->CallSiteEncoding);
319         ::_SysDebug("->CallSiteTable = %p", header->CallSiteTable);
320         ::_SysDebug("->ActionTable = %p", header->ActionTable);
321         #endif
322 }
323
324 static size_t _get_encoded_size(int encoding)
325 {
326         switch(encoding & DW_EH_PE_fmtmask)
327         {
328         case DW_EH_PE_absptr:   // absolute
329                 return sizeof(void*);
330         default:
331                 _SysDebug("_get_encoded_size: Unknown encoding 0x%02x", encoding);
332                 return 0;
333         }
334 }
335
336 /*
337  * \brief Read a DWARF encoded pointer from the stream
338  */
339 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base)
340 {
341         (void)context;
342         if( encoding == DW_EH_PE_aligned ) {
343                 void    **aligned = (void**)( ((uintptr_t)ptr + sizeof(void*) - 1) & -(sizeof(void*)-1) );
344                 ptr = (void*)( (uintptr_t)aligned + sizeof(void*) );
345                 return (uintptr_t)*aligned;
346         }
347         uint64_t        rv;
348         uintptr_t       orig_ptr = (uintptr_t)ptr;
349         switch(encoding & DW_EH_PE_fmtmask)
350         {
351         case DW_EH_PE_absptr:   // absolute
352                 rv = (uintptr_t)read<void*>(ptr);
353                 break;
354         case DW_EH_PE_uleb128:
355                 rv = _read_leb128u(ptr);
356                 break;
357         case DW_EH_PE_sleb128:
358                 rv = _read_leb128s(ptr);
359                 break;
360         case DW_EH_PE_udata2:
361                 rv = read<uint16_t>(ptr);
362                 break;
363         case DW_EH_PE_udata4:
364                 rv = read<uint32_t>(ptr);
365                 break;
366         case DW_EH_PE_udata8:
367                 rv = read<uint64_t>(ptr);
368                 break;
369         default:
370                 ::_SysDebug("_read_encoded: Unknown encoding format 0x%x", encoding & DW_EH_PE_fmtmask);
371                 ::abort();
372         }
373         if( rv != 0 )
374         {
375                 if( (encoding & DW_EH_PE_relmask) == DW_EH_PE_pcrel ) {
376                         rv += orig_ptr;
377                 }
378                 else {
379                         rv += base;
380                 }
381                 
382                 if( encoding & DW_EH_PE_indirect ) {
383                         rv = (uintptr_t)*(void**)(uintptr_t)rv;
384                 }
385                 else {
386                         // nothing
387                 }
388         }
389         return rv;
390 }
391 static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context)
392 {
393         if( encoding == 0xFF )
394                 return 0;
395         switch(encoding & DW_EH_PE_relmask)
396         {
397         case DW_EH_PE_absptr:
398         case DW_EH_PE_pcrel:
399         case DW_EH_PE_aligned:
400                 return 0;
401         //case DW_EH_PE_textrel:
402                 //return _Unwind_GetTextRelBase(context);
403         //case DW_EH_PE_datarel:
404                 //return _Unwind_GetDataRelBase(context);
405         case DW_EH_PE_funcrel:
406                 return _Unwind_GetRegionStart(context);
407         default:
408                 ::_SysDebug("_get_base: Unknown encoding relativity 0x%x", (encoding & DW_EH_PE_relmask));
409                 ::abort();
410         }
411 }
412 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding)
413 {
414         return _read_encoded(ptr, context, encoding, _get_base(encoding, context));
415 }
416
417 static uint64_t _read_leb128_raw(const void *&ptr, int *sizep)
418 {
419          int    size = 0;
420         uint64_t val = 0;
421         const uint8_t   *ptr8 = static_cast<const uint8_t*>(ptr);
422         do
423         {
424                 val |= (*ptr8 & 0x7F) << (size*7);
425                 size ++;
426         } while( *ptr8++ & 0x80 );
427         
428         ptr = ptr8;
429         *sizep = size;
430         
431         return val;
432 }
433
434 static uint64_t _read_leb128u(const void *&ptr)
435 {
436          int    unused_size;
437         return _read_leb128_raw(ptr, &unused_size);
438 }
439 static int64_t _read_leb128s(const void *&ptr)
440 {
441          int    size;
442         uint64_t val = _read_leb128_raw(ptr, &size);
443         if( size*7 <= 64 && (val & (1 << (size*7-1))) )
444         {
445                 val |= 0xFFFFFFFFFFFFFFFF << (size*7);
446         }
447         return val;
448 }
449

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