Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/exception.c

    rc960331 reb46fdf  
    99// Author           : Andrew Beach
    1010// Created On       : Mon Jun 26 15:13:00 2017
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Tue Oct 27 16:27:00 2020
    13 // Update Count     : 35
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Thu Feb 22 18:17:34 2018
     13// Update Count     : 11
    1414//
    1515
    16 // Normally we would get this from the CFA prelude.
    1716#include <stddef.h> // for size_t
    1817
    19 #include <unwind.h> // for struct _Unwind_Exception {...};
    20 
    2118#include "exception.h"
     19
     20// Implementation of the secret header.
    2221
    2322#include <stdlib.h>
    2423#include <stdio.h>
     24#include <unwind.h>
    2525#include <bits/debug.hfa>
    26 #include "concurrency/invoke.h"
    27 #include "stdhdr/assert.h"
    28 
    29 #if defined( __ARM_ARCH )
    30 #warning FIX ME: temporary hack to keep ARM build working
     26
     27// FIX ME: temporary hack to keep ARM build working
    3128#ifndef _URC_FATAL_PHASE1_ERROR
    32 #define _URC_FATAL_PHASE1_ERROR 3
     29#define _URC_FATAL_PHASE1_ERROR 2
    3330#endif // ! _URC_FATAL_PHASE1_ERROR
    3431#ifndef _URC_FATAL_PHASE2_ERROR
    3532#define _URC_FATAL_PHASE2_ERROR 2
    3633#endif // ! _URC_FATAL_PHASE2_ERROR
    37 #endif // __ARM_ARCH
    3834
    3935#include "lsda.h"
    4036
    41 /* The exception class for our exceptions. Because of the vendor component
    42  * its value would not be standard.
    43  * Vendor: UWPL
    44  * Language: CFA\0
    45  */
    46 const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643;
    4737
    4838// Base exception vtable is abstract, you should not have base exceptions.
    49 struct __cfaehm_base_exception_t_vtable
    50                 ___cfaehm_base_exception_t_vtable_instance = {
     39struct __cfaabi_ehm__base_exception_t_vtable
     40                ___cfaabi_ehm__base_exception_t_vtable_instance = {
    5141        .parent = NULL,
    5242        .size = 0,
     
    5747
    5848
     49// Temperary global exception context. Does not work with concurency.
     50struct exception_context_t {
     51    struct __cfaabi_ehm__try_resume_node * top_resume;
     52    struct __cfaabi_ehm__try_resume_node * current_resume;
     53
     54    exception_t * current_exception;
     55    int current_handler_index;
     56} shared_stack = {NULL, NULL, 0, 0};
     57
    5958// Get the current exception context.
    6059// There can be a single global until multithreading occurs, then each stack
    61 // needs its own. We get this from libcfathreads (no weak attribute).
    62 __attribute__((weak)) struct exception_context_t * this_exception_context() {
    63         static struct exception_context_t shared_stack = {NULL, NULL};
     60// needs its own. It will have to be updated to handle that.
     61struct exception_context_t * this_exception_context() {
    6462        return &shared_stack;
    6563}
     64//#define SAVE_EXCEPTION_CONTEXT(to_name)
     65//struct exception_context_t * to_name = this_exception_context();
     66//exception * this_exception() {
     67//    return this_exception_context()->current_exception;
     68//}
     69
     70
     71// This macro should be the only thing that needs to change across machines.
     72// Used in the personality function, way down in termination.
     73// struct _Unwind_Context * -> _Unwind_Reason_Code(*)(exception_t *)
     74#define MATCHER_FROM_CONTEXT(ptr_to_context) \
     75        (*(_Unwind_Reason_Code(**)(exception_t *))(_Unwind_GetCFA(ptr_to_context) + 8))
    6676
    6777
    6878// RESUMPTION ================================================================
    6979
    70 static void reset_top_resume(struct __cfaehm_try_resume_node ** store) {
    71         this_exception_context()->top_resume = *store;
    72 }
    73 
    74 void __cfaehm_throw_resume(exception_t * except, void (*defaultHandler)(exception_t *)) {
    75         struct exception_context_t * context = this_exception_context();
    76 
    77         __cfadbg_print_safe(exception, "Throwing resumption exception\n");
    78 
    79         {
    80                 __attribute__((cleanup(reset_top_resume)))
    81                 struct __cfaehm_try_resume_node * original_head = context->top_resume;
    82                 struct __cfaehm_try_resume_node * current = context->top_resume;
    83 
    84                 for ( ; current ; current = current->next) {
    85                         context->top_resume = current->next;
    86                         if (current->handler(except)) {
    87                                 return;
    88                         }
     80void __cfaabi_ehm__throw_resume(exception_t * except) {
     81
     82        __cfaabi_dbg_print_safe("Throwing resumption exception\n");
     83
     84        struct __cfaabi_ehm__try_resume_node * original_head = shared_stack.current_resume;
     85        struct __cfaabi_ehm__try_resume_node * current =
     86                (original_head) ? original_head->next : shared_stack.top_resume;
     87
     88        for ( ; current ; current = current->next) {
     89                shared_stack.current_resume = current;
     90                if (current->handler(except)) {
     91                        shared_stack.current_resume = original_head;
     92                        return;
    8993                }
    90         } // End the search and return to the top of the stack.
    91 
    92         // No handler found, fall back to the default operation.
    93         __cfadbg_print_safe(exception, "Unhandled exception\n");
    94         defaultHandler(except);
     94        }
     95
     96        __cfaabi_dbg_print_safe("Unhandled exception\n");
     97        shared_stack.current_resume = original_head;
     98
     99        // Fall back to termination:
     100        __cfaabi_ehm__throw_terminate(except);
     101        // TODO: Default handler for resumption.
    95102}
    96103
     
    99106// be added after the node is built but before it is made the top node.
    100107
    101 void __cfaehm_try_resume_setup(struct __cfaehm_try_resume_node * node,
     108void __cfaabi_ehm__try_resume_setup(struct __cfaabi_ehm__try_resume_node * node,
    102109                        _Bool (*handler)(exception_t * except)) {
     110        node->next = shared_stack.top_resume;
     111        node->handler = handler;
     112        shared_stack.top_resume = node;
     113}
     114
     115void __cfaabi_ehm__try_resume_cleanup(struct __cfaabi_ehm__try_resume_node * node) {
     116        shared_stack.top_resume = node->next;
     117}
     118
     119
     120// TERMINATION ===============================================================
     121
     122// MEMORY MANAGEMENT (still for integers)
     123// May have to move to cfa for constructors and destructors (references).
     124
     125struct __cfaabi_ehm__node {
     126        struct __cfaabi_ehm__node * next;
     127};
     128
     129#define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node)))
     130#define EXCEPT_TO_NODE(except) ((struct __cfaabi_ehm__node *)(except) - 1)
     131
     132// Creates a copy of the indicated exception and sets current_exception to it.
     133static void __cfaabi_ehm__allocate_exception( exception_t * except ) {
    103134        struct exception_context_t * context = this_exception_context();
    104         node->next = context->top_resume;
    105         node->handler = handler;
    106         context->top_resume = node;
    107 }
    108 
    109 void __cfaehm_try_resume_cleanup(struct __cfaehm_try_resume_node * node) {
    110         struct exception_context_t * context = this_exception_context();
    111         context->top_resume = node->next;
    112 }
    113 
    114 
    115 // MEMORY MANAGEMENT =========================================================
    116 
    117 #define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node)))
    118 #define EXCEPT_TO_NODE(except) ((struct __cfaehm_node *)(except) - 1)
    119 #define UNWIND_TO_NODE(unwind) ((struct __cfaehm_node *)(unwind))
    120 #define NULL_MAP(map, ptr) ((ptr) ? (map(ptr)) : NULL)
    121 
    122 // How to clean up an exception in various situations.
    123 static void __cfaehm_exception_cleanup(
    124                 _Unwind_Reason_Code reason,
    125                 struct _Unwind_Exception * exception) {
    126         switch (reason) {
    127         case _URC_FOREIGN_EXCEPTION_CAUGHT:
    128                 // This one we could clean-up to allow cross-language exceptions.
    129         case _URC_FATAL_PHASE1_ERROR:
    130         case _URC_FATAL_PHASE2_ERROR:
    131         default:
    132                 abort();
    133         }
    134 }
    135 
    136 // Creates a copy of the indicated exception and sets current_exception to it.
    137 static void __cfaehm_allocate_exception( exception_t * except ) {
    138         struct exception_context_t * context = this_exception_context();
    139135
    140136        // Allocate memory for the exception.
    141         struct __cfaehm_node * store = malloc(
    142                 sizeof( struct __cfaehm_node ) + except->virtual_table->size );
     137        struct __cfaabi_ehm__node * store = malloc(
     138                sizeof( struct __cfaabi_ehm__node ) + except->virtual_table->size );
    143139
    144140        if ( ! store ) {
     
    147143        }
    148144
    149         // Initialize the node:
    150         exception_t * except_store = NODE_TO_EXCEPT(store);
    151         store->unwind_exception.exception_class = __cfaehm_exception_class;
    152         store->unwind_exception.exception_cleanup = __cfaehm_exception_cleanup;
    153         store->handler_index = 0;
    154         except->virtual_table->copy( except_store, except );
    155 
    156145        // Add the node to the list:
    157         store->next = NULL_MAP(EXCEPT_TO_NODE, context->current_exception);
    158         context->current_exception = except_store;
     146        store->next = EXCEPT_TO_NODE(context->current_exception);
     147        context->current_exception = NODE_TO_EXCEPT(store);
     148
     149        // Copy the exception to storage.
     150        except->virtual_table->copy( context->current_exception, except );
    159151}
    160152
    161153// Delete the provided exception, unsetting current_exception if relivant.
    162 static void __cfaehm_delete_exception( exception_t * except ) {
     154static void __cfaabi_ehm__delete_exception( exception_t * except ) {
    163155        struct exception_context_t * context = this_exception_context();
    164156
    165         __cfadbg_print_safe(exception, "Deleting Exception\n");
     157        __cfaabi_dbg_print_safe("Deleting Exception\n");
    166158
    167159        // Remove the exception from the list.
    168         struct __cfaehm_node * to_free = EXCEPT_TO_NODE(except);
    169         struct __cfaehm_node * node;
     160        struct __cfaabi_ehm__node * to_free = EXCEPT_TO_NODE(except);
     161        struct __cfaabi_ehm__node * node;
    170162
    171163        if ( context->current_exception == except ) {
    172164                node = to_free->next;
    173                 context->current_exception = NULL_MAP(NODE_TO_EXCEPT, node);
     165                context->current_exception = (node) ? NODE_TO_EXCEPT(node) : 0;
    174166        } else {
    175167                node = EXCEPT_TO_NODE(context->current_exception);
    176168                // It may always be in the first or second position.
    177                 while ( to_free != node->next ) {
     169                while( to_free != node->next ) {
    178170                        node = node->next;
    179171                }
     
    186178}
    187179
    188 // CANCELLATION ==============================================================
     180// If this isn't a rethrow (*except==0), delete the provided exception.
     181void __cfaabi_ehm__cleanup_terminate( void * except ) {
     182        if ( *(void**)except ) __cfaabi_ehm__delete_exception( *(exception_t **)except );
     183}
     184
     185
     186// We need a piece of storage to raise the exception
     187struct _Unwind_Exception this_exception_storage;
    189188
    190189// Function needed by force unwind
     
    193192                int version,
    194193                _Unwind_Action actions,
    195                 _Unwind_Exception_Class exception_class,
     194                _Unwind_Exception_Class exceptionClass,
    196195                struct _Unwind_Exception * unwind_exception,
    197                 struct _Unwind_Context * unwind_context,
    198                 void * stop_param) {
    199         // Verify actions follow the rules we expect.
    200         verify(actions & _UA_CLEANUP_PHASE);
    201         verify(actions & _UA_FORCE_UNWIND);
    202         verify(!(actions & _UA_SEARCH_PHASE));
    203         verify(!(actions & _UA_HANDLER_FRAME));
    204 
    205         if ( actions & _UA_END_OF_STACK ) {
    206                 abort();
    207         } else {
    208                 return _URC_NO_REASON;
    209         }
    210 }
    211 
    212 __attribute__((weak)) _Unwind_Reason_Code
    213 __cfaehm_cancellation_unwind( struct _Unwind_Exception * exception ) {
    214         return _Unwind_ForcedUnwind( exception, _Stop_Fn, (void*)0x22 );
    215 }
    216 
    217 // Cancel the current stack, prefroming approprate clean-up and messaging.
    218 void __cfaehm_cancel_stack( exception_t * exception ) {
    219         __cfaehm_allocate_exception( exception );
    220 
    221         struct exception_context_t * context = this_exception_context();
    222         struct __cfaehm_node * node = EXCEPT_TO_NODE(context->current_exception);
    223 
    224         // Preform clean-up of any extra active exceptions.
    225         while ( node->next ) {
    226                 struct __cfaehm_node * to_free = node->next;
    227                 node->next = to_free->next;
    228                 exception_t * except = NODE_TO_EXCEPT( to_free );
    229                 except->virtual_table->free( except );
    230             free( to_free );
    231         }
    232 
    233         _Unwind_Reason_Code ret;
    234         ret = __cfaehm_cancellation_unwind( &node->unwind_exception );
    235         printf("UNWIND ERROR %d after force unwind\n", ret);
    236         abort();
    237 }
    238 
    239 
    240 // TERMINATION ===============================================================
    241 
    242 // If this isn't a rethrow (*except==0), delete the provided exception.
    243 void __cfaehm_cleanup_terminate( void * except ) {
    244         if ( *(void**)except ) __cfaehm_delete_exception( *(exception_t **)except );
    245 }
    246 
    247 static void __cfaehm_cleanup_default( exception_t ** except ) {
    248         __cfaehm_delete_exception( *except );
    249         *except = NULL;
     196                struct _Unwind_Context * context,
     197                void * some_param) {
     198        if( actions & _UA_END_OF_STACK  ) exit(1);
     199        if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON;
     200
     201        return _URC_FATAL_PHASE2_ERROR;
    250202}
    251203
    252204// The exception that is being thrown must already be stored.
    253 static void __cfaehm_begin_unwind(void(*defaultHandler)(exception_t *)) {
    254         struct exception_context_t * context = this_exception_context();
    255         if ( NULL == context->current_exception ) {
     205__attribute__((noreturn)) void __cfaabi_ehm__begin_unwind(void) {
     206        if ( ! this_exception_context()->current_exception ) {
    256207                printf("UNWIND ERROR missing exception in begin unwind\n");
    257208                abort();
    258209        }
    259         struct _Unwind_Exception * storage =
    260                 &EXCEPT_TO_NODE(context->current_exception)->unwind_exception;
     210
    261211
    262212        // Call stdlibc to raise the exception
    263         __cfadbg_print_safe(exception, "Begin unwinding (storage &p, context %p)\n", storage, context);
    264         _Unwind_Reason_Code ret = _Unwind_RaiseException( storage );
     213        _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage );
    265214
    266215        // If we reach here it means something happened. For resumption to work we need to find a way
     
    271220        // the whole stack.
    272221
     222        if( ret == _URC_END_OF_STACK ) {
     223                // No proper handler was found. This can be handled in many ways, C++ calls std::terminate.
     224                // Here we force unwind the stack, basically raising a cancellation.
     225                printf("Uncaught exception %p\n", &this_exception_storage);
     226
     227                ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 );
     228                printf("UNWIND ERROR %d after force unwind\n", ret);
     229                abort();
     230        }
     231
    273232        // We did not simply reach the end of the stack without finding a handler. This is an error.
    274         if ( ret != _URC_END_OF_STACK ) {
    275                 printf("UNWIND ERROR %d after raise exception\n", ret);
    276                 abort();
    277         }
    278 
    279         // No handler found, go to the default operation.
    280         __cfadbg_print_safe(exception, "Uncaught exception %p\n", storage);
    281 
    282         __attribute__((cleanup(__cfaehm_cleanup_default)))
    283         exception_t * exception = context->current_exception;
    284         defaultHandler( exception );
    285 }
    286 
    287 void __cfaehm_throw_terminate( exception_t * val, void (*defaultHandler)(exception_t *) ) {
    288         __cfadbg_print_safe(exception, "Throwing termination exception\n");
    289 
    290         __cfaehm_allocate_exception( val );
    291         __cfaehm_begin_unwind( defaultHandler );
    292 }
    293 
    294 static __attribute__((noreturn)) void __cfaehm_rethrow_adapter( exception_t * except ) {
    295         // TODO: Print some error message.
    296         (void)except;
     233        printf("UNWIND ERROR %d after raise exception\n", ret);
    297234        abort();
    298235}
    299236
    300 void __cfaehm_rethrow_terminate(void) {
    301         __cfadbg_print_safe(exception, "Rethrowing termination exception\n");
    302 
    303         __cfaehm_begin_unwind( __cfaehm_rethrow_adapter );
     237void __cfaabi_ehm__throw_terminate( exception_t * val ) {
     238        __cfaabi_dbg_print_safe("Throwing termination exception\n");
     239
     240        __cfaabi_ehm__allocate_exception( val );
     241        __cfaabi_ehm__begin_unwind();
     242}
     243
     244void __cfaabi_ehm__rethrow_terminate(void) {
     245        __cfaabi_dbg_print_safe("Rethrowing termination exception\n");
     246
     247        __cfaabi_ehm__begin_unwind();
     248}
     249
     250#if defined(PIC)
     251#warning Exceptions not yet supported when using Position-Independent Code
     252__attribute__((noinline))
     253void __cfaabi_ehm__try_terminate(void (*try_block)(),
     254                void (*catch_block)(int index, exception_t * except),
     255                __attribute__((unused)) int (*match_block)(exception_t * except)) {
    304256        abort();
    305257}
    306 
    307 #if defined( __x86_64 ) || defined( __i386 )
     258#else // PIC
    308259// This is our personality routine. For every stack frame annotated with
    309260// ".cfi_personality 0x3,__gcfa_personality_v0" this function will be called twice when unwinding.
    310261//  Once in the search phase and once in the cleanup phase.
    311 _Unwind_Reason_Code __gcfa_personality_v0(
    312                 int version,
    313                 _Unwind_Action actions,
    314                 unsigned long long exception_class,
    315                 struct _Unwind_Exception * unwind_exception,
    316                 struct _Unwind_Context * unwind_context)
     262_Unwind_Reason_Code __gcfa_personality_v0 (
     263                int version, _Unwind_Action actions, unsigned long long exceptionClass,
     264                struct _Unwind_Exception* unwind_exception,
     265                struct _Unwind_Context* context)
    317266{
    318267
    319         //__cfadbg_print_safe(exception, "CFA: 0x%lx\n", _Unwind_GetCFA(context));
    320         __cfadbg_print_safe(exception, "Personality function (%d, %x, %llu, %p, %p):",
    321                         version, actions, exception_class, unwind_exception, unwind_context);
    322 
    323         // Verify that actions follow the rules we expect.
    324         // This function should never be called at the end of the stack.
    325         verify(!(actions & _UA_END_OF_STACK));
    326         // Either only the search phase flag is set or...
     268        //__cfaabi_dbg_print_safe("CFA: 0x%lx\n", _Unwind_GetCFA(context));
     269        __cfaabi_dbg_print_safe("Personality function (%d, %x, %llu, %p, %p):",
     270                        version, actions, exceptionClass, unwind_exception, context);
     271
     272        // If we've reached the end of the stack then there is nothing much we can do...
     273        if( actions & _UA_END_OF_STACK ) return _URC_END_OF_STACK;
     274
    327275        if (actions & _UA_SEARCH_PHASE) {
    328                 verify(actions == _UA_SEARCH_PHASE);
    329                 __cfadbg_print_safe(exception, " lookup phase");
    330         // ... we are in clean-up phase.
    331         } else {
    332                 verify(actions & _UA_CLEANUP_PHASE);
    333                 __cfadbg_print_safe(exception, " cleanup phase");
    334                 // We shouldn't be the handler frame during forced unwind.
    335                 if (actions & _UA_HANDLER_FRAME) {
    336                         verify(!(actions & _UA_FORCE_UNWIND));
    337                         __cfadbg_print_safe(exception, " (handler frame)");
    338                 } else if (actions & _UA_FORCE_UNWIND) {
    339                         __cfadbg_print_safe(exception, " (force unwind)");
    340                 }
     276                __cfaabi_dbg_print_safe(" lookup phase");
     277        }
     278        else if (actions & _UA_CLEANUP_PHASE) {
     279                __cfaabi_dbg_print_safe(" cleanup phase");
     280        }
     281        // Just in case, probably can't actually happen
     282        else {
     283                printf(" error\n");
     284                return _URC_FATAL_PHASE1_ERROR;
    341285        }
    342286
    343287        // Get a pointer to the language specific data from which we will read what we need
    344         const unsigned char * lsd = _Unwind_GetLanguageSpecificData( unwind_context );
    345 
    346         if ( !lsd ) {   //Nothing to do, keep unwinding
     288        const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData( context );
     289
     290        if( !lsd ) {    //Nothing to do, keep unwinding
    347291                printf(" no LSD");
    348292                goto UNWIND;
     
    351295        // Get the instuction pointer and a reading pointer into the exception table
    352296        lsda_header_info lsd_info;
    353         const unsigned char * cur_ptr = parse_lsda_header(unwind_context, lsd, &lsd_info);
    354         _Unwind_Ptr instruction_ptr = _Unwind_GetIP(unwind_context);
    355 
    356         struct exception_context_t * context = this_exception_context();
     297        const unsigned char * cur_ptr = parse_lsda_header(context, lsd, &lsd_info);
     298        _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context );
    357299
    358300        // Linearly search the table for stuff to do
    359         while ( cur_ptr < lsd_info.action_table ) {
     301        while( cur_ptr < lsd_info.action_table ) {
    360302                _Unwind_Ptr callsite_start;
    361303                _Unwind_Ptr callsite_len;
     
    370312
    371313                // Have we reach the correct frame info yet?
    372                 if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {
     314                if( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {
    373315#ifdef __CFA_DEBUG_PRINT__
    374316                        void * ls = (void*)lsd_info.Start;
     
    378320                        void * ep = (void*)lsd_info.Start + callsite_start + callsite_len;
    379321                        void * ip = (void*)instruction_ptr;
    380                         __cfadbg_print_safe(exception, "\nfound %p - %p (%p, %p, %p), looking for %p\n",
     322                        __cfaabi_dbg_print_safe("\nfound %p - %p (%p, %p, %p), looking for %p\n",
    381323                                        bp, ep, ls, cs, cl, ip);
    382324#endif // __CFA_DEBUG_PRINT__
     
    385327
    386328                // Have we gone too far?
    387                 if ( lsd_info.Start + callsite_start > instruction_ptr ) {
     329                if( lsd_info.Start + callsite_start > instruction_ptr ) {
    388330                        printf(" gone too far");
    389331                        break;
    390332                }
    391333
    392                 // Check for what we must do:
    393                 if ( 0 == callsite_landing_pad ) {
    394                         // Nothing to do, move along
    395                         __cfadbg_print_safe(exception, " no landing pad");
    396                 } else if (actions & _UA_SEARCH_PHASE) {
    397                         // In search phase, these means we found a potential handler we must check.
    398 
    399                         // We have arbitrarily decided that 0 means nothing to do and 1 means there is
    400                         // a potential handler. This doesn't seem to conflict the gcc default behavior.
    401                         if (callsite_action != 0) {
    402                                 // Now we want to run some code to see if the handler matches
    403                                 // This is the tricky part where we want to the power to run arbitrary code
    404                                 // However, generating a new exception table entry and try routine every time
    405                                 // is way more expansive than we might like
    406                                 // The information we have is :
    407                                 //  - The GR (Series of registers)
    408                                 //    GR1=GP Global Pointer of frame ref by context
    409                                 //  - The instruction pointer
    410                                 //  - The instruction pointer info (???)
    411                                 //  - The CFA (Canonical Frame Address)
    412                                 //  - The BSP (Probably the base stack pointer)
    413 
    414                                 // The current apprach uses one exception table entry per try block
    415                                 _uleb128_t imatcher;
    416                                 // Get the relative offset to the {...}?
    417                                 cur_ptr = read_uleb128(cur_ptr, &imatcher);
    418 
    419                                 _Unwind_Word match_pos =
    420 #                               if defined( __x86_64 )
    421                                     _Unwind_GetCFA(unwind_context) + 8;
    422 #                               elif defined( __i386 )
    423                                     _Unwind_GetCFA(unwind_context) + 24;
    424 #                               elif defined( __ARM_ARCH )
    425 #                                   warning FIX ME: check if anything needed for ARM
    426                                     42;
    427 #                               endif
    428                                 int (*matcher)(exception_t *) = *(int(**)(exception_t *))match_pos;
    429 
    430                                 int index = matcher(context->current_exception);
    431                                 _Unwind_Reason_Code ret = (0 == index)
    432                                         ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND;
    433                                 UNWIND_TO_NODE(unwind_exception)->handler_index = index;
    434 
    435                                 // Based on the return value, check if we matched the exception
    436                                 if (ret == _URC_HANDLER_FOUND) {
    437                                         __cfadbg_print_safe(exception, " handler found\n");
    438                                 } else {
    439                                         // TODO: Continue the search if there is more in the table.
    440                                         __cfadbg_print_safe(exception, " no handler\n");
     334                // Something to do?
     335                if( callsite_landing_pad ) {
     336                        // Which phase are we in
     337                        if (actions & _UA_SEARCH_PHASE) {
     338                                // In search phase, these means we found a potential handler we must check.
     339
     340                                // We have arbitrarily decided that 0 means nothing to do and 1 means there is
     341                                // a potential handler. This doesn't seem to conflict the gcc default behavior.
     342                                if (callsite_action != 0) {
     343                                        // Now we want to run some code to see if the handler matches
     344                                        // This is the tricky part where we want to the power to run arbitrary code
     345                                        // However, generating a new exception table entry and try routine every time
     346                                        // is way more expansive than we might like
     347                                        // The information we have is :
     348                                        //  - The GR (Series of registers)
     349                                        //    GR1=GP Global Pointer of frame ref by context
     350                                        //  - The instruction pointer
     351                                        //  - The instruction pointer info (???)
     352                                        //  - The CFA (Canonical Frame Address)
     353                                        //  - The BSP (Probably the base stack pointer)
     354
     355
     356                                        // The current apprach uses one exception table entry per try block
     357                                        _uleb128_t imatcher;
     358                                        // Get the relative offset to the {...}?
     359                                        cur_ptr = read_uleb128(cur_ptr, &imatcher);
     360
     361                                        _Unwind_Reason_Code (*matcher)(exception_t *) =
     362                                                MATCHER_FROM_CONTEXT(context);
     363                                        int index = matcher(shared_stack.current_exception);
     364                                        _Unwind_Reason_Code ret = (0 == index)
     365                                                ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND;
     366                                        shared_stack.current_handler_index = index;
     367
     368                                        // Based on the return value, check if we matched the exception
     369                                        if( ret == _URC_HANDLER_FOUND) {
     370                                                __cfaabi_dbg_print_safe(" handler found\n");
     371                                        } else {
     372                                                __cfaabi_dbg_print_safe(" no handler\n");
     373                                        }
     374                                        return ret;
    441375                                }
    442                                 return ret;
     376
     377                                // This is only a cleanup handler, ignore it
     378                                __cfaabi_dbg_print_safe(" no action");
    443379                        }
    444 
    445                         // This is only a cleanup handler, ignore it
    446                         __cfadbg_print_safe(exception, " no action");
    447                 } else {
    448                         // In clean-up phase, no destructors here but this could be the handler.
    449 
    450                         if ( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){
    451                                 // If this is a potential exception handler
    452                                 // but not the one that matched the exception in the seach phase,
    453                                 // just ignore it
    454                                 goto UNWIND;
     380                        else if (actions & _UA_CLEANUP_PHASE) {
     381
     382                                if( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){
     383                                        // If this is a potential exception handler
     384                                        // but not the one that matched the exception in the seach phase,
     385                                        // just ignore it
     386                                        goto UNWIND;
     387                                }
     388
     389                                // We need to run some clean-up or a handler
     390                                // These statment do the right thing but I don't know any specifics at all
     391                                _Unwind_SetGR( context, __builtin_eh_return_data_regno(0), (_Unwind_Ptr) unwind_exception );
     392                                _Unwind_SetGR( context, __builtin_eh_return_data_regno(1), 0 );
     393
     394                                // I assume this sets the instruction pointer to the adress of the landing pad
     395                                // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT
     396                                _Unwind_SetIP( context, ((lsd_info.LPStart) + (callsite_landing_pad)) );
     397
     398                                __cfaabi_dbg_print_safe(" action\n");
     399
     400                                // Return have some action to run
     401                                return _URC_INSTALL_CONTEXT;
    455402                        }
    456 
    457                         // We need to run some clean-up or a handler
    458                         // These statment do the right thing but I don't know any specifics at all
    459                         _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(0),
    460                                 (_Unwind_Ptr)unwind_exception );
    461                         _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(1), 0 );
    462 
    463                         // I assume this sets the instruction pointer to the adress of the landing pad
    464                         // It doesn't actually set it, it only state the value that needs to be set once we
    465                         // return _URC_INSTALL_CONTEXT
    466                         _Unwind_SetIP( unwind_context, ((lsd_info.LPStart) + (callsite_landing_pad)) );
    467 
    468                         __cfadbg_print_safe(exception, " action\n");
    469 
    470                         // Return have some action to run
    471                         return _URC_INSTALL_CONTEXT;
    472403                }
     404
     405                // Nothing to do, move along
     406                __cfaabi_dbg_print_safe(" no landing pad");
    473407        }
    474408        // No handling found
    475         __cfadbg_print_safe(exception, " table end reached");
     409        __cfaabi_dbg_print_safe(" table end reached\n");
    476410
    477411        UNWIND:
    478         __cfadbg_print_safe(exception, " unwind\n");
     412        __cfaabi_dbg_print_safe(" unwind\n");
    479413
    480414        // Keep unwinding the stack
    481415        return _URC_CONTINUE_UNWIND;
    482416}
    483 
    484 #pragma GCC push_options
    485 #pragma GCC optimize(0)
    486417
    487418// Try statements are hoisted out see comments for details. While this could probably be unique
    488419// and simply linked from libcfa but there is one problem left, see the exception table for details
    489420__attribute__((noinline))
    490 void __cfaehm_try_terminate(void (*try_block)(),
     421void __cfaabi_ehm__try_terminate(void (*try_block)(),
    491422                void (*catch_block)(int index, exception_t * except),
    492423                __attribute__((unused)) int (*match_block)(exception_t * except)) {
     
    494425        //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy);
    495426
     427        // Setup statments: These 2 statments won't actually result in any code, they only setup global tables.
     428        // However, they clobber gcc cancellation support from gcc.  We can replace the personality routine but
     429        // replacing the exception table gcc generates is not really doable, it generates labels based on how the
     430        // assembly works.
     431
    496432        // Setup the personality routine and exception table.
    497         // Unforturnately these clobber gcc cancellation support which means we can't get access to
    498         // the attribute cleanup tables at the same time. We would have to inspect the assembly to
    499         // create a new set ourselves.
    500 #ifdef __PIC__
    501         asm volatile (".cfi_personality 0x9b,CFA.ref.__gcfa_personality_v0");
    502         asm volatile (".cfi_lsda 0x1b, .LLSDACFA2");
    503 #else
    504433        asm volatile (".cfi_personality 0x3,__gcfa_personality_v0");
    505434        asm volatile (".cfi_lsda 0x3, .LLSDACFA2");
    506 #endif
    507435
    508436        // Label which defines the start of the area for which the handler is setup.
     
    522450        // Label which defines the end of the area for which the handler is setup.
    523451        asm volatile (".TRYEND:");
    524         // Label which defines the start of the exception landing pad. Basically what is called when
    525         // the exception is caught. Note, if multiple handlers are given, the multiplexing should be
    526         // done by the generated code, not the exception runtime.
     452        // Label which defines the start of the exception landing pad.  Basically what is called when the exception is
     453        // caught.  Note, if multiple handlers are given, the multiplexing should be done by the generated code, not the
     454        // exception runtime.
    527455        asm volatile (".CATCH:");
    528456
    529457        // Exception handler
    530         // Note: Saving the exception context on the stack breaks termination exceptions.
    531         catch_block( EXCEPT_TO_NODE( this_exception_context()->current_exception )->handler_index,
    532                      this_exception_context()->current_exception );
     458        catch_block( shared_stack.current_handler_index,
     459                     shared_stack.current_exception );
    533460}
    534461
     
    537464// have a single call to the try routine.
    538465
    539 #ifdef __PIC__
    540 asm (
    541         // HEADER
    542         ".LFECFA1:\n"
    543         "       .globl  __gcfa_personality_v0\n"
    544         "       .section        .gcc_except_table,\"a\",@progbits\n"
    545         // TABLE HEADER (important field is the BODY length at the end)
    546         ".LLSDACFA2:\n"
    547         "       .byte   0xff\n"
    548         "       .byte   0xff\n"
    549         "       .byte   0x1\n"
    550         "       .uleb128 .LLSDACSECFA2-.LLSDACSBCFA2\n"
    551         // BODY (language specific data)
    552         // This uses language specific data and can be modified arbitrarily
    553         // We use handled area offset, handled area length,
    554         // handler landing pad offset and 1 (action code, gcc seems to use 0).
    555         ".LLSDACSBCFA2:\n"
    556         "       .uleb128 .TRYSTART-__cfaehm_try_terminate\n"
    557         "       .uleb128 .TRYEND-.TRYSTART\n"
    558         "       .uleb128 .CATCH-__cfaehm_try_terminate\n"
    559         "       .uleb128 1\n"
    560         ".LLSDACSECFA2:\n"
    561         // TABLE FOOTER
    562         "       .text\n"
    563         "       .size   __cfaehm_try_terminate, .-__cfaehm_try_terminate\n"
    564 );
    565 
    566 // Somehow this piece of helps with the resolution of debug symbols.
    567 __attribute__((unused)) static const int dummy = 0;
    568 
    569 asm (
    570         // Add a hidden symbol which points at the function.
    571         "       .hidden CFA.ref.__gcfa_personality_v0\n"
    572         "       .weak   CFA.ref.__gcfa_personality_v0\n"
    573         // No clue what this does specifically
    574         "       .section        .data.rel.local.CFA.ref.__gcfa_personality_v0,\"awG\",@progbits,CFA.ref.__gcfa_personality_v0,comdat\n"
    575         "       .align 8\n"
    576         "       .type CFA.ref.__gcfa_personality_v0, @object\n"
    577         "       .size CFA.ref.__gcfa_personality_v0, 8\n"
    578         "CFA.ref.__gcfa_personality_v0:\n"
    579 #if defined( __x86_64 )
    580         "       .quad __gcfa_personality_v0\n"
    581 #else // then __i386
    582         "       .long __gcfa_personality_v0\n"
    583 #endif
    584 );
    585 #else // __PIC__
     466#if defined( __i386 ) || defined( __x86_64 )
    586467asm (
    587468        // HEADER
     
    598479        ".LLSDACSBCFA2:\n"
    599480        //      Handled area start (relative to start of function)
    600         "       .uleb128 .TRYSTART-__cfaehm_try_terminate\n"
     481        "       .uleb128 .TRYSTART-__cfaabi_ehm__try_terminate\n"
    601482        //      Handled area length
    602483        "       .uleb128 .TRYEND-.TRYSTART\n"
    603484        //      Handler landing pad address (relative to start of function)
    604         "       .uleb128 .CATCH-__cfaehm_try_terminate\n"
     485        "       .uleb128 .CATCH-__cfaabi_ehm__try_terminate\n"
    605486        //      Action code, gcc seems to always use 0.
    606487        "       .uleb128 1\n"
     
    608489        ".LLSDACSECFA2:\n"
    609490        "       .text\n"
    610         "       .size   __cfaehm_try_terminate, .-__cfaehm_try_terminate\n"
     491        "       .size   __cfaabi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n"
    611492        "       .ident  \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n"
    612         "       .section        .note.GNU-stack,\"x\",@progbits\n"
     493//      "       .section        .note.GNU-stack,\"x\",@progbits\n"
    613494);
    614 #endif // __PIC__
    615 
    616 #pragma GCC pop_options
    617 
    618 #elif defined( __ARM_ARCH )
    619 _Unwind_Reason_Code __gcfa_personality_v0(
    620                 int version,
    621                 _Unwind_Action actions,
    622                 unsigned long long exception_class,
    623                 struct _Unwind_Exception * unwind_exception,
    624                 struct _Unwind_Context * unwind_context) {
    625         return _URC_CONTINUE_UNWIND;
    626 }
    627 
    628 __attribute__((noinline))
    629 void __cfaehm_try_terminate(void (*try_block)(),
    630                 void (*catch_block)(int index, exception_t * except),
    631                 __attribute__((unused)) int (*match_block)(exception_t * except)) {
    632 }
    633 #else
    634         #error unsupported hardware architecture
    635 #endif // __x86_64 || __i386
     495#endif // __i386 || __x86_64
     496#endif // PIC
Note: See TracChangeset for help on using the changeset viewer.