Wiki Roadmap Timeline Tickets New Ticket Source Search Help / Guide About Trac Login

root/runtime/internal/eh.d

Revision 1597:761bf823e59e, 13.8 kB (checked in by Christian Kamm <kamm incasoftware de>, 9 months ago)

Fix definition of _Unwind_Action.

Thanks to Garrison.

Line 
1 /**
2  * This module contains functions and structures required for
3  * exception handling.
4  */
5 module eh;
6
7 import util.console;
8 import ldc.cstdarg;
9
10 // debug = EH_personality;
11 // debug = EH_personality_verbose;
12
13 // current EH implementation works on x86
14 // if it has a working unwind runtime
15 version(X86) {
16     version(linux) version=X86_UNWIND;
17     version(darwin) version=X86_UNWIND;
18     version(solaris) version=X86_UNWIND;
19 }
20 version(X86_64) {
21     version(linux) version=X86_UNWIND;
22     version(darwin) version=X86_UNWIND;
23     version(solaris) version=X86_UNWIND;
24 }
25
26 //version = HP_LIBUNWIND;
27
28 private extern(C) void abort();
29 private extern(C) int printf(char*, ...);
30 private extern(C) int vprintf(char*, va_list va);
31
32 // D runtime functions
33 extern(C) {
34     int _d_isbaseof(ClassInfo oc, ClassInfo c);
35 }
36
37 // libunwind headers
38 extern(C)
39 {
40     enum _Unwind_Reason_Code : int
41     {
42         NO_REASON = 0,
43         FOREIGN_EXCEPTION_CAUGHT = 1,
44         FATAL_PHASE2_ERROR = 2,
45         FATAL_PHASE1_ERROR = 3,
46         NORMAL_STOP = 4,
47         END_OF_STACK = 5,
48         HANDLER_FOUND = 6,
49         INSTALL_CONTEXT = 7,
50         CONTINUE_UNWIND = 8
51     }
52
53     enum _Unwind_Action : int
54     {
55         SEARCH_PHASE = 1,
56         CLEANUP_PHASE = 2,
57         HANDLER_FRAME = 4,
58         FORCE_UNWIND = 8
59     }
60
61     alias void* _Unwind_Context_Ptr;
62
63     alias void function(_Unwind_Reason_Code, _Unwind_Exception*) _Unwind_Exception_Cleanup_Fn;
64
65     struct _Unwind_Exception
66     {
67         ulong exception_class;
68         _Unwind_Exception_Cleanup_Fn exception_cleanup;
69         ptrdiff_t private_1;
70         ptrdiff_t private_2;
71     }
72
73 // interface to HP's libunwind from http://www.nongnu.org/libunwind/
74 version(HP_LIBUNWIND)
75 {
76     void __libunwind_Unwind_Resume(_Unwind_Exception *);
77     _Unwind_Reason_Code __libunwind_Unwind_RaiseException(_Unwind_Exception *);
78     ptrdiff_t __libunwind_Unwind_GetLanguageSpecificData(_Unwind_Context_Ptr
79             context);
80     ptrdiff_t __libunwind_Unwind_GetIP(_Unwind_Context_Ptr context);
81     ptrdiff_t __libunwind_Unwind_SetIP(_Unwind_Context_Ptr context,
82             ptrdiff_t new_value);
83     ptrdiff_t __libunwind_Unwind_SetGR(_Unwind_Context_Ptr context, int index,
84             ptrdiff_t new_value);
85     ptrdiff_t __libunwind_Unwind_GetRegionStart(_Unwind_Context_Ptr context);
86
87     alias __libunwind_Unwind_Resume _Unwind_Resume;
88     alias __libunwind_Unwind_RaiseException _Unwind_RaiseException;
89     alias __libunwind_Unwind_GetLanguageSpecificData
90         _Unwind_GetLanguageSpecificData;
91     alias __libunwind_Unwind_GetIP _Unwind_GetIP;
92     alias __libunwind_Unwind_SetIP _Unwind_SetIP;
93     alias __libunwind_Unwind_SetGR _Unwind_SetGR;
94     alias __libunwind_Unwind_GetRegionStart _Unwind_GetRegionStart;
95 }
96 else version(X86_UNWIND)
97 {
98     void _Unwind_Resume(_Unwind_Exception*);
99     _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*);
100     ptrdiff_t _Unwind_GetLanguageSpecificData(_Unwind_Context_Ptr context);
101     ptrdiff_t _Unwind_GetIP(_Unwind_Context_Ptr context);
102     ptrdiff_t _Unwind_SetIP(_Unwind_Context_Ptr context, ptrdiff_t new_value);
103     ptrdiff_t _Unwind_SetGR(_Unwind_Context_Ptr context, int index,
104             ptrdiff_t new_value);
105     ptrdiff_t _Unwind_GetRegionStart(_Unwind_Context_Ptr context);
106 }
107 else
108 {
109     // runtime calls these directly
110     void _Unwind_Resume(_Unwind_Exception*)
111     {
112         console("_Unwind_Resume is not implemented on this platform.\n");
113     }
114     _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*)
115     {
116         console("_Unwind_RaiseException is not implemented on this platform.\n");
117         return _Unwind_Reason_Code.FATAL_PHASE1_ERROR;
118     }
119 }
120
121 }
122
123 // error and exit
124 extern(C) private void fatalerror(char* format, ...)
125 {
126   va_list args;
127   va_start(args, format);
128   printf("Fatal error in EH code: ");
129   vprintf(format, args);
130   printf("\n");
131   abort();
132 }
133
134
135 // helpers for reading certain DWARF data
136 private ubyte* get_uleb128(ubyte* addr, ref size_t res)
137 {
138   res = 0;
139   size_t bitsize = 0;
140
141   // read as long as high bit is set
142   while(*addr & 0x80) {
143     res |= (*addr & 0x7f) << bitsize;
144     bitsize += 7;
145     addr += 1;
146     if(bitsize >= size_t.sizeof*8)
147        fatalerror("tried to read uleb128 that exceeded size of size_t");
148   }
149   // read last
150   if(bitsize != 0 && *addr >= 1 << size_t.sizeof*8 - bitsize)
151     fatalerror("Fatal error in EH code: tried to read uleb128 that exceeded size of size_t");
152   res |= (*addr) << bitsize;
153
154   return addr + 1;
155 }
156
157 private ubyte* get_sleb128(ubyte* addr, ref ptrdiff_t res)
158 {
159   res = 0;
160   size_t bitsize = 0;
161
162   // read as long as high bit is set
163   while(*addr & 0x80) {
164     res |= (*addr & 0x7f) << bitsize;
165     bitsize += 7;
166     addr += 1;
167     if(bitsize >= size_t.sizeof*8)
168        fatalerror("tried to read sleb128 that exceeded size of size_t");
169   }
170   // read last
171   if(bitsize != 0 && *addr >= 1 << size_t.sizeof*8 - bitsize)
172     fatalerror("tried to read sleb128 that exceeded size of size_t");
173   res |= (*addr) << bitsize;
174
175   // take care of sign
176   if(bitsize < size_t.sizeof*8 && ((*addr) & 0x40))
177     res |= cast(ptrdiff_t)(-1) ^ ((1 << (bitsize+7)) - 1);
178
179   return addr + 1;
180 }
181
182
183 // exception struct used by the runtime.
184 // _d_throw allocates a new instance and passes the address of its
185 // _Unwind_Exception member to the unwind call. The personality
186 // routine is then able to get the whole struct by looking at the data
187 // surrounding the unwind info.
188 struct _d_exception
189 {
190   Object exception_object;
191   _Unwind_Exception unwind_info;
192 }
193
194 // the 8-byte string identifying the type of exception
195 // the first 4 are for vendor, the second 4 for language
196 //TODO: This may be the wrong way around
197 char[8] _d_exception_class = "LLDCD1\0\0";
198
199
200 //
201 // x86 unwind specific implementation of personality function
202 // and helpers
203 //
204 version(X86_UNWIND)
205 {
206
207 // the personality routine gets called by the unwind handler and is responsible for
208 // reading the EH tables and deciding what to do
209 extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, ulong exception_class, _Unwind_Exception* exception_info, _Unwind_Context_Ptr context)
210 {
211   debug(EH_personality_verbose) printf("entering personality function. context: %p\n", context);
212   // check ver: the C++ Itanium ABI only allows ver == 1
213   if(ver != 1)
214     return _Unwind_Reason_Code.FATAL_PHASE1_ERROR;
215
216   // check exceptionClass
217   //TODO: Treat foreign exceptions with more respect
218   if((cast(char*)&exception_class)[0..8] != _d_exception_class)
219     return _Unwind_Reason_Code.FATAL_PHASE1_ERROR;
220
221   // find call site table, action table and classinfo table
222   // Note: callsite and action tables do not contain static-length
223   // data and will be parsed as needed
224   // Note: classinfo_table points past the end of the table
225   ubyte* callsite_table;
226   ubyte* action_table;
227   ClassInfo* classinfo_table;
228   _d_getLanguageSpecificTables(context, callsite_table, action_table, classinfo_table);
229   if (callsite_table is null)
230     return _Unwind_Reason_Code.CONTINUE_UNWIND;
231
232   /*
233     find landing pad and action table index belonging to ip by walking
234     the callsite_table
235   */
236   ubyte* callsite_walker = callsite_table;
237
238   // get the instruction pointer
239   // will be used to find the right entry in the callsite_table
240   // -1 because it will point past the last instruction
241   ptrdiff_t ip = _Unwind_GetIP(context) - 1;
242
243   // address block_start is relative to
244   ptrdiff_t region_start = _Unwind_GetRegionStart(context);
245
246   // table entries
247   uint block_start_offset, block_size;
248   ptrdiff_t landing_pad;
249   size_t action_offset;
250
251   while(true) {
252     // if we've gone through the list and found nothing...
253     if(callsite_walker >= action_table)
254       return _Unwind_Reason_Code.CONTINUE_UNWIND;
255
256     block_start_offset = *cast(uint*)callsite_walker;
257     block_size = *(cast(uint*)callsite_walker + 1);
258     landing_pad = *(cast(uint*)callsite_walker + 2);
259     if(landing_pad)
260       landing_pad += region_start;
261     callsite_walker = get_uleb128(callsite_walker + 3*uint.sizeof, action_offset);
262
263     debug(EH_personality_verbose) printf("ip=%llx %d %d %llx\n", ip, block_start_offset, block_size, landing_pad);
264
265     // since the list is sorted, as soon as we're past the ip
266     // there's no handler to be found
267     if(ip < region_start + block_start_offset)
268       return _Unwind_Reason_Code.CONTINUE_UNWIND;
269
270     // if we've found our block, exit
271     if(ip < region_start + block_start_offset + block_size)
272       break;
273   }
274
275   debug(EH_personality) printf("Found correct landing pad and actionOffset %d\n", action_offset);
276
277   // now we need the exception's classinfo to find a handler
278   // the exception_info is actually a member of a larger _d_exception struct
279   // the runtime allocated. get that now
280   _d_exception* exception_struct = cast(_d_exception*)(cast(ubyte*)exception_info - _d_exception.unwind_info.offsetof);
281
282   // if there's no action offset and no landing pad, continue unwinding
283   if(!action_offset && !landing_pad)
284     return _Unwind_Reason_Code.CONTINUE_UNWIND;
285
286   // if there's no action offset but a landing pad, this is a cleanup handler
287   else if(!action_offset && landing_pad)
288     return _d_eh_install_finally_context(actions, landing_pad, exception_struct, context);
289
290   /*
291    walk action table chain, comparing classinfos using _d_isbaseof
292   */
293   ubyte* action_walker = action_table + action_offset - 1;
294
295   ptrdiff_t ti_offset, next_action_offset;
296   while(true) {
297     action_walker = get_sleb128(action_walker, ti_offset);
298     // it is intentional that we not modify action_walker here
299     // next_action_offset is from current action_walker position
300     get_sleb128(action_walker, next_action_offset);
301
302     // negative are 'filters' which we don't use
303     if(!(ti_offset >= 0))
304       fatalerror("Filter actions are unsupported");
305
306     // zero means cleanup, which we require to be the last action
307     if(ti_offset == 0) {
308       if(!(next_action_offset == 0))
309         fatalerror("Cleanup action must be last in chain");
310       return _d_eh_install_finally_context(actions, landing_pad, exception_struct, context);
311     }
312
313     // get classinfo for action and check if the one in the
314     // exception structure is a base
315     ClassInfo catch_ci = *(classinfo_table - ti_offset);
316     debug(EH_personality) printf("Comparing catch %s to exception %s\n", catch_ci.name.ptr, exception_struct.exception_object.classinfo.name.ptr);
317     if(_d_isbaseof(exception_struct.exception_object.classinfo, catch_ci))
318       return _d_eh_install_catch_context(actions, ti_offset, landing_pad, exception_struct, context);
319
320     // we've walked through all actions and found nothing...
321     if(next_action_offset == 0)
322       return _Unwind_Reason_Code.CONTINUE_UNWIND;
323     else
324       action_walker += next_action_offset;
325   }
326
327   fatalerror("reached unreachable");
328   return _Unwind_Reason_Code.FATAL_PHASE1_ERROR;
329 }
330
331 // These are the register numbers for SetGR that
332 // llvm's eh.exception and eh.selector intrinsics
333 // will pick up.
334 // Hints for these can be found by looking at the
335 // EH_RETURN_DATA_REGNO macro in GCC, careful testing
336 // is required though.
337 version (X86_64)
338 {
339   private int eh_exception_regno = 0;
340   private int eh_selector_regno = 1;
341 } else {
342   private int eh_exception_regno = 0;
343   private int eh_selector_regno = 2;
344 }
345
346 private _Unwind_Reason_Code _d_eh_install_catch_context(_Unwind_Action actions, ptrdiff_t switchval, ptrdiff_t landing_pad, _d_exception* exception_struct, _Unwind_Context_Ptr context)
347 {
348   debug(EH_personality) printf("Found catch clause!\n");
349
350   if(actions & _Unwind_Action.SEARCH_PHASE)
351     return _Unwind_Reason_Code.HANDLER_FOUND;
352
353   else if(actions & _Unwind_Action.CLEANUP_PHASE)
354   {
355     debug(EH_personality) printf("Setting switch value to: %d!\n", switchval);
356     _Unwind_SetGR(context, eh_exception_regno, cast(ptrdiff_t)cast(void*)(exception_struct.exception_object));
357     _Unwind_SetGR(context, eh_selector_regno, cast(ptrdiff_t)switchval);
358     _Unwind_SetIP(context, landing_pad);
359     return _Unwind_Reason_Code.INSTALL_CONTEXT;
360   }
361
362   fatalerror("reached unreachable");
363   return _Unwind_Reason_Code.FATAL_PHASE2_ERROR;
364 }
365
366 private _Unwind_Reason_Code _d_eh_install_finally_context(_Unwind_Action actions, ptrdiff_t landing_pad, _d_exception* exception_struct, _Unwind_Context_Ptr context)
367 {
368   // if we're merely in search phase, continue
369   if(actions & _Unwind_Action.SEARCH_PHASE)
370     return _Unwind_Reason_Code.CONTINUE_UNWIND;
371
372   debug(EH_personality) printf("Calling cleanup routine...\n");
373
374   _Unwind_SetGR(context, eh_exception_regno, cast(ptrdiff_t)exception_struct);
375   _Unwind_SetGR(context, eh_selector_regno, 0);
376   _Unwind_SetIP(context, landing_pad);
377   return _Unwind_Reason_Code.INSTALL_CONTEXT;
378 }
379
380 private void _d_getLanguageSpecificTables(_Unwind_Context_Ptr context, ref ubyte* callsite, ref ubyte* action, ref ClassInfo* ci)
381 {
382   ubyte* data = cast(ubyte*)_Unwind_GetLanguageSpecificData(context);
383   if (data is null)
384   {
385     //printf("language specific data was null\n");
386     callsite = null;
387     action = null;
388     ci = null;
389     return;
390   }
391
392   //TODO: Do proper DWARF reading here
393   if(*data++ != 0xff)
394     fatalerror("DWARF header has unexpected format 1");
395
396   if(*data++ != 0x00)
397     fatalerror("DWARF header has unexpected format 2");
398   size_t cioffset;
399   data = get_uleb128(data, cioffset);
400   ci = cast(ClassInfo*)(data + cioffset);
401
402   if(*data++ != 0x03)
403     fatalerror("DWARF header has unexpected format 3");
404   size_t callsitelength;
405   data = get_uleb128(data, callsitelength);
406   action = data + callsitelength;
407
408   callsite = data;
409 }
410
411 } // end of x86 Linux specific implementation
412
413
414 extern(C) void _d_throw_exception(Object e)
415 {
416     if (e !is null)
417     {
418         _d_exception* exc_struct = new _d_exception;
419         exc_struct.unwind_info.exception_class = *cast(ulong*)_d_exception_class.ptr;
420         exc_struct.exception_object = e;
421         _Unwind_Reason_Code ret = _Unwind_RaiseException(&exc_struct.unwind_info);
422         console("_Unwind_RaiseException failed with reason code: ")(ret)("\n");
423     }
424     abort();
425 }
426
427 extern(C) void _d_eh_resume_unwind(_d_exception* exception_struct)
428 {
429   _Unwind_Resume(&exception_struct.unwind_info);
430 }
Note: See TracBrowser for help on using the browser.
Copyright © 2008, LDC Development Team.