Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/libcfa/exception.c

    rcbce272 r38ac6ec  
    1010// Created On       : Mon Jun 26 15:13:00 2017
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Aug  4 15:20:00 2017
    13 // Update Count     : 6
    14 //
    15 
    16 #include <stddef.h> // for size_t
     12// Last Modified On : Tus Jul 11 16:36:00 2017
     13// Update Count     : 1
     14//
    1715
    1816#include "exception.h"
     
    2422#include <unwind.h>
    2523
    26 // FIX ME: temporary hack to keep ARM build working
    27 #ifndef _URC_FATAL_PHASE1_ERROR
    28 #define _URC_FATAL_PHASE1_ERROR 2
    29 #endif // ! _URC_FATAL_PHASE1_ERROR
    30 #ifndef _URC_FATAL_PHASE2_ERROR
    31 #define _URC_FATAL_PHASE2_ERROR 2
    32 #endif // ! _URC_FATAL_PHASE2_ERROR
    33 
    3424#include "lsda.h"
    3525
    36 
    37 // Base exception vtable is abstract, you should not have base exceptions.
    38 struct __cfaehm__base_exception_t_vtable
    39                 ___cfaehm__base_exception_t_vtable_instance = {
    40         .parent = NULL,
    41         .size = 0,
    42         .copy = NULL,
    43         .free = NULL,
    44         .msg = NULL
    45 };
    46 
    47 
    4826// Temperary global exception context. Does not work with concurency.
    49 struct exception_context_t {
     27struct shared_stack_t {
    5028    struct __cfaehm__try_resume_node * top_resume;
    5129    struct __cfaehm__try_resume_node * current_resume;
    5230
    53     exception * current_exception;
     31    exception current_exception;
    5432    int current_handler_index;
    5533} shared_stack = {NULL, NULL, 0, 0};
    5634
    57 // Get the current exception context.
    58 // There can be a single global until multithreading occurs, then each stack
    59 // needs its own. It will have to be updated to handle that.
    60 struct exception_context_t * this_exception_context() {
    61         return &shared_stack;
    62 }
    63 //#define SAVE_EXCEPTION_CONTEXT(to_name)
    64 //struct exception_context_t * to_name = this_exception_context();
    65 //exception * this_exception() {
    66 //    return this_exception_context()->current_exception;
    67 //}
    68 
    69 
    70 // This macro should be the only thing that needs to change across machines.  Used in the personality function, way down
    71 // in termination.
     35
     36
     37// This macro should be the only thing that needs to change across machines.
     38// Used in the personality function, way down in termination.
    7239// struct _Unwind_Context * -> _Unwind_Reason_Code(*)(exception *)
    7340#define MATCHER_FROM_CONTEXT(ptr_to_context) \
     
    8047
    8148        // DEBUG
    82         printf("Throwing resumption exception\n");
     49        printf("Throwing resumption exception %d\n", *except);
    8350
    8451        struct __cfaehm__try_resume_node * original_head = shared_stack.current_resume;
     
    9461        }
    9562
    96         printf("Unhandled exception\n");
     63        printf("Unhandled exception %d\n", *except);
    9764        shared_stack.current_resume = original_head;
    9865
     
    10269}
    10370
    104 // Do we control where exceptions get thrown even with concurency?  If not these are not quite thread safe, the cleanup
    105 // hook has to be added after the node is built but before it is made the top node.
    106 
     71/* Do we control where exceptions get thrown even with concurency?
     72 * If not these are not quite thread safe, the cleanup hook has to be added
     73 * after the node is built but before it is made the top node.
     74 */
    10775void __cfaehm__try_resume_setup(struct __cfaehm__try_resume_node * node,
    10876                        int (*handler)(exception * except)) {
     
    11987// TERMINATION ===============================================================
    12088
    121 // MEMORY MANAGEMENT (still for integers)
    122 // May have to move to cfa for constructors and destructors (references).
    123 
    124 struct __cfaehm__node {
    125         struct __cfaehm__node * next;
    126 };
    127 
    128 #define NODE_TO_EXCEPT(node) ((exception *)(1 + (node)))
    129 #define EXCEPT_TO_NODE(except) ((struct __cfaehm__node *)(except) - 1)
    130 
    131 // Creates a copy of the indicated exception and sets current_exception to it.
    132 static void __cfaehm__allocate_exception( exception * except ) {
    133         struct exception_context_t * context = this_exception_context();
    134 
    135         // Allocate memory for the exception.
    136         struct __cfaehm__node * store = malloc(
    137                 sizeof( struct __cfaehm__node ) + except->virtual_table->size );
    138 
    139         if ( ! store ) {
    140                 // Failure: cannot allocate exception. Terminate thread.
    141                 abort(); // <- Although I think it might be the process.
    142         }
    143 
    144         // Add the node to the list:
    145         store->next = EXCEPT_TO_NODE(context->current_exception);
    146         context->current_exception = NODE_TO_EXCEPT(store);
    147 
    148         // Copy the exception to storage.
    149         except->virtual_table->copy( context->current_exception, except );
    150 }
    151 
    152 // Delete the provided exception, unsetting current_exception if relivant.
    153 static void __cfaehm__delete_exception( exception * except ) {
    154         struct exception_context_t * context = this_exception_context();
    155 
    156         // DEBUG
    157         printf( "Deleting Exception\n");
    158 
    159         // Remove the exception from the list.
    160         struct __cfaehm__node * to_free = EXCEPT_TO_NODE(except);
    161         struct __cfaehm__node * node;
    162 
    163         if ( context->current_exception == except ) {
    164                 node = to_free->next;
    165                 context->current_exception = (node) ? NODE_TO_EXCEPT(node) : 0;
    166         } else {
    167                 node = EXCEPT_TO_NODE(context->current_exception);
    168                 // It may always be in the first or second position.
    169                 while( to_free != node->next ) {
    170                         node = node->next;
    171                 }
    172                 node->next = to_free->next;
    173         }
    174 
    175         // Free the old exception node.
    176         except->virtual_table->free( except );
    177         free( to_free );
    178 }
    179 
    180 // If this isn't a rethrow (*except==0), delete the provided exception.
    181 void __cfaehm__cleanup_terminate( void * except ) {
    182         if ( *(void**)except ) __cfaehm__delete_exception( *(exception**)except );
    183 }
    184 
     89// Requires -fexceptions to work.
     90
     91// Global which defines the current exception
     92// Currently an int just to make matching easier
     93//int this_exception; (became shared_stack.current_exception)
    18594
    18695// We need a piece of storage to raise the exception
     
    202111}
    203112
    204 // The exception that is being thrown must already be stored.
    205 __attribute__((noreturn)) void __cfaehm__begin_unwind(void) {
    206         if ( ! this_exception_context()->current_exception ) {
    207                 printf("UNWIND ERROR missing exception in begin unwind\n");
    208                 abort();
    209         }
    210 
     113void __cfaehm__throw_terminate( exception * val ) {
     114        // Store the current exception
     115        shared_stack.current_exception = *val;
     116
     117        // DEBUG
     118        printf("Throwing termination exception %d\n", *val);
    211119
    212120        // Call stdlibc to raise the exception
    213121        _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage );
    214122
    215         // If we reach here it means something happened.  For resumption to work we need to find a way to return back to
    216         // here.  Most of them will probably boil down to setting a global flag and making the phase 1 either stop or
    217         // fail.  Causing an error on purpose may help avoiding unnecessary work but it might have some weird side
    218         // effects.  If we just pretend no handler was found that would work but may be expensive for no reason since we
    219         // will always search the whole stack.
     123        // If we reach here it means something happened
     124        // For resumption to work we need to find a way to return back to here
     125        // Most of them will probably boil down to setting a global flag and making the phase 1 either stop or fail.
     126        // Causing an error on purpose may help avoiding unnecessary work but it might have some weird side effects.
     127        // If we just pretend no handler was found that would work but may be expensive for no reason since we will always
     128        // search the whole stack
    220129
    221130        if( ret == _URC_END_OF_STACK ) {
    222                 // No proper handler was found.  This can be handled in several way.  C++ calls std::terminate Here we
    223                 // force unwind the stack, basically raising a cancellation.
     131                // No proper handler was found
     132                // This can be handled in several way
     133                // C++ calls std::terminate
     134                // Here we force unwind the stack, basically raising a cancellation
    224135                printf("Uncaught exception %p\n", &this_exception_storage);
    225136
     
    229140        }
    230141
    231         // We did not simply reach the end of the stack without finding a handler.  Something wen't wrong
     142        // We did not simply reach the end of the stack without finding a handler,
     143        // Something wen't wrong
    232144        printf("UNWIND ERROR %d after raise exception\n", ret);
    233145        abort();
    234146}
    235147
    236 void __cfaehm__throw_terminate( exception * val ) {
    237         // DEBUG
    238         printf("Throwing termination exception\n");
    239 
    240         __cfaehm__allocate_exception( val );
    241         __cfaehm__begin_unwind();
    242 }
    243 
     148// Nesting this the other way would probably be faster.
    244149void __cfaehm__rethrow_terminate(void) {
    245150        // DEBUG
    246151        printf("Rethrowing termination exception\n");
    247152
    248         __cfaehm__begin_unwind();
    249 }
    250 
    251 // This is our personality routine.  For every stack frame anotated with ".cfi_personality 0x3,__gcfa_personality_v0".
    252 // This function will be called twice when unwinding.  Once in the search phased and once in the cleanup phase.
     153        __cfaehm__throw_terminate(&shared_stack.current_exception);
     154}
     155
     156// This is our personality routine
     157// For every stack frame anotated with ".cfi_personality 0x3,__gcfa_personality_v0"
     158// This function will be called twice when unwinding
     159// Once in the search phased and once in the cleanup phase
    253160_Unwind_Reason_Code __gcfa_personality_v0 (
    254161                int version, _Unwind_Action actions, unsigned long long exceptionClass,
     
    356263                                        _Unwind_Reason_Code (*matcher)(exception *) =
    357264                                                MATCHER_FROM_CONTEXT(context);
    358                                         int index = matcher(shared_stack.current_exception);
     265                                        int index = matcher(&shared_stack.current_exception);
    359266                                        _Unwind_Reason_Code ret = (0 == index)
    360267                                                ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND;
     
    386293                                // I assume this sets the instruction pointer to the adress of the landing pad
    387294                                // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT
    388                                 _Unwind_SetIP( context, ((lsd_info.LPStart) + (callsite_landing_pad)) );
     295                                _Unwind_SetIP( context, lsd_info.LPStart + callsite_landing_pad );
    389296
    390297                                // DEBUG
     
    410317}
    411318
    412 // Try statements are hoisted out see comments for details.  With this could probably be unique and simply linked from
    413 // libcfa but there is one problem left, see the exception table for details
     319// Try statements are hoisted out see comments for details
     320// With this could probably be unique and simply linked from
     321// libcfa but there is one problem left, see the exception table
     322// for details
    414323__attribute__((noinline))
    415324void __cfaehm__try_terminate(void (*try_block)(),
     
    419328        //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy);
    420329
    421         // Setup statments: These 2 statments won't actually result in any code, they only setup global tables.
    422         // However, they clobber gcc cancellation support from gcc.  We can replace the personality routine but
    423         // replacing the exception table gcc generates is not really doable, it generates labels based on how the
    424         // assembly works.
    425 
     330        // Setup statments
     331        // These 2 statments won't actually result in any code,
     332        // they only setup global tables.
     333        // However, they clobber gcc cancellation support from gcc.
     334        // We can replace the personality routine but replacing the exception
     335        // table gcc generates is not really doable, it generates labels based
     336        // on how the assembly works.
    426337        // Setup the personality routine
    427338        asm volatile (".cfi_personality 0x3,__gcfa_personality_v0");
     
    429340        asm volatile (".cfi_lsda 0x3, .LLSDACFA2");
    430341
    431         // Label which defines the start of the area for which the handler is setup.
     342        // Label which defines the start of the area for which the handler is setup
    432343        asm volatile (".TRYSTART:");
    433344
     
    443354        // Exceptionnal path
    444355        CATCH : __attribute__(( unused ));
    445         // Label which defines the end of the area for which the handler is setup.
     356        // Label which defines the end of the area for which the handler is setup
    446357        asm volatile (".TRYEND:");
    447         // Label which defines the start of the exception landing pad.  Basically what is called when the exception is
    448         // caught.  Note, if multiple handlers are given, the multiplexing should be done by the generated code, not the
    449         // exception runtime.
     358        // Label which defines the start of the exception landing pad
     359        // basically what will be called when the exception is caught
     360        // Note, if multiple handlers are given, the multiplexing should be done
     361        // by the generated code, not the exception runtime
    450362        asm volatile (".CATCH:");
    451363
    452364        // Exception handler
    453         catch_block( shared_stack.current_handler_index,
    454                      shared_stack.current_exception );
    455 }
    456 
    457 // Exception table data we need to generate.  While this is almost generic, the custom data refers to foo_try_match try
    458 // match, which is no way generic.  Some more works need to be done if we want to have a single call to the try routine.
    459 
    460 #if defined( __x86_64__ ) || defined( __i386__ )
     365        catch_block(shared_stack.current_handler_index,
     366                    &shared_stack.current_exception);
     367}
     368
     369// Exception table data we need to generate
     370// While this is almost generic, the custom data refers to
     371// foo_try_match try match, which is no way generic
     372// Some more works need to be done if we want to have a single
     373// call to the try routine
    461374asm (
    462375        //HEADER
     
    481394//      "       .section        .note.GNU-stack,\"x\",@progbits\n"
    482395);
    483 #endif // __x86_64__ || __i386__
Note: See TracChangeset for help on using the changeset viewer.