Ignore:
Timestamp:
Jan 7, 2021, 2:55:57 PM (5 years ago)
Author:
Thierry Delisle <tdelisle@…>
Branches:
ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
58fe85a
Parents:
bdfc032 (diff), 44e37ef (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' into dkobets-vector

File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/exception.c

    rbdfc032 reef8dfb  
    99// Author           : Andrew Beach
    1010// Created On       : Mon Jun 26 15:13:00 2017
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Feb 22 18:17:34 2018
    13 // Update Count     : 11
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tue Oct 27 16:27:00 2020
     13// Update Count     : 35
    1414//
    1515
     16// Normally we would get this from the CFA prelude.
    1617#include <stddef.h> // for size_t
    1718
     19#include <unwind.h> // for struct _Unwind_Exception {...};
     20
    1821#include "exception.h"
    19 
    20 // Implementation of the secret header.
    2122
    2223#include <stdlib.h>
    2324#include <stdio.h>
    24 #include <unwind.h>
    2525#include <bits/debug.hfa>
    26 
    27 // FIX ME: temporary hack to keep ARM build working
     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
    2831#ifndef _URC_FATAL_PHASE1_ERROR
    29 #define _URC_FATAL_PHASE1_ERROR 2
     32#define _URC_FATAL_PHASE1_ERROR 3
    3033#endif // ! _URC_FATAL_PHASE1_ERROR
    3134#ifndef _URC_FATAL_PHASE2_ERROR
    3235#define _URC_FATAL_PHASE2_ERROR 2
    3336#endif // ! _URC_FATAL_PHASE2_ERROR
     37#endif // __ARM_ARCH
    3438
    3539#include "lsda.h"
    3640
     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 */
     46const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643;
    3747
    3848// Base exception vtable is abstract, you should not have base exceptions.
    39 struct __cfaabi_ehm__base_exception_t_vtable
    40                 ___cfaabi_ehm__base_exception_t_vtable_instance = {
     49struct __cfaehm_base_exception_t_vtable
     50                ___cfaehm_base_exception_t_vtable_instance = {
    4151        .parent = NULL,
    4252        .size = 0,
     
    4757
    4858
    49 // Temperary global exception context. Does not work with concurency.
    50 struct 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 
    5859// Get the current exception context.
    5960// There can be a single global until multithreading occurs, then each stack
    60 // needs its own. It will have to be updated to handle that.
    61 struct exception_context_t * this_exception_context() {
     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};
    6264        return &shared_stack;
    6365}
    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))
    7666
    7767
    7868// RESUMPTION ================================================================
    7969
    80 void __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;
     70static void reset_top_resume(struct __cfaehm_try_resume_node ** store) {
     71        this_exception_context()->top_resume = *store;
     72}
     73
     74void __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                        }
    9389                }
    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.
     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);
    10295}
    10396
     
    10699// be added after the node is built but before it is made the top node.
    107100
    108 void __cfaabi_ehm__try_resume_setup(struct __cfaabi_ehm__try_resume_node * node,
     101void __cfaehm_try_resume_setup(struct __cfaehm_try_resume_node * node,
    109102                        _Bool (*handler)(exception_t * except)) {
    110         node->next = shared_stack.top_resume;
     103        struct exception_context_t * context = this_exception_context();
     104        node->next = context->top_resume;
    111105        node->handler = handler;
    112         shared_stack.top_resume = node;
    113 }
    114 
    115 void __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 
    125 struct __cfaabi_ehm__node {
    126         struct __cfaabi_ehm__node * next;
    127 };
     106        context->top_resume = node;
     107}
     108
     109void __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 =========================================================
    128116
    129117#define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node)))
    130 #define EXCEPT_TO_NODE(except) ((struct __cfaabi_ehm__node *)(except) - 1)
     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.
     123static 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}
    131135
    132136// Creates a copy of the indicated exception and sets current_exception to it.
    133 static void __cfaabi_ehm__allocate_exception( exception_t * except ) {
     137static void __cfaehm_allocate_exception( exception_t * except ) {
    134138        struct exception_context_t * context = this_exception_context();
    135139
    136140        // Allocate memory for the exception.
    137         struct __cfaabi_ehm__node * store = malloc(
    138                 sizeof( struct __cfaabi_ehm__node ) + except->virtual_table->size );
     141        struct __cfaehm_node * store = malloc(
     142                sizeof( struct __cfaehm_node ) + except->virtual_table->size );
    139143
    140144        if ( ! store ) {
     
    143147        }
    144148
     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
    145156        // Add the node to the list:
    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 );
     157        store->next = NULL_MAP(EXCEPT_TO_NODE, context->current_exception);
     158        context->current_exception = except_store;
    151159}
    152160
    153161// Delete the provided exception, unsetting current_exception if relivant.
    154 static void __cfaabi_ehm__delete_exception( exception_t * except ) {
    155         struct exception_context_t * context = this_exception_context();
    156 
    157         __cfaabi_dbg_print_safe("Deleting Exception\n");
     162static void __cfaehm_delete_exception( exception_t * except ) {
     163        struct exception_context_t * context = this_exception_context();
     164
     165        __cfadbg_print_safe(exception, "Deleting Exception\n");
    158166
    159167        // Remove the exception from the list.
    160         struct __cfaabi_ehm__node * to_free = EXCEPT_TO_NODE(except);
    161         struct __cfaabi_ehm__node * node;
     168        struct __cfaehm_node * to_free = EXCEPT_TO_NODE(except);
     169        struct __cfaehm_node * node;
    162170
    163171        if ( context->current_exception == except ) {
    164172                node = to_free->next;
    165                 context->current_exception = (node) ? NODE_TO_EXCEPT(node) : 0;
     173                context->current_exception = NULL_MAP(NODE_TO_EXCEPT, node);
    166174        } else {
    167175                node = EXCEPT_TO_NODE(context->current_exception);
    168176                // It may always be in the first or second position.
    169                 while( to_free != node->next ) {
     177                while ( to_free != node->next ) {
    170178                        node = node->next;
    171179                }
     
    178186}
    179187
    180 // If this isn't a rethrow (*except==0), delete the provided exception.
    181 void __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
    187 struct _Unwind_Exception this_exception_storage;
     188// CANCELLATION ==============================================================
    188189
    189190// Function needed by force unwind
     
    192193                int version,
    193194                _Unwind_Action actions,
    194                 _Unwind_Exception_Class exceptionClass,
     195                _Unwind_Exception_Class exception_class,
    195196                struct _Unwind_Exception * unwind_exception,
    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;
     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.
     218void __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.
     243void __cfaehm_cleanup_terminate( void * except ) {
     244        if ( *(void**)except ) __cfaehm_delete_exception( *(exception_t **)except );
     245}
     246
     247static void __cfaehm_cleanup_default( exception_t ** except ) {
     248        __cfaehm_delete_exception( *except );
     249        *except = NULL;
    202250}
    203251
    204252// The exception that is being thrown must already be stored.
    205 __attribute__((noreturn)) void __cfaabi_ehm__begin_unwind(void) {
    206         if ( ! this_exception_context()->current_exception ) {
     253static void __cfaehm_begin_unwind(void(*defaultHandler)(exception_t *)) {
     254        struct exception_context_t * context = this_exception_context();
     255        if ( NULL == context->current_exception ) {
    207256                printf("UNWIND ERROR missing exception in begin unwind\n");
    208257                abort();
    209258        }
    210 
     259        struct _Unwind_Exception * storage =
     260                &EXCEPT_TO_NODE(context->current_exception)->unwind_exception;
    211261
    212262        // Call stdlibc to raise the exception
    213         _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage );
     263        __cfadbg_print_safe(exception, "Begin unwinding (storage &p, context %p)\n", storage, context);
     264        _Unwind_Reason_Code ret = _Unwind_RaiseException( storage );
    214265
    215266        // If we reach here it means something happened. For resumption to work we need to find a way
     
    220271        // the whole stack.
    221272
    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);
     273        // 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);
    229276                abort();
    230277        }
    231278
    232         // We did not simply reach the end of the stack without finding a handler. This is an error.
    233         printf("UNWIND ERROR %d after raise exception\n", ret);
     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
     287void __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
     294static __attribute__((noreturn)) void __cfaehm_rethrow_adapter( exception_t * except ) {
     295        // TODO: Print some error message.
     296        (void)except;
    234297        abort();
    235298}
    236299
    237 void __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 
    244 void __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))
    253 void __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)) {
     300void __cfaehm_rethrow_terminate(void) {
     301        __cfadbg_print_safe(exception, "Rethrowing termination exception\n");
     302
     303        __cfaehm_begin_unwind( __cfaehm_rethrow_adapter );
    256304        abort();
    257305}
    258 #else // PIC
     306
     307#if defined( __x86_64 ) || defined( __i386 )
    259308// This is our personality routine. For every stack frame annotated with
    260309// ".cfi_personality 0x3,__gcfa_personality_v0" this function will be called twice when unwinding.
    261310//  Once in the search phase and once in the cleanup phase.
    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)
     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)
    266317{
    267318
    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 
     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...
    275327        if (actions & _UA_SEARCH_PHASE) {
    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;
     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                }
    285341        }
    286342
    287343        // Get a pointer to the language specific data from which we will read what we need
    288         const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData( context );
    289 
    290         if( !lsd ) {    //Nothing to do, keep unwinding
     344        const unsigned char * lsd = _Unwind_GetLanguageSpecificData( unwind_context );
     345
     346        if ( !lsd ) {   //Nothing to do, keep unwinding
    291347                printf(" no LSD");
    292348                goto UNWIND;
     
    295351        // Get the instuction pointer and a reading pointer into the exception table
    296352        lsda_header_info lsd_info;
    297         const unsigned char * cur_ptr = parse_lsda_header(context, lsd, &lsd_info);
    298         _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context );
     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();
    299357
    300358        // Linearly search the table for stuff to do
    301         while( cur_ptr < lsd_info.action_table ) {
     359        while ( cur_ptr < lsd_info.action_table ) {
    302360                _Unwind_Ptr callsite_start;
    303361                _Unwind_Ptr callsite_len;
     
    312370
    313371                // Have we reach the correct frame info yet?
    314                 if( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {
     372                if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {
    315373#ifdef __CFA_DEBUG_PRINT__
    316374                        void * ls = (void*)lsd_info.Start;
     
    320378                        void * ep = (void*)lsd_info.Start + callsite_start + callsite_len;
    321379                        void * ip = (void*)instruction_ptr;
    322                         __cfaabi_dbg_print_safe("\nfound %p - %p (%p, %p, %p), looking for %p\n",
     380                        __cfadbg_print_safe(exception, "\nfound %p - %p (%p, %p, %p), looking for %p\n",
    323381                                        bp, ep, ls, cs, cl, ip);
    324382#endif // __CFA_DEBUG_PRINT__
     
    327385
    328386                // Have we gone too far?
    329                 if( lsd_info.Start + callsite_start > instruction_ptr ) {
     387                if ( lsd_info.Start + callsite_start > instruction_ptr ) {
    330388                        printf(" gone too far");
    331389                        break;
    332390                }
    333391
    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;
     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");
    375441                                }
    376 
    377                                 // This is only a cleanup handler, ignore it
    378                                 __cfaabi_dbg_print_safe(" no action");
     442                                return ret;
    379443                        }
    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;
     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;
    402455                        }
     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;
    403472                }
    404 
    405                 // Nothing to do, move along
    406                 __cfaabi_dbg_print_safe(" no landing pad");
    407473        }
    408474        // No handling found
    409         __cfaabi_dbg_print_safe(" table end reached\n");
     475        __cfadbg_print_safe(exception, " table end reached");
    410476
    411477        UNWIND:
    412         __cfaabi_dbg_print_safe(" unwind\n");
     478        __cfadbg_print_safe(exception, " unwind\n");
    413479
    414480        // Keep unwinding the stack
    415481        return _URC_CONTINUE_UNWIND;
    416482}
     483
     484#pragma GCC push_options
     485#pragma GCC optimize(0)
    417486
    418487// Try statements are hoisted out see comments for details. While this could probably be unique
    419488// and simply linked from libcfa but there is one problem left, see the exception table for details
    420489__attribute__((noinline))
    421 void __cfaabi_ehm__try_terminate(void (*try_block)(),
     490void __cfaehm_try_terminate(void (*try_block)(),
    422491                void (*catch_block)(int index, exception_t * except),
    423492                __attribute__((unused)) int (*match_block)(exception_t * except)) {
     
    425494        //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy);
    426495
    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 
    432496        // 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
    433504        asm volatile (".cfi_personality 0x3,__gcfa_personality_v0");
    434505        asm volatile (".cfi_lsda 0x3, .LLSDACFA2");
     506#endif
    435507
    436508        // Label which defines the start of the area for which the handler is setup.
     
    450522        // Label which defines the end of the area for which the handler is setup.
    451523        asm volatile (".TRYEND:");
    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.
     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.
    455527        asm volatile (".CATCH:");
    456528
    457529        // Exception handler
    458         catch_block( shared_stack.current_handler_index,
    459                      shared_stack.current_exception );
     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 );
    460533}
    461534
     
    464537// have a single call to the try routine.
    465538
    466 #if defined( __i386 ) || defined( __x86_64 )
     539#ifdef __PIC__
     540asm (
     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
     569asm (
     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__
    467586asm (
    468587        // HEADER
     
    479598        ".LLSDACSBCFA2:\n"
    480599        //      Handled area start (relative to start of function)
    481         "       .uleb128 .TRYSTART-__cfaabi_ehm__try_terminate\n"
     600        "       .uleb128 .TRYSTART-__cfaehm_try_terminate\n"
    482601        //      Handled area length
    483602        "       .uleb128 .TRYEND-.TRYSTART\n"
    484603        //      Handler landing pad address (relative to start of function)
    485         "       .uleb128 .CATCH-__cfaabi_ehm__try_terminate\n"
     604        "       .uleb128 .CATCH-__cfaehm_try_terminate\n"
    486605        //      Action code, gcc seems to always use 0.
    487606        "       .uleb128 1\n"
     
    489608        ".LLSDACSECFA2:\n"
    490609        "       .text\n"
    491         "       .size   __cfaabi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n"
     610        "       .size   __cfaehm_try_terminate, .-__cfaehm_try_terminate\n"
    492611        "       .ident  \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n"
    493 //      "       .section        .note.GNU-stack,\"x\",@progbits\n"
     612        "       .section        .note.GNU-stack,\"x\",@progbits\n"
    494613);
    495 #endif // __i386 || __x86_64
    496 #endif // PIC
     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))
     629void __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
Note: See TracChangeset for help on using the changeset viewer.