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
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);
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)
38 //::_SysDebug("__gxx_personality_v0(%i, 0x%x, 0x%llx, ...)", version, actions, exceptionClass);
39 //::_SysDebug("__gxx_personality_v0(..., %p, %p)", exceptionObject, context);
41 //if( exceptionObject ) {
42 // ::_SysDebug("exception_object: Class 0x%llx", exceptionObject->exception_class);
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));
52 ::_SysDebug("%s: Unexpected version %i != exp 1", __func__, version);
53 return _URC_FATAL_PHASE1_ERROR;
56 const void *lsda_ptr = (const void*)_Unwind_GetLanguageSpecificData(context);
57 struct sLSDA_Header header;
58 read_lsda_header(lsda_ptr, context, &header);
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
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);
70 if( (actions & _UA_SEARCH_PHASE) && (actions & _UA_CLEANUP_PHASE) )
72 _SysDebug("__gxx_personality_v0: ERROR Both _UA_SEARCH_PHASE and _UA_CLEANUP_PHASE set");
77 if( actions & _UA_SEARCH_PHASE )
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;
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;
93 if( actions & _UA_CLEANUP_PHASE )
95 // No action, continue
96 if( frame_action == 0 ) {
97 return _URC_CONTINUE_UNWIND;
101 if( actions & _UA_FORCE_UNWIND )
103 // Unwind forced, override switch_value to 0
104 // - Disables any attempt to handle exception
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
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;
119 _SysDebug("__gxx_personality_v0: Neither _UA_SEARCH_PHASE or _UA_CLEANUP_PHASE set");
120 return _URC_FATAL_PHASE1_ERROR;
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)
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)
132 const void *lsda_ptr = header.CallSiteTable;
133 while( lsda_ptr < header.ActionTable )
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);
140 // If IP falls below this entry, it's not on the list
141 if( ip <= cs_start ) {
142 lsda_ptr = header.ActionTable;
145 if( ip > cs_start + cs_len )
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);
154 if( lsda_ptr >= header.ActionTable ) {
156 _SysDebug("__gxx_personality_v0: No entry for IP 0x%x", ip);
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);
169 else if( cs_action == 0 ) {
170 _SysDebug("No action, cleanups only");
172 landingpad = header.LPStart + cs_ldgpad;
173 return 1; // 1 = cleanup only
177 landingpad = header.LPStart + cs_ldgpad;
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
183 leb128s_t filter = _read_leb128s(action_list);
184 leb128s_t disp = _read_leb128s(action_list);
190 else if( filter < 0 )
192 // Exception specifcation
193 if( !exception_matches_list(throw_type, header, -filter) )
195 switch_value = filter;
202 if( exception_matches_single(throw_type, header, filter) )
204 switch_value = filter;
211 action_list = (const char*)action_list + disp-1;
214 // If a cleanup request was seen, tell the caller that we want to handle it
216 return 1; // 1 = cleanup only
218 // Else, there's nothing here to handle
223 const ::std::type_info *get_type_info(const struct sLSDA_Header &header, int type_index)
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 );
231 uintptr_t type_ptr = _read_encoded(ptr, NULL, header.TTEncoding, header.TypePtrBase);
232 return reinterpret_cast< ::std::type_info* >(type_ptr);
235 const ::std::type_info *get_exception_type(const void *exception_object)
237 if( !exception_object )
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 )
244 return except->exceptionType;
247 bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index)
249 const ::std::type_info *catch_type = get_type_info(header, type_index);
253 _SysDebug("catch(...)");
256 else if( !throw_type )
258 _SysDebug("threw UNK");
263 _SysDebug("catch(%s), throw %s", catch_type->name(), throw_type->name());
265 if( !catch_type->__is_child(*throw_type, ofs) ) {
266 _SysDebug("> No match");
273 bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index)
275 _SysDebug("TODO: exception_matches_list %i", list_index);
282 template <typename T> T read(const void *&ptr)
284 T rv = *reinterpret_cast<const T*>(ptr);
285 ptr = (const char*)ptr + sizeof(T);
289 static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header)
293 uint8_t start_encoding = read<uint8_t>(ptr);
294 if( start_encoding == DW_EH_PE_omit )
295 header->LPStart = _Unwind_GetRegionStart(context);
297 header->LPStart = _read_encoded(ptr, context, start_encoding);
299 header->TTEncoding = read<uint8_t>(ptr);
300 if( header->TTEncoding == DW_EH_PE_omit )
303 ptrdiff_t tt_ofs = _read_leb128u(ptr);
304 header->TTBase = (uintptr_t)ptr + tt_ofs;
306 header->TypePtrBase = _get_base(header->TTEncoding, context);
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 );
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);
324 static size_t _get_encoded_size(int encoding)
326 switch(encoding & DW_EH_PE_fmtmask)
328 case DW_EH_PE_absptr: // absolute
329 return sizeof(void*);
331 _SysDebug("_get_encoded_size: Unknown encoding 0x%02x", encoding);
337 * \brief Read a DWARF encoded pointer from the stream
339 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base)
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;
348 uintptr_t orig_ptr = (uintptr_t)ptr;
349 switch(encoding & DW_EH_PE_fmtmask)
351 case DW_EH_PE_absptr: // absolute
352 rv = (uintptr_t)read<void*>(ptr);
354 case DW_EH_PE_uleb128:
355 rv = _read_leb128u(ptr);
357 case DW_EH_PE_sleb128:
358 rv = _read_leb128s(ptr);
360 case DW_EH_PE_udata2:
361 rv = read<uint16_t>(ptr);
363 case DW_EH_PE_udata4:
364 rv = read<uint32_t>(ptr);
366 case DW_EH_PE_udata8:
367 rv = read<uint64_t>(ptr);
370 ::_SysDebug("_read_encoded: Unknown encoding format 0x%x", encoding & DW_EH_PE_fmtmask);
375 if( (encoding & DW_EH_PE_relmask) == DW_EH_PE_pcrel ) {
382 if( encoding & DW_EH_PE_indirect ) {
383 rv = (uintptr_t)*(void**)(uintptr_t)rv;
391 static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context)
393 if( encoding == 0xFF )
395 switch(encoding & DW_EH_PE_relmask)
397 case DW_EH_PE_absptr:
399 case DW_EH_PE_aligned:
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);
408 ::_SysDebug("_get_base: Unknown encoding relativity 0x%x", (encoding & DW_EH_PE_relmask));
412 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding)
414 return _read_encoded(ptr, context, encoding, _get_base(encoding, context));
417 static uint64_t _read_leb128_raw(const void *&ptr, int *sizep)
421 const uint8_t *ptr8 = static_cast<const uint8_t*>(ptr);
424 val |= (*ptr8 & 0x7F) << (size*7);
426 } while( *ptr8++ & 0x80 );
434 static uint64_t _read_leb128u(const void *&ptr)
437 return _read_leb128_raw(ptr, &unused_size);
439 static int64_t _read_leb128s(const void *&ptr)
442 uint64_t val = _read_leb128_raw(ptr, &size);
443 if( size*7 <= 64 && (val & (1 << (size*7-1))) )
445 val |= 0xFFFFFFFFFFFFFFFF << (size*7);