source: libcfa/src/exception.c @ 2554f24

Last change on this file since 2554f24 was 2554f24, checked in by Andrew Beach <ajbeach@…>, 5 months ago

Try terminate now does not call the catch function, now they have the same caller. This involved updating some platform dependent code which should be correct in all cases.

  • Property mode set to 100644
File size: 21.6 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// exception.c --
8//
9// Author           : Andrew Beach
10// Created On       : Mon Jun 26 15:13:00 2017
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Thu Aug 10 16:45:22 2023
13// Update Count     : 69
14//
15
16// Normally we would get this from the CFA prelude.
17#include <stddef.h> // for size_t
18
19#include <unwind.h> // for struct _Unwind_Exception {...};
20
21#include "exception.h"
22
23#include <stdlib.h>
24#include <stdio.h>
25#include <bits/debug.hfa>
26#include "concurrency/invoke.h"
27#include "stdhdr/assert.h"
28#include "virtual.h"
29
30extern void __cabi_abort( const char fmt[], ... );
31
32#pragma GCC visibility push(default)
33
34#include "lsda.h"
35
36/* The exception class for our exceptions. Because of the vendor component
37 * its value would not be standard.
38 * Vendor: UWPL
39 * Language: CFA\0
40 */
41const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643;
42
43// Base Exception type id:
44struct __cfavir_type_info __cfatid_exception_t = {
45        NULL,
46};
47
48
49// Get the current exception context.
50// There can be a single global until multithreading occurs, then each stack
51// needs its own. We get this from libcfathreads (no weak attribute).
52__attribute__((weak)) struct exception_context_t * this_exception_context() {
53        static struct exception_context_t shared_stack = {NULL, NULL};
54        return &shared_stack;
55}
56
57struct __cfaehm_base_exception_t * __cfaehm_get_current_exception(void) {
58        return this_exception_context()->current_exception;
59}
60
61// RESUMPTION ================================================================
62
63static void reset_top_resume(struct __cfaehm_try_resume_node ** store) {
64        this_exception_context()->top_resume = *store;
65}
66
67void __cfaehm_throw_resume(exception_t * except, void (*defaultHandler)(exception_t *)) {
68        struct exception_context_t * context = this_exception_context();
69
70        __cfadbg_print_safe(exception, "Throwing resumption exception\n");
71
72        {
73                __attribute__((cleanup(reset_top_resume)))
74                struct __cfaehm_try_resume_node * original_head = context->top_resume;
75                struct __cfaehm_try_resume_node * current = context->top_resume;
76
77                for ( ; current ; current = current->next) {
78                        context->top_resume = current->next;
79                        if (current->handler(except)) {
80                                return;
81                        }
82                }
83        } // End the search and return to the top of the stack.
84
85        // No handler found, fall back to the default operation.
86        __cfadbg_print_safe(exception, "Unhandled exception\n");
87        defaultHandler(except);
88}
89
90// Do we control where exceptions get thrown even with concurency?
91// If not these are not quite thread safe, the cleanup hook has to
92// be added after the node is built but before it is made the top node.
93
94void __cfaehm_try_resume_setup(struct __cfaehm_try_resume_node * node,
95                        _Bool (*handler)(exception_t * except)) {
96        struct exception_context_t * context = this_exception_context();
97        node->next = context->top_resume;
98        node->handler = handler;
99        context->top_resume = node;
100}
101
102void __cfaehm_try_resume_cleanup(struct __cfaehm_try_resume_node * node) {
103        struct exception_context_t * context = this_exception_context();
104        context->top_resume = node->next;
105}
106
107
108// MEMORY MANAGEMENT =========================================================
109
110#define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node)))
111#define EXCEPT_TO_NODE(except) ((struct __cfaehm_node *)(except) - 1)
112#define UNWIND_TO_NODE(unwind) ((struct __cfaehm_node *)(unwind))
113#define NULL_MAP(map, ptr) ((ptr) ? (map(ptr)) : NULL)
114
115// How to clean up an exception in various situations.
116static void __cfaehm_exception_cleanup(
117                _Unwind_Reason_Code reason,
118                struct _Unwind_Exception * exception) {
119        switch (reason) {
120        case _URC_FOREIGN_EXCEPTION_CAUGHT:
121                // This one we could clean-up to allow cross-language exceptions.
122        case _URC_FATAL_PHASE1_ERROR:
123        case _URC_FATAL_PHASE2_ERROR:
124        default:
125                abort();
126        }
127}
128
129// Creates a copy of the indicated exception and sets current_exception to it.
130void __cfaehm_allocate_exception( exception_t * except ) {
131        struct exception_context_t * context = this_exception_context();
132
133        // Allocate memory for the exception.
134        struct __cfaehm_node * store = malloc(
135                sizeof( struct __cfaehm_node ) + except->virtual_table->size );
136
137        if ( ! store ) {
138                // Failure: cannot allocate exception. Terminate thread.
139                abort(); // <- Although I think it might be the process.
140        }
141
142        // Initialize the node:
143        exception_t * except_store = NODE_TO_EXCEPT(store);
144        store->unwind_exception.exception_class = __cfaehm_exception_class;
145        store->unwind_exception.exception_cleanup = __cfaehm_exception_cleanup;
146        store->handler_index = 0;
147        except->virtual_table->copy( except_store, except );
148
149        // Add the node to the list:
150        store->next = NULL_MAP(EXCEPT_TO_NODE, context->current_exception);
151        context->current_exception = except_store;
152}
153
154// Delete the provided exception, unsetting current_exception if relivant.
155static void __cfaehm_delete_exception( exception_t * except ) {
156        struct exception_context_t * context = this_exception_context();
157
158        __cfadbg_print_safe(exception, "Deleting Exception\n");
159
160        // Remove the exception from the list.
161        struct __cfaehm_node * to_free = EXCEPT_TO_NODE(except);
162        struct __cfaehm_node * node;
163
164        if ( context->current_exception == except ) {
165                node = to_free->next;
166                context->current_exception = NULL_MAP(NODE_TO_EXCEPT, node);
167        } else {
168                node = EXCEPT_TO_NODE(context->current_exception);
169                // It may always be in the first or second position.
170                while ( to_free != node->next ) {
171                        node = node->next;
172                }
173                node->next = to_free->next;
174        }
175
176        // Free the old exception node.
177        except->virtual_table->free( except );
178        free( to_free );
179}
180
181// CANCELLATION ==============================================================
182
183// Function needed by force unwind
184// It basically says to unwind the whole stack and then exit when we reach the end of the stack
185static _Unwind_Reason_Code _Stop_Fn(
186                int version,
187                _Unwind_Action actions,
188                _Unwind_Exception_Class exception_class,
189                struct _Unwind_Exception * unwind_exception,
190                struct _Unwind_Context * unwind_context,
191                void * stop_param) {
192        // Verify actions follow the rules we expect.
193        verify(actions & _UA_CLEANUP_PHASE);
194        verify(actions & _UA_FORCE_UNWIND);
195        verify(!(actions & _UA_SEARCH_PHASE));
196        verify(!(actions & _UA_HANDLER_FRAME));
197
198        if ( actions & _UA_END_OF_STACK ) {
199                __cabi_abort(
200                        "Propagation failed to find a matching handler.\n"
201                        "Possible cause is a missing try block with appropriate catch clause for specified exception type.\n"
202                        "Last exception name or message: %s.\n",
203                        NODE_TO_EXCEPT( UNWIND_TO_NODE( unwind_exception ) )->
204                                virtual_table->msg( NODE_TO_EXCEPT( UNWIND_TO_NODE( unwind_exception ) ) )
205                );
206        } else {
207                return _URC_NO_REASON;
208        }
209}
210
211__attribute__((weak)) _Unwind_Reason_Code
212__cfaehm_cancellation_unwind( struct _Unwind_Exception * exception ) {
213        return _Unwind_ForcedUnwind( exception, _Stop_Fn, (void*)0x22 );
214}
215
216// Cancel the current stack, prefroming approprate clean-up and messaging.
217void __cfaehm_cancel_stack( exception_t * exception ) {
218        __cfaehm_allocate_exception( exception );
219
220        struct exception_context_t * context = this_exception_context();
221        struct __cfaehm_node * node = EXCEPT_TO_NODE(context->current_exception);
222
223        _Unwind_Reason_Code ret;
224        ret = __cfaehm_cancellation_unwind( &node->unwind_exception );
225        printf("UNWIND ERROR %d after force unwind\n", ret);
226        abort();
227}
228
229
230// TERMINATION ===============================================================
231
232// If this isn't a rethrow (*except==0), delete the provided exception.
233void __cfaehm_cleanup_terminate( void * except ) {
234        if ( *(void**)except ) __cfaehm_delete_exception( *(exception_t **)except );
235}
236
237static void __cfaehm_cleanup_default( exception_t ** except ) {
238        __cfaehm_delete_exception( *except );
239        *except = NULL;
240}
241
242// The exception that is being thrown must already be stored.
243void __cfaehm_begin_unwind(void(*defaultHandler)(exception_t *)) {
244        struct exception_context_t * context = this_exception_context();
245        if ( NULL == context->current_exception ) {
246                printf("UNWIND ERROR missing exception in begin unwind\n");
247                abort();
248        }
249        struct _Unwind_Exception * storage =
250                &EXCEPT_TO_NODE(context->current_exception)->unwind_exception;
251
252        // Call stdlibc to raise the exception
253        __cfadbg_print_safe(exception, "Begin unwinding (storage &p, context %p)\n", storage, context);
254        _Unwind_Reason_Code ret = _Unwind_RaiseException( storage );
255
256        // If we reach here it means something happened. For resumption to work we need to find a way
257        // to return back to here. Most of them will probably boil down to setting a global flag and
258        // making the phase 1 either stop or fail. Causing an error on purpose may help avoiding
259        // unnecessary work but it might have some weird side effects. If we just pretend no handler
260        // was found that would work but may be expensive for no reason since we will always search
261        // the whole stack.
262
263#if defined( __x86_64 ) || defined( __i386 )
264        // We did not simply reach the end of the stack without finding a handler. This is an error.
265        if ( ret != _URC_END_OF_STACK ) {
266#else // defined( __ARM_ARCH )
267        // The return code from _Unwind_RaiseException seems to be corrupt on ARM at end of stack.
268        // This workaround tries to keep default exception handling working.
269        if ( ret == _URC_FATAL_PHASE1_ERROR || ret == _URC_FATAL_PHASE2_ERROR ) {
270#endif
271                printf("UNWIND ERROR %d after raise exception\n", ret);
272                abort();
273        }
274
275        // No handler found, go to the default operation.
276        __cfadbg_print_safe(exception, "Uncaught exception %p\n", storage);
277
278        __attribute__((cleanup(__cfaehm_cleanup_default)))
279        exception_t * exception = context->current_exception;
280        defaultHandler( exception );
281}
282
283void __cfaehm_throw_terminate( exception_t * val, void (*defaultHandler)(exception_t *) ) {
284        __cfadbg_print_safe(exception, "Throwing termination exception\n");
285
286        __cfaehm_allocate_exception( val );
287        __cfaehm_begin_unwind( defaultHandler );
288}
289
290static __attribute__((noreturn)) void __cfaehm_rethrow_adapter( exception_t * except ) {
291        // TODO: Print some error message.
292        (void)except;
293        abort();
294}
295
296void __cfaehm_rethrow_terminate(void) {
297        __cfadbg_print_safe(exception, "Rethrowing termination exception\n");
298
299        __cfaehm_begin_unwind( __cfaehm_rethrow_adapter );
300        abort();
301}
302
303#if defined( __x86_64 ) || defined( __i386 ) || defined( __ARM_ARCH )
304// This is our personality routine. For every stack frame annotated with
305// ".cfi_personality 0x3,__gcfa_personality_v0" this function will be called twice when unwinding.
306//  Once in the search phase and once in the cleanup phase.
307_Unwind_Reason_Code __gcfa_personality_v0(
308                int version,
309                _Unwind_Action actions,
310                unsigned long long exception_class,
311                struct _Unwind_Exception * unwind_exception,
312                struct _Unwind_Context * unwind_context)
313{
314        //__cfadbg_print_safe(exception, "CFA: 0x%lx\n", _Unwind_GetCFA(context));
315        __cfadbg_print_safe(exception, "Personality function (%d, %x, %llu, %p, %p):",
316                        version, actions, exception_class, unwind_exception, unwind_context);
317
318        // Verify that actions follow the rules we expect.
319        // This function should never be called at the end of the stack.
320        verify(!(actions & _UA_END_OF_STACK));
321        // Either only the search phase flag is set or...
322        if (actions & _UA_SEARCH_PHASE) {
323                verify(actions == _UA_SEARCH_PHASE);
324                __cfadbg_print_safe(exception, " lookup phase");
325        // ... we are in clean-up phase.
326        } else {
327                verify(actions & _UA_CLEANUP_PHASE);
328                __cfadbg_print_safe(exception, " cleanup phase");
329                // We shouldn't be the handler frame during forced unwind.
330                if (actions & _UA_HANDLER_FRAME) {
331                        verify(!(actions & _UA_FORCE_UNWIND));
332                        __cfadbg_print_safe(exception, " (handler frame)");
333                } else if (actions & _UA_FORCE_UNWIND) {
334                        __cfadbg_print_safe(exception, " (force unwind)");
335                }
336        }
337
338        // Get a pointer to the language specific data from which we will read what we need
339        const unsigned char * lsd = _Unwind_GetLanguageSpecificData( unwind_context );
340
341        if ( !lsd ) {   //Nothing to do, keep unwinding
342                printf(" no LSD");
343                goto UNWIND;
344        }
345
346        // Get the instuction pointer and a reading pointer into the exception table
347        lsda_header_info lsd_info;
348        const unsigned char * cur_ptr = parse_lsda_header(unwind_context, lsd, &lsd_info);
349        _Unwind_Ptr instruction_ptr = _Unwind_GetIP(unwind_context);
350
351        struct exception_context_t * context = this_exception_context();
352
353        // Linearly search the table for stuff to do
354        while ( cur_ptr < lsd_info.action_table ) {
355                _Unwind_Ptr callsite_start;
356                _Unwind_Ptr callsite_len;
357                _Unwind_Ptr callsite_landing_pad;
358                _uleb128_t  callsite_action;
359
360                // Decode the common stuff we have in here
361                cur_ptr = read_encoded_value(0, lsd_info.call_site_encoding, cur_ptr, &callsite_start);
362                cur_ptr = read_encoded_value(0, lsd_info.call_site_encoding, cur_ptr, &callsite_len);
363                cur_ptr = read_encoded_value(0, lsd_info.call_site_encoding, cur_ptr, &callsite_landing_pad);
364                cur_ptr = read_uleb128(cur_ptr, &callsite_action);
365
366                // Have we reach the correct frame info yet?
367                if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {
368#ifdef __CFA_DEBUG_PRINT__
369                        void * ls = (void*)lsd_info.Start;
370                        void * cs = (void*)callsite_start;
371                        void * cl = (void*)callsite_len;
372                        void * bp = (void*)lsd_info.Start + callsite_start;
373                        void * ep = (void*)lsd_info.Start + callsite_start + callsite_len;
374                        void * ip = (void*)instruction_ptr;
375                        __cfadbg_print_safe(exception, "\nfound %p - %p (%p, %p, %p), looking for %p\n",
376                                        bp, ep, ls, cs, cl, ip);
377#endif // __CFA_DEBUG_PRINT__
378                        continue;
379                }
380
381                // Have we gone too far?
382                if ( lsd_info.Start + callsite_start > instruction_ptr ) {
383                        printf(" gone too far");
384                        break;
385                }
386
387                // Check for what we must do:
388                if ( 0 == callsite_landing_pad ) {
389                        // Nothing to do, move along
390                        __cfadbg_print_safe(exception, " no landing pad");
391                } else if (actions & _UA_SEARCH_PHASE) {
392                        // In search phase, these means we found a potential handler we must check.
393
394                        // We have arbitrarily decided that 0 means nothing to do and 1 means there is
395                        // a potential handler. This doesn't seem to conflict the gcc default behavior.
396                        if (callsite_action != 0) {
397                                // Now we want to run some code to see if the handler matches
398                                // This is the tricky part where we want to the power to run arbitrary code
399                                // However, generating a new exception table entry and try routine every time
400                                // is way more expansive than we might like
401                                // The information we have is :
402                                //  - The GR (Series of registers)
403                                //    GR1=GP Global Pointer of frame ref by context
404                                //  - The instruction pointer
405                                //  - The instruction pointer info (???)
406                                //  - The CFA (Canonical Frame Address)
407                                //  - The BSP (Probably the base stack pointer)
408
409                                // The current apprach uses one exception table entry per try block
410                                _uleb128_t imatcher;
411                                // Get the relative offset to the {...}?
412                                cur_ptr = read_uleb128(cur_ptr, &imatcher);
413
414                                _Unwind_Word match_pos =
415#                               if defined( __x86_64 )
416                                    _Unwind_GetCFA(unwind_context);
417#                               elif defined( __i386 )
418                                    _Unwind_GetCFA(unwind_context) + 8;
419#                               elif defined( __ARM_ARCH )
420                                    _Unwind_GetCFA(unwind_context) + 16;
421#                               endif
422                                int (*matcher)(exception_t *) = *(int(**)(exception_t *))match_pos;
423
424                                int index = matcher(context->current_exception);
425                                _Unwind_Reason_Code ret = (0 == index)
426                                        ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND;
427                                UNWIND_TO_NODE(unwind_exception)->handler_index = index;
428
429                                // Based on the return value, check if we matched the exception
430                                if (ret == _URC_HANDLER_FOUND) {
431                                        __cfadbg_print_safe(exception, " handler found\n");
432                                } else {
433                                        // TODO: Continue the search if there is more in the table.
434                                        __cfadbg_print_safe(exception, " no handler\n");
435                                }
436                                return ret;
437                        }
438
439                        // This is only a cleanup handler, ignore it
440                        __cfadbg_print_safe(exception, " no action");
441                } else {
442                        // In clean-up phase, no destructors here but this could be the handler.
443
444                        if ( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){
445                                // If this is a potential exception handler
446                                // but not the one that matched the exception in the seach phase,
447                                // just ignore it
448                                goto UNWIND;
449                        }
450
451                        // We need to run some clean-up or a handler
452                        // These statment do the right thing but I don't know any specifics at all
453                        _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(0),
454                                (_Unwind_Ptr)unwind_exception );
455                        _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(1), 0 );
456
457                        // I assume this sets the instruction pointer to the adress of the landing pad
458                        // It doesn't actually set it, it only state the value that needs to be set once we
459                        // return _URC_INSTALL_CONTEXT
460                        _Unwind_SetIP( unwind_context, ((lsd_info.LPStart) + (callsite_landing_pad)) );
461
462                        __cfadbg_print_safe(exception, " action\n");
463
464                        // Return have some action to run
465                        return _URC_INSTALL_CONTEXT;
466                }
467        }
468        // No handling found
469        __cfadbg_print_safe(exception, " table end reached");
470
471        UNWIND:
472        __cfadbg_print_safe(exception, " unwind\n");
473
474        // Keep unwinding the stack
475        return _URC_CONTINUE_UNWIND;
476}
477
478#pragma GCC push_options
479#pragma GCC optimize(0)
480
481// Try statements are hoisted out see comments for details. While this could probably be unique
482// and simply linked from libcfa but there is one problem left, see the exception table for details
483__attribute__((noinline))
484int __cfaehm_try_terminate(void (*try_block)(),
485                __attribute__((unused)) int (*match_block)(exception_t * except)) {
486        //! volatile int xy = 0;
487        //! printf("%p %p %p\n", &try_block, &match_block, &xy);
488
489        // Setup the personality routine and exception table.
490        // Unforturnately these clobber gcc cancellation support which means we can't get access to
491        // the attribute cleanup tables at the same time. We would have to inspect the assembly to
492        // create a new set ourselves.
493#ifdef __PIC__
494        asm volatile (".cfi_personality 0x9b,CFA.ref.__gcfa_personality_v0");
495        asm volatile (".cfi_lsda 0x1b, .LLSDACFA2");
496#else
497        asm volatile (".cfi_personality 0x3,__gcfa_personality_v0");
498        asm volatile (".cfi_lsda 0x3, .LLSDACFA2");
499#endif
500
501        // Label which defines the start of the area for which the handler is setup.
502        asm volatile (".TRYSTART:");
503
504        // The actual statements of the try blocks
505        try_block();
506
507        // asm statement to prevent deadcode removal
508        asm volatile goto ("" : : : : CATCH );
509
510        // Normal return for when there is no throw.
511        return 0;
512
513        // Exceptionnal path
514        CATCH : __attribute__(( unused ));
515        // Label which defines the end of the area for which the handler is setup.
516        asm volatile (".TRYEND:");
517        // Label which defines the start of the exception landing pad. Basically what is called when
518        // the exception is caught. Note, if multiple handlers are given, the multiplexing should be
519        // done by the generated code, not the exception runtime.
520        asm volatile (".CATCH:");
521
522        return EXCEPT_TO_NODE( this_exception_context()->current_exception )->handler_index;
523}
524
525// Exception table data we need to generate. While this is almost generic, the custom data refers
526// to {*}try_terminate, which is no way generic. Some more works need to be done if we want to
527// have a single call to the try routine.
528
529#ifdef __PIC__
530asm (
531        // HEADER
532        ".LFECFA1:\n"
533#if defined( __x86_64 ) || defined( __i386 )
534        "       .globl  __gcfa_personality_v0\n"
535#else // defined( __ARM_ARCH )
536        "       .global __gcfa_personality_v0\n"
537#endif
538        "       .section        .gcc_except_table,\"a\",@progbits\n"
539        // TABLE HEADER (important field is the BODY length at the end)
540        ".LLSDACFA2:\n"
541        "       .byte   0xff\n"
542        "       .byte   0xff\n"
543        "       .byte   0x1\n"
544        "       .uleb128 .LLSDACSECFA2-.LLSDACSBCFA2\n"
545        // BODY (language specific data)
546        // This uses language specific data and can be modified arbitrarily
547        // We use handled area offset, handled area length,
548        // handler landing pad offset and 1 (action code, gcc seems to use 0).
549        ".LLSDACSBCFA2:\n"
550        "       .uleb128 .TRYSTART-__cfaehm_try_terminate\n"
551        "       .uleb128 .TRYEND-.TRYSTART\n"
552        "       .uleb128 .CATCH-__cfaehm_try_terminate\n"
553        "       .uleb128 1\n"
554        ".LLSDACSECFA2:\n"
555        // TABLE FOOTER
556        "       .text\n"
557        "       .size   __cfaehm_try_terminate, .-__cfaehm_try_terminate\n"
558);
559
560// Somehow this piece of helps with the resolution of debug symbols.
561__attribute__((unused)) static const int dummy = 0;
562
563asm (
564        // Add a hidden symbol which points at the function.
565        "       .hidden CFA.ref.__gcfa_personality_v0\n"
566        "       .weak   CFA.ref.__gcfa_personality_v0\n"
567#if defined( __x86_64 ) || defined( __i386 )
568        "       .align 8\n"
569#else // defined( __ARM_ARCH )
570        "       .align 3\n"
571#endif
572        "       .type CFA.ref.__gcfa_personality_v0, @object\n"
573        "       .size CFA.ref.__gcfa_personality_v0, 8\n"
574        "CFA.ref.__gcfa_personality_v0:\n"
575#if defined( __x86_64 )
576        "       .quad __gcfa_personality_v0\n"
577#elif defined( __i386 )
578        "       .long __gcfa_personality_v0\n"
579#else // defined( __ARM_ARCH )
580        "       .xword __gcfa_personality_v0\n"
581#endif
582);
583#else // __PIC__
584asm (
585        // HEADER
586        ".LFECFA1:\n"
587#if defined( __x86_64 ) || defined( __i386 )
588        "       .globl  __gcfa_personality_v0\n"
589#else // defined( __ARM_ARCH )
590        "       .global __gcfa_personality_v0\n"
591#endif
592        "       .section        .gcc_except_table,\"a\",@progbits\n"
593        // TABLE HEADER (important field is the BODY length at the end)
594        ".LLSDACFA2:\n"
595        "       .byte   0xff\n"
596        "       .byte   0xff\n"
597        "       .byte   0x1\n"
598        "       .uleb128 .LLSDACSECFA2-.LLSDACSBCFA2\n"
599        // BODY (language specific data)
600        ".LLSDACSBCFA2:\n"
601        //      Handled area start (relative to start of function)
602        "       .uleb128 .TRYSTART-__cfaehm_try_terminate\n"
603        //      Handled area length
604        "       .uleb128 .TRYEND-.TRYSTART\n"
605        //      Handler landing pad address (relative to start of function)
606        "       .uleb128 .CATCH-__cfaehm_try_terminate\n"
607        //      Action code, gcc seems to always use 0.
608        "       .uleb128 1\n"
609        // TABLE FOOTER
610        ".LLSDACSECFA2:\n"
611        "       .text\n"
612        "       .size   __cfaehm_try_terminate, .-__cfaehm_try_terminate\n"
613        "       .ident  \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n"
614        "       .section        .note.GNU-stack,\"x\",@progbits\n"
615);
616#endif // __PIC__
617
618#pragma GCC pop_options
619
620#else
621        #error unsupported hardware architecture
622#endif // __x86_64 || __i386 || __ARM_ARCH
Note: See TracBrowser for help on using the repository browser.