2 * Acess2 C++ Support Library
3 * - By John Hodge (thePowersGang)
6 * - Implementation of GNU C++ Exception handling "Personality"
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>
16 #include <cxxabi.h> // __dynamic_cast
18 #define DEBUG_ENABLED 1
21 # define DEBUG(v...) ::_SysDebug(v)
23 # define DEBUG(v...) do{}while(0)
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);
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)
46 //::_SysDebug("__gxx_personality_v0(%i, 0x%x, 0x%llx, ...)", version, actions, exceptionClass);
47 //::_SysDebug("__gxx_personality_v0(..., %p, %p)", exceptionObject, context);
49 //if( exceptionObject ) {
50 // ::_SysDebug("exception_object: Class 0x%llx", exceptionObject->exception_class);
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));
60 ::_SysDebug("%s: Unexpected version %i != exp 1", __func__, version);
61 return _URC_FATAL_PHASE1_ERROR;
64 const void *lsda_ptr = (const void*)_Unwind_GetLanguageSpecificData(context);
65 struct sLSDA_Header header;
66 read_lsda_header(lsda_ptr, context, &header);
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
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);
78 if( (actions & _UA_SEARCH_PHASE) && (actions & _UA_CLEANUP_PHASE) )
80 _SysDebug("__gxx_personality_v0: ERROR Both _UA_SEARCH_PHASE and _UA_CLEANUP_PHASE set");
85 if( actions & _UA_SEARCH_PHASE )
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;
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;
101 if( actions & _UA_CLEANUP_PHASE )
103 // No action, continue
104 if( frame_action == 0 ) {
105 return _URC_CONTINUE_UNWIND;
109 if( actions & _UA_FORCE_UNWIND )
111 // Unwind forced, override switch_value to 0
112 // - Disables any attempt to handle exception
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
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;
127 _SysDebug("__gxx_personality_v0: Neither _UA_SEARCH_PHASE or _UA_CLEANUP_PHASE set");
128 return _URC_FATAL_PHASE1_ERROR;
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)
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)
140 const void *lsda_ptr = header.CallSiteTable;
141 while( lsda_ptr < header.ActionTable )
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);
148 // If IP falls below this entry, it's not on the list
149 if( ip <= cs_start ) {
150 lsda_ptr = header.ActionTable;
153 if( ip > cs_start + cs_len )
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);
162 if( lsda_ptr >= header.ActionTable ) {
164 DEBUG("__gxx_personality_v0: No entry for IP 0x%x", ip);
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);
177 else if( cs_action == 0 ) {
178 DEBUG("No action, cleanups only");
180 landingpad = header.LPStart + cs_ldgpad;
181 return 1; // 1 = cleanup only
185 landingpad = header.LPStart + cs_ldgpad;
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
191 leb128s_t filter = _read_leb128s(action_list);
192 leb128s_t disp = _read_leb128s(action_list);
198 else if( filter < 0 )
200 // Exception specifcation
201 if( !exception_matches_list(throw_type, header, -filter) )
203 switch_value = filter;
210 if( exception_matches_single(throw_type, header, filter) )
212 switch_value = filter;
219 action_list = (const char*)action_list + disp-1;
222 // If a cleanup request was seen, tell the caller that we want to handle it
224 return 1; // 1 = cleanup only
226 // Else, there's nothing here to handle
231 const ::std::type_info *get_type_info(const struct sLSDA_Header &header, int type_index)
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 );
239 uintptr_t type_ptr = _read_encoded(ptr, NULL, header.TTEncoding, header.TypePtrBase);
240 return reinterpret_cast< ::std::type_info* >(type_ptr);
243 const ::std::type_info *get_exception_type(const void *exception_object)
245 if( !exception_object )
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 )
252 return except->exceptionType;
255 bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index)
257 const ::std::type_info *catch_type = get_type_info(header, type_index);
258 DEBUG("catch_type = %p", catch_type);
265 else if( !throw_type )
272 DEBUG("catch(%s), throw %s", catch_type->name(), throw_type->name());
274 if( !catch_type->__is_child(*throw_type, ofs) ) {
275 _SysDebug("> No match");
282 bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index)
284 _SysDebug("TODO: exception_matches_list %i", list_index);
291 template <typename T> T read(const void *&ptr)
293 T rv = *reinterpret_cast<const T*>(ptr);
294 ptr = (const char*)ptr + sizeof(T);
298 static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header)
302 uint8_t start_encoding = read<uint8_t>(ptr);
303 if( start_encoding == DW_EH_PE_omit )
304 header->LPStart = _Unwind_GetRegionStart(context);
306 header->LPStart = _read_encoded(ptr, context, start_encoding);
308 header->TTEncoding = read<uint8_t>(ptr);
309 if( header->TTEncoding == DW_EH_PE_omit )
312 ptrdiff_t tt_ofs = _read_leb128u(ptr);
313 header->TTBase = (uintptr_t)ptr + tt_ofs;
315 header->TypePtrBase = _get_base(header->TTEncoding, context);
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 );
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);
333 static size_t _get_encoded_size(int encoding)
335 switch(encoding & DW_EH_PE_fmtmask)
337 case DW_EH_PE_absptr: // absolute
338 return sizeof(void*);
340 _SysDebug("_get_encoded_size: Unknown encoding 0x%02x", encoding);
346 * \brief Read a DWARF encoded pointer from the stream
348 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base)
351 if( encoding == DW_EH_PE_aligned ) {
352 void **aligned = (void**)( ((uintptr_t)ptr + sizeof(void*) - 1) & -(sizeof(void*)-1) );
353 ptr = (void*)( (uintptr_t)aligned + sizeof(void*) );
354 return (uintptr_t)*aligned;
357 uintptr_t orig_ptr = (uintptr_t)ptr;
358 switch(encoding & DW_EH_PE_fmtmask)
360 case DW_EH_PE_absptr: // absolute
361 rv = (uintptr_t)read<void*>(ptr);
363 case DW_EH_PE_uleb128:
364 rv = _read_leb128u(ptr);
366 case DW_EH_PE_sleb128:
367 rv = _read_leb128s(ptr);
369 case DW_EH_PE_udata2:
370 rv = read<uint16_t>(ptr);
372 case DW_EH_PE_udata4:
373 rv = read<uint32_t>(ptr);
375 case DW_EH_PE_udata8:
376 rv = read<uint64_t>(ptr);
379 ::_SysDebug("_read_encoded: Unknown encoding format 0x%x", encoding & DW_EH_PE_fmtmask);
384 if( (encoding & DW_EH_PE_relmask) == DW_EH_PE_pcrel ) {
391 if( encoding & DW_EH_PE_indirect ) {
392 rv = (uintptr_t)*(void**)(uintptr_t)rv;
400 static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context)
402 if( encoding == 0xFF )
404 switch(encoding & DW_EH_PE_relmask)
406 case DW_EH_PE_absptr:
408 case DW_EH_PE_aligned:
410 //case DW_EH_PE_textrel:
411 //return _Unwind_GetTextRelBase(context);
412 //case DW_EH_PE_datarel:
413 //return _Unwind_GetDataRelBase(context);
414 case DW_EH_PE_funcrel:
415 return _Unwind_GetRegionStart(context);
417 ::_SysDebug("_get_base: Unknown encoding relativity 0x%x", (encoding & DW_EH_PE_relmask));
422 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding)
424 return _read_encoded(ptr, context, encoding, _get_base(encoding, context));
427 static uint64_t _read_leb128_raw(const void *&ptr, int *sizep)
431 const uint8_t *ptr8 = static_cast<const uint8_t*>(ptr);
434 val |= (*ptr8 & 0x7F) << (size*7);
436 } while( *ptr8++ & 0x80 );
444 static uint64_t _read_leb128u(const void *&ptr)
447 return _read_leb128_raw(ptr, &unused_size);
449 static int64_t _read_leb128s(const void *&ptr)
452 uint64_t val = _read_leb128_raw(ptr, &size);
453 if( size*7 <= 64 && (val & (1 << (size*7-1))) )
455 val |= 0xFFFFFFFFFFFFFFFF << (size*7);