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

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