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

root/runtime/internal/eh.d

Revision 1289:4a5eea0334e5, 13.4 kB (checked in by Benjamin Kramer <benny.kra@gmail.com>, 2 months ago)

Add runtime support for darwin x86_64

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