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("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);
185 _SysDebug("filter=%lli,disp=%lli", filter, disp);
191 else if( filter < 0 )
193 // Exception specifcation
194 if( !exception_matches_list(throw_type, header, -filter) )
196 switch_value = filter;
203 if( exception_matches_single(throw_type, header, filter) )
205 switch_value = filter;
212 action_list = (const char*)action_list + disp-1;
215 // If a cleanup request was seen, tell the caller that we want to handle it
217 return 1; // 1 = cleanup only
219 // Else, there's nothing here to handle
224 const ::std::type_info *get_type_info(const struct sLSDA_Header &header, int type_index)
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 );
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);
237 const ::std::type_info *get_exception_type(const void *exception_object)
239 if( !exception_object )
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 )
246 return except->exceptionType;
249 bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index)
251 const ::std::type_info *catch_type = get_type_info(header, type_index);
255 _SysDebug("catch(...)");
258 else if( !throw_type )
260 _SysDebug("threw UNK");
265 _SysDebug("catch(%s), throw %s", catch_type->name(), throw_type->name());
267 if( !catch_type->__is_child(*throw_type, ofs) ) {
268 _SysDebug("> No match");
275 bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index)
277 _SysDebug("TODO: exception_matches_list %i", list_index);
283 template <typename T> T read(const void *&ptr)
285 T rv = *reinterpret_cast<const T*>(ptr);
286 ptr = (const char*)ptr + sizeof(T);
290 static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header)
294 uint8_t start_encoding = read<uint8_t>(ptr);
295 if( start_encoding == DW_EH_PE_omit )
296 header->LPStart = _Unwind_GetRegionStart(context);
298 header->LPStart = _read_encoded(ptr, context, start_encoding);
300 header->TTEncoding = read<uint8_t>(ptr);
301 if( header->TTEncoding == DW_EH_PE_omit )
304 ptrdiff_t tt_ofs = _read_leb128u(ptr);
305 header->TTBase = (uintptr_t)ptr + tt_ofs;
307 header->TypePtrBase = _get_base(header->TTEncoding, context);
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 );
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);
325 static size_t _get_encoded_size(int encoding)
327 switch(encoding & DW_EH_PE_fmtmask)
329 case DW_EH_PE_absptr: // absolute
330 return sizeof(void*);
332 _SysDebug("_get_encoded_size: Unknown encoding 0x%02x", encoding);
338 * \brief Read a DWARF encoded pointer from the stream
340 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base)
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;
349 uintptr_t orig_ptr = (uintptr_t)ptr;
350 switch(encoding & DW_EH_PE_fmtmask)
352 case DW_EH_PE_absptr: // absolute
353 rv = (uintptr_t)read<void*>(ptr);
355 case DW_EH_PE_uleb128:
356 rv = _read_leb128u(ptr);
358 case DW_EH_PE_sleb128:
359 rv = _read_leb128s(ptr);
361 case DW_EH_PE_udata2:
362 rv = read<uint16_t>(ptr);
364 case DW_EH_PE_udata4:
365 rv = read<uint32_t>(ptr);
367 case DW_EH_PE_udata8:
368 rv = read<uint64_t>(ptr);
371 ::_SysDebug("_read_encoded: Unknown encoding format 0x%x", encoding & DW_EH_PE_fmtmask);
376 if( (encoding & DW_EH_PE_relmask) == DW_EH_PE_pcrel ) {
383 if( encoding & DW_EH_PE_indirect ) {
384 rv = (uintptr_t)*(void**)(uintptr_t)rv;
392 static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context)
394 if( encoding == 0xFF )
396 switch(encoding & DW_EH_PE_relmask)
398 case DW_EH_PE_absptr:
400 case DW_EH_PE_aligned:
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);
409 ::_SysDebug("_get_base: Unknown encoding relativity 0x%x", (encoding & DW_EH_PE_relmask));
413 static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding)
415 return _read_encoded(ptr, context, encoding, _get_base(encoding, context));
418 static uint64_t _read_leb128_raw(const void *&ptr, int *sizep)
422 const uint8_t *ptr8 = static_cast<const uint8_t*>(ptr);
425 val |= (*ptr8 & 0x7F) << (size*7);
427 } while( *ptr8++ & 0x80 );
435 static uint64_t _read_leb128u(const void *&ptr)
438 return _read_leb128_raw(ptr, &unused_size);
440 static int64_t _read_leb128s(const void *&ptr)
443 uint64_t val = _read_leb128_raw(ptr, &size);
444 if( size*7 <= 64 && (val & (1 << (size*7-1))) )
446 val |= 0xFFFFFFFFFFFFFFFF << (size*7);