source: src/libcfa/exception.c@ e213560

ADT aaron-thesis arm-eh ast-experimental cleanup-dtors deferred_resn demangler enum forall-pointer-decay jacob/cs343-translation jenkins-sandbox new-ast new-ast-unique-expr new-env no_list persistent-indexer pthread-emulation qualifiedEnum resolv-new with_gc
Last change on this file since e213560 was b947fb2, checked in by Peter A. Buhr <pabuhr@…>, 8 years ago

temporary hacks to allow build on ARM

  • Property mode set to 100644
File size: 14.0 KB
RevLine 
[fa4805f]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
[b947fb2]11// Last Modified By : Peter A. Buhr
12// Last Modified On : Wed Jul 26 10:37:51 2017
13// Update Count : 2
[fa4805f]14//
15
16#include "exception.h"
17
18// Implementation of the secret header.
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <unwind.h>
23
[b947fb2]24// FIX ME: temporary hack to keep ARM build working
25#ifndef _URC_FATAL_PHASE1_ERROR
26#define _URC_FATAL_PHASE1_ERROR 2
27#endif // ! _URC_FATAL_PHASE1_ERROR
28#ifndef _URC_FATAL_PHASE2_ERROR
29#define _URC_FATAL_PHASE2_ERROR 2
30#endif // ! _URC_FATAL_PHASE2_ERROR
31
[fa4805f]32#include "lsda.h"
33
34// Temperary global exception context. Does not work with concurency.
35struct shared_stack_t {
36 struct __cfaehm__try_resume_node * top_resume;
37 struct __cfaehm__try_resume_node * current_resume;
38
39 exception current_exception;
40 int current_handler_index;
41} shared_stack = {NULL, NULL, 0, 0};
42
43
44
[b947fb2]45// This macro should be the only thing that needs to change across machines. Used in the personality function, way down
46// in termination.
[ad0be81]47// struct _Unwind_Context * -> _Unwind_Reason_Code(*)(exception *)
[fa4805f]48#define MATCHER_FROM_CONTEXT(ptr_to_context) \
[ad0be81]49 (*(_Unwind_Reason_Code(**)(exception*))(_Unwind_GetCFA(ptr_to_context) + 8))
[fa4805f]50
51
52// RESUMPTION ================================================================
53
[38ac6ec]54void __cfaehm__throw_resume(exception * except) {
[fa4805f]55
56 // DEBUG
[307a732]57 printf("Throwing resumption exception %d\n", *except);
[fa4805f]58
[307a732]59 struct __cfaehm__try_resume_node * original_head = shared_stack.current_resume;
60 struct __cfaehm__try_resume_node * current =
[fa4805f]61 (original_head) ? original_head->next : shared_stack.top_resume;
62
63 for ( ; current ; current = current->next) {
64 shared_stack.current_resume = current;
[307a732]65 if (current->handler(except)) {
[fa4805f]66 shared_stack.current_resume = original_head;
67 return;
68 }
69 }
70
[307a732]71 printf("Unhandled exception %d\n", *except);
[fa4805f]72 shared_stack.current_resume = original_head;
73
74 // Fall back to termination:
[38ac6ec]75 __cfaehm__throw_terminate(except);
[fa4805f]76 // TODO: Default handler for resumption.
77}
78
[b947fb2]79// Do we control where exceptions get thrown even with concurency? If not these are not quite thread safe, the cleanup
80// hook has to be added after the node is built but before it is made the top node.
81
[307a732]82void __cfaehm__try_resume_setup(struct __cfaehm__try_resume_node * node,
83 int (*handler)(exception * except)) {
[fa4805f]84 node->next = shared_stack.top_resume;
[307a732]85 node->handler = handler;
[fa4805f]86 shared_stack.top_resume = node;
87}
88
[307a732]89void __cfaehm__try_resume_cleanup(struct __cfaehm__try_resume_node * node) {
[fa4805f]90 shared_stack.top_resume = node->next;
91}
92
93
94// TERMINATION ===============================================================
95
96// Requires -fexceptions to work.
97
[b947fb2]98// Global which defines the current exception. Currently an int just to make matching easier.
[fa4805f]99//int this_exception; (became shared_stack.current_exception)
100
101// We need a piece of storage to raise the exception
102struct _Unwind_Exception this_exception_storage;
103
104// Function needed by force unwind
105// It basically says to unwind the whole stack and then exit when we reach the end of the stack
106static _Unwind_Reason_Code _Stop_Fn(
107 int version,
108 _Unwind_Action actions,
109 _Unwind_Exception_Class exceptionClass,
110 struct _Unwind_Exception * unwind_exception,
111 struct _Unwind_Context * context,
112 void * some_param) {
113 if( actions & _UA_END_OF_STACK ) exit(1);
114 if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON;
115
116 return _URC_FATAL_PHASE2_ERROR;
117}
118
[38ac6ec]119void __cfaehm__throw_terminate( exception * val ) {
[fa4805f]120 // Store the current exception
[307a732]121 shared_stack.current_exception = *val;
[fa4805f]122
123 // DEBUG
[307a732]124 printf("Throwing termination exception %d\n", *val);
[fa4805f]125
126 // Call stdlibc to raise the exception
127 _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage );
128
[b947fb2]129 // If we reach here it means something happened. For resumption to work we need to find a way to return back to
130 // here. Most of them will probably boil down to setting a global flag and making the phase 1 either stop or
131 // fail. Causing an error on purpose may help avoiding unnecessary work but it might have some weird side
132 // effects. If we just pretend no handler was found that would work but may be expensive for no reason since we
133 // will always search the whole stack.
[fa4805f]134
135 if( ret == _URC_END_OF_STACK ) {
[b947fb2]136 // No proper handler was found. This can be handled in several way. C++ calls std::terminate Here we
137 // force unwind the stack, basically raising a cancellation.
[fa4805f]138 printf("Uncaught exception %p\n", &this_exception_storage);
139
140 ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 );
141 printf("UNWIND ERROR %d after force unwind\n", ret);
142 abort();
143 }
144
[b947fb2]145 // We did not simply reach the end of the stack without finding a handler. Something wen't wrong
[fa4805f]146 printf("UNWIND ERROR %d after raise exception\n", ret);
147 abort();
148}
149
150// Nesting this the other way would probably be faster.
[38ac6ec]151void __cfaehm__rethrow_terminate(void) {
[fa4805f]152 // DEBUG
153 printf("Rethrowing termination exception\n");
154
[38ac6ec]155 __cfaehm__throw_terminate(&shared_stack.current_exception);
[fa4805f]156}
157
[b947fb2]158// This is our personality routine. For every stack frame anotated with ".cfi_personality 0x3,__gcfa_personality_v0".
159// This function will be called twice when unwinding. Once in the search phased and once in the cleanup phase.
[fa4805f]160_Unwind_Reason_Code __gcfa_personality_v0 (
161 int version, _Unwind_Action actions, unsigned long long exceptionClass,
162 struct _Unwind_Exception* unwind_exception,
163 struct _Unwind_Context* context)
164{
165
166 // DEBUG
167 //printf("CFA: 0x%lx\n", _Unwind_GetCFA(context));
168 printf("Personality function (%d, %x, %llu, %p, %p):", version, actions, exceptionClass, unwind_exception, context);
169
170 // If we've reached the end of the stack then there is nothing much we can do...
171 if( actions & _UA_END_OF_STACK ) return _URC_END_OF_STACK;
172
173 // DEBUG
174 if (actions & _UA_SEARCH_PHASE) {
175 printf(" lookup phase");
176 }
177 // DEBUG
178 else if (actions & _UA_CLEANUP_PHASE) {
179 printf(" cleanup phase");
180 }
181 // Just in case, probably can't actually happen
182 else {
183 printf(" error\n");
184 return _URC_FATAL_PHASE1_ERROR;
185 }
186
187 // Get a pointer to the language specific data from which we will read what we need
188 const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData( context );
189
190 if( !lsd ) { //Nothing to do, keep unwinding
191 printf(" no LSD");
192 goto UNWIND;
193 }
194
195 // Get the instuction pointer and a reading pointer into the exception table
196 lsda_header_info lsd_info;
197 const unsigned char * cur_ptr = parse_lsda_header( context, lsd, &lsd_info);
198 _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context );
199
200 // Linearly search the table for stuff to do
201 while( cur_ptr < lsd_info.action_table ) {
202 _Unwind_Ptr callsite_start;
203 _Unwind_Ptr callsite_len;
204 _Unwind_Ptr callsite_landing_pad;
205 _uleb128_t callsite_action;
206
207 // Decode the common stuff we have in here
208 cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_start);
209 cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_len);
210 cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_landing_pad);
211 cur_ptr = read_uleb128 (cur_ptr, &callsite_action);
212
213 // Have we reach the correct frame info yet?
214 if( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {
215 //DEBUG BEGIN
216 void * ls = (void*)lsd_info.Start;
217 void * cs = (void*)callsite_start;
218 void * cl = (void*)callsite_len;
219 void * bp = (void*)lsd_info.Start + callsite_start;
220 void * ep = (void*)lsd_info.Start + callsite_start + callsite_len;
221 void * ip = (void*)instruction_ptr;
222 printf("\nfound %p - %p (%p, %p, %p), looking for %p\n", bp, ep, ls, cs, cl, ip);
223 //DEBUG END
224 continue;
225 }
226
227 // Have we gone too far
228 if( lsd_info.Start + callsite_start > instruction_ptr ) {
229 printf(" gone too far");
230 break;
231 }
232
233 // Something to do?
234 if( callsite_landing_pad ) {
235 // Which phase are we in
236 if (actions & _UA_SEARCH_PHASE) {
237 // Search phase, this means we probably found a potential handler and must check if it is a match
238
239 // If we have arbitrarily decided that 0 means nothing to do and 1 means there is a potential handler
240 // This doesn't seem to conflict the gcc default behavior
241 if (callsite_action != 0) {
242 // Now we want to run some code to see if the handler matches
243 // This is the tricky part where we want to the power to run arbitrary code
244 // However, generating a new exception table entry and try routine every time
245 // is way more expansive than we might like
246 // The information we have is :
247 // - The GR (Series of registers)
248 // GR1=GP Global Pointer of frame ref by context
249 // - The instruction pointer
250 // - The instruction pointer info (???)
251 // - The CFA (Canonical Frame Address)
252 // - The BSP (Probably the base stack pointer)
253
254
255 // The current apprach uses one exception table entry per try block
256 _uleb128_t imatcher;
257 // Get the relative offset to the
258 cur_ptr = read_uleb128 (cur_ptr, &imatcher);
259
260 // Get a function pointer from the relative offset and call it
261 // _Unwind_Reason_Code (*matcher)() = (_Unwind_Reason_Code (*)())lsd_info.LPStart + imatcher;
262
[ad0be81]263 _Unwind_Reason_Code (*matcher)(exception *) =
[fa4805f]264 MATCHER_FROM_CONTEXT(context);
[ad0be81]265 int index = matcher(&shared_stack.current_exception);
[fa4805f]266 _Unwind_Reason_Code ret = (0 == index)
267 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND;
268 shared_stack.current_handler_index = index;
269
270 // Based on the return value, check if we matched the exception
271 if( ret == _URC_HANDLER_FOUND) printf(" handler found\n");
272 else printf(" no handler\n");
273 return ret;
274 }
275
276 // This is only a cleanup handler, ignore it
277 printf(" no action");
278 }
279 else if (actions & _UA_CLEANUP_PHASE) {
280
281 if( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){
282 // If this is a potential exception handler
283 // but not the one that matched the exception in the seach phase,
284 // just ignore it
285 goto UNWIND;
286 }
287
288 // We need to run some clean-up or a handler
289 // These statment do the right thing but I don't know any specifics at all
290 _Unwind_SetGR( context, __builtin_eh_return_data_regno(0), (_Unwind_Ptr) unwind_exception );
291 _Unwind_SetGR( context, __builtin_eh_return_data_regno(1), 0 );
292
293 // I assume this sets the instruction pointer to the adress of the landing pad
294 // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT
[b947fb2]295 _Unwind_SetIP( context, ((lsd_info.LPStart) + (callsite_landing_pad)) );
[fa4805f]296
297 // DEBUG
298 printf(" action\n");
299
300 // Return have some action to run
301 return _URC_INSTALL_CONTEXT;
302 }
303 }
304
305 // Nothing to do, move along
306 printf(" no landing pad");
307 }
308 // No handling found
309 printf(" table end reached\n");
310
311 // DEBUG
312 UNWIND:
313 printf(" unwind\n");
314
315 // Keep unwinding the stack
316 return _URC_CONTINUE_UNWIND;
317}
318
[b947fb2]319// Try statements are hoisted out see comments for details. With this could probably be unique and simply linked from
320// libcfa but there is one problem left, see the exception table for details
[fa4805f]321__attribute__((noinline))
[307a732]322void __cfaehm__try_terminate(void (*try_block)(),
323 void (*catch_block)(int index, exception * except),
324 __attribute__((unused)) int (*match_block)(exception * except)) {
[fa4805f]325 //! volatile int xy = 0;
326 //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy);
327
[b947fb2]328 // Setup statments: These 2 statments won't actually result in any code, they only setup global tables.
329 // However, they clobber gcc cancellation support from gcc. We can replace the personality routine but
330 // replacing the exception table gcc generates is not really doable, it generates labels based on how the
331 // assembly works.
332
[fa4805f]333 // Setup the personality routine
334 asm volatile (".cfi_personality 0x3,__gcfa_personality_v0");
335 // Setup the exception table
336 asm volatile (".cfi_lsda 0x3, .LLSDACFA2");
337
[b947fb2]338 // Label which defines the start of the area for which the handler is setup.
[fa4805f]339 asm volatile (".TRYSTART:");
340
341 // The actual statements of the try blocks
342 try_block();
343
344 // asm statement to prevent deadcode removal
345 asm volatile goto ("" : : : : CATCH );
346
347 // Normal return
348 return;
349
350 // Exceptionnal path
351 CATCH : __attribute__(( unused ));
[b947fb2]352 // Label which defines the end of the area for which the handler is setup.
[fa4805f]353 asm volatile (".TRYEND:");
[b947fb2]354 // Label which defines the start of the exception landing pad. Basically what is called when the exception is
355 // caught. Note, if multiple handlers are given, the multiplexing should be done by the generated code, not the
356 // exception runtime.
[fa4805f]357 asm volatile (".CATCH:");
358
359 // Exception handler
[b947fb2]360 catch_block( shared_stack.current_handler_index,
361 &shared_stack.current_exception );
[fa4805f]362}
363
[b947fb2]364// Exception table data we need to generate. While this is almost generic, the custom data refers to foo_try_match try
365// 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.
366
367#if defined( __x86_64__ ) || defined( __i386__ )
[fa4805f]368asm (
369 //HEADER
370 ".LFECFA1:\n"
371 " .globl __gcfa_personality_v0\n"
372 " .section .gcc_except_table,\"a\",@progbits\n"
373 ".LLSDACFA2:\n" //TABLE header
374 " .byte 0xff\n"
375 " .byte 0xff\n"
376 " .byte 0x1\n"
377 " .uleb128 .LLSDACSECFA2-.LLSDACSBCFA2\n" // BODY length
378 // Body uses language specific data and therefore could be modified arbitrarily
379 ".LLSDACSBCFA2:\n" // BODY start
[307a732]380 " .uleb128 .TRYSTART-__cfaehm__try_terminate\n" // Handled area start (relative to start of function)
[fa4805f]381 " .uleb128 .TRYEND-.TRYSTART\n" // Handled area length
[307a732]382 " .uleb128 .CATCH-__cfaehm__try_terminate\n" // Hanlder landing pad adress (relative to start of function)
[fa4805f]383 " .uleb128 1\n" // Action code, gcc seems to use always 0
384 ".LLSDACSECFA2:\n" // BODY end
385 " .text\n" // TABLE footer
[307a732]386 " .size __cfaehm__try_terminate, .-__cfaehm__try_terminate\n"
[fa4805f]387 " .ident \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n"
388// " .section .note.GNU-stack,\"x\",@progbits\n"
389);
[b947fb2]390#endif // __x86_64__ || __i386__
Note: See TracBrowser for help on using the repository browser.