source: src/libcfa/exception.c@ 533804b

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 533804b was 38ac6ec, checked in by Andrew Beach <ajbeach@…>, 8 years ago

Fixed some names that had drifted from terminate/resume.

  • Property mode set to 100644
File size: 13.7 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 : Andrew Beach
12// Last Modified On : Tus Jul 11 16:36:00 2017
13// Update Count : 1
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
24#include "lsda.h"
25
26// Temperary global exception context. Does not work with concurency.
27struct shared_stack_t {
28 struct __cfaehm__try_resume_node * top_resume;
29 struct __cfaehm__try_resume_node * current_resume;
30
31 exception current_exception;
32 int current_handler_index;
33} shared_stack = {NULL, NULL, 0, 0};
34
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.
39// struct _Unwind_Context * -> _Unwind_Reason_Code(*)(exception *)
40#define MATCHER_FROM_CONTEXT(ptr_to_context) \
41 (*(_Unwind_Reason_Code(**)(exception*))(_Unwind_GetCFA(ptr_to_context) + 8))
42
43
44// RESUMPTION ================================================================
45
46void __cfaehm__throw_resume(exception * except) {
47
48 // DEBUG
49 printf("Throwing resumption exception %d\n", *except);
50
51 struct __cfaehm__try_resume_node * original_head = shared_stack.current_resume;
52 struct __cfaehm__try_resume_node * current =
53 (original_head) ? original_head->next : shared_stack.top_resume;
54
55 for ( ; current ; current = current->next) {
56 shared_stack.current_resume = current;
57 if (current->handler(except)) {
58 shared_stack.current_resume = original_head;
59 return;
60 }
61 }
62
63 printf("Unhandled exception %d\n", *except);
64 shared_stack.current_resume = original_head;
65
66 // Fall back to termination:
67 __cfaehm__throw_terminate(except);
68 // TODO: Default handler for resumption.
69}
70
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 */
75void __cfaehm__try_resume_setup(struct __cfaehm__try_resume_node * node,
76 int (*handler)(exception * except)) {
77 node->next = shared_stack.top_resume;
78 node->handler = handler;
79 shared_stack.top_resume = node;
80}
81
82void __cfaehm__try_resume_cleanup(struct __cfaehm__try_resume_node * node) {
83 shared_stack.top_resume = node->next;
84}
85
86
87// TERMINATION ===============================================================
88
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)
94
95// We need a piece of storage to raise the exception
96struct _Unwind_Exception this_exception_storage;
97
98// Function needed by force unwind
99// It basically says to unwind the whole stack and then exit when we reach the end of the stack
100static _Unwind_Reason_Code _Stop_Fn(
101 int version,
102 _Unwind_Action actions,
103 _Unwind_Exception_Class exceptionClass,
104 struct _Unwind_Exception * unwind_exception,
105 struct _Unwind_Context * context,
106 void * some_param) {
107 if( actions & _UA_END_OF_STACK ) exit(1);
108 if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON;
109
110 return _URC_FATAL_PHASE2_ERROR;
111}
112
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);
119
120 // Call stdlibc to raise the exception
121 _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage );
122
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
129
130 if( ret == _URC_END_OF_STACK ) {
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
135 printf("Uncaught exception %p\n", &this_exception_storage);
136
137 ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 );
138 printf("UNWIND ERROR %d after force unwind\n", ret);
139 abort();
140 }
141
142 // We did not simply reach the end of the stack without finding a handler,
143 // Something wen't wrong
144 printf("UNWIND ERROR %d after raise exception\n", ret);
145 abort();
146}
147
148// Nesting this the other way would probably be faster.
149void __cfaehm__rethrow_terminate(void) {
150 // DEBUG
151 printf("Rethrowing termination exception\n");
152
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
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
263 _Unwind_Reason_Code (*matcher)(exception *) =
264 MATCHER_FROM_CONTEXT(context);
265 int index = matcher(&shared_stack.current_exception);
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
295 _Unwind_SetIP( context, lsd_info.LPStart + callsite_landing_pad );
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
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
323__attribute__((noinline))
324void __cfaehm__try_terminate(void (*try_block)(),
325 void (*catch_block)(int index, exception * except),
326 __attribute__((unused)) int (*match_block)(exception * except)) {
327 //! volatile int xy = 0;
328 //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy);
329
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.
337 // Setup the personality routine
338 asm volatile (".cfi_personality 0x3,__gcfa_personality_v0");
339 // Setup the exception table
340 asm volatile (".cfi_lsda 0x3, .LLSDACFA2");
341
342 // Label which defines the start of the area for which the handler is setup
343 asm volatile (".TRYSTART:");
344
345 // The actual statements of the try blocks
346 try_block();
347
348 // asm statement to prevent deadcode removal
349 asm volatile goto ("" : : : : CATCH );
350
351 // Normal return
352 return;
353
354 // Exceptionnal path
355 CATCH : __attribute__(( unused ));
356 // Label which defines the end of the area for which the handler is setup
357 asm volatile (".TRYEND:");
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
362 asm volatile (".CATCH:");
363
364 // Exception handler
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
374asm (
375 //HEADER
376 ".LFECFA1:\n"
377 " .globl __gcfa_personality_v0\n"
378 " .section .gcc_except_table,\"a\",@progbits\n"
379 ".LLSDACFA2:\n" //TABLE header
380 " .byte 0xff\n"
381 " .byte 0xff\n"
382 " .byte 0x1\n"
383 " .uleb128 .LLSDACSECFA2-.LLSDACSBCFA2\n" // BODY length
384 // Body uses language specific data and therefore could be modified arbitrarily
385 ".LLSDACSBCFA2:\n" // BODY start
386 " .uleb128 .TRYSTART-__cfaehm__try_terminate\n" // Handled area start (relative to start of function)
387 " .uleb128 .TRYEND-.TRYSTART\n" // Handled area length
388 " .uleb128 .CATCH-__cfaehm__try_terminate\n" // Hanlder landing pad adress (relative to start of function)
389 " .uleb128 1\n" // Action code, gcc seems to use always 0
390 ".LLSDACSECFA2:\n" // BODY end
391 " .text\n" // TABLE footer
392 " .size __cfaehm__try_terminate, .-__cfaehm__try_terminate\n"
393 " .ident \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n"
394// " .section .note.GNU-stack,\"x\",@progbits\n"
395);
Note: See TracBrowser for help on using the repository browser.