1 /**
2  * This module contains functions and structures required for
3  * exception handling.
4  */
5 module rt.eh;
6 
7 import ldc.cstdarg;
8 import rt.compiler.util.console;
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     version(freebsd) 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     version(freebsd) version=X86_UNWIND;
25 }
26 
27 //version = HP_LIBUNWIND;
28 
29 private extern(C) void abort();
30 private extern(C) int printf(char*, ...);
31 private extern(C) int vprintf(char*, va_list va);
32 
33 // D runtime functions
34 extern(C) {
35     int _d_isbaseof(ClassInfo oc, ClassInfo c);
36 }
37 
38 // libunwind headers
39 extern(C)
40 {
41     enum _Unwind_Reason_Code : int
42     {
43         NO_REASON = 0,
44         FOREIGN_EXCEPTION_CAUGHT = 1,
45         FATAL_PHASE2_ERROR = 2,
46         FATAL_PHASE1_ERROR = 3,
47         NORMAL_STOP = 4,
48         END_OF_STACK = 5,
49         HANDLER_FOUND = 6,
50         INSTALL_CONTEXT = 7,
51         CONTINUE_UNWIND = 8
52     }
53 
54     enum _Unwind_Action : int
55     {
56         SEARCH_PHASE = 1,
57         CLEANUP_PHASE = 2,
58         HANDLER_PHASE = 3,
59         FORCE_UNWIND = 4
60     }
61 
62     alias void* _Unwind_Context_Ptr;
63 
64     alias void function(_Unwind_Reason_Code, _Unwind_Exception*) _Unwind_Exception_Cleanup_Fn;
65 
66     struct _Unwind_Exception
67     {
68         ulong exception_class;
69         _Unwind_Exception_Cleanup_Fn exception_cleanup;
70         ptrdiff_t private_1;
71         ptrdiff_t private_2;
72     }
73 
74 // interface to HP's libunwind from http://www.nongnu.org/libunwind/
75 version(HP_LIBUNWIND)
76 {
77     // Haven't checked whether and how it has _Unwind_Get{Text,Data}RelBase
78     pragma (msg, "HP_LIBUNWIND interface is out of date and untested");
79 
80     void __libunwind_Unwind_Resume(_Unwind_Exception *);
81     _Unwind_Reason_Code __libunwind_Unwind_RaiseException(_Unwind_Exception *);
82     ptrdiff_t __libunwind_Unwind_GetLanguageSpecificData(_Unwind_Context_Ptr
83             context);
84     ptrdiff_t __libunwind_Unwind_GetIP(_Unwind_Context_Ptr context);
85     ptrdiff_t __libunwind_Unwind_SetIP(_Unwind_Context_Ptr context,
86             ptrdiff_t new_value);
87     ptrdiff_t __libunwind_Unwind_SetGR(_Unwind_Context_Ptr context, int index,
88             ptrdiff_t new_value);
89     ptrdiff_t __libunwind_Unwind_GetRegionStart(_Unwind_Context_Ptr context);
90 
91     alias __libunwind_Unwind_Resume _Unwind_Resume;
92     alias __libunwind_Unwind_RaiseException _Unwind_RaiseException;
93     alias __libunwind_Unwind_GetLanguageSpecificData
94         _Unwind_GetLanguageSpecificData;
95     alias __libunwind_Unwind_GetIP _Unwind_GetIP;
96     alias __libunwind_Unwind_SetIP _Unwind_SetIP;
97     alias __libunwind_Unwind_SetGR _Unwind_SetGR;
98     alias __libunwind_Unwind_GetRegionStart _Unwind_GetRegionStart;
99 }
100 else version(X86_UNWIND)
101 {
102     void _Unwind_Resume(_Unwind_Exception*);
103     _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*);
104     ptrdiff_t _Unwind_GetLanguageSpecificData(_Unwind_Context_Ptr context);
105     ptrdiff_t _Unwind_GetIP(_Unwind_Context_Ptr context);
106     ptrdiff_t _Unwind_SetIP(_Unwind_Context_Ptr context, ptrdiff_t new_value);
107     ptrdiff_t _Unwind_SetGR(_Unwind_Context_Ptr context, int index,
108             ptrdiff_t new_value);
109     ptrdiff_t _Unwind_GetRegionStart(_Unwind_Context_Ptr context);
110 
111     size_t _Unwind_GetTextRelBase(_Unwind_Context_Ptr);
112     size_t _Unwind_GetDataRelBase(_Unwind_Context_Ptr);
113 }
114 else
115 {
116     // runtime calls these directly
117     void _Unwind_Resume(_Unwind_Exception*)
118     {
119         console("_Unwind_Resume is not implemented on this platform.\n");
120     }
121     _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*)
122     {
123         console("_Unwind_RaiseException is not implemented on this platform.\n");
124         return _Unwind_Reason_Code.FATAL_PHASE1_ERROR;
125     }
126 }
127 
128 }
129 
130 // error and exit
131 extern(C) private void fatalerror(char[] format)
132 {
133   printf("Fatal error in EH code: %.*s\n", format.length, format.ptr);
134   abort();
135 }
136 
137 
138 // DWARF EH encoding enum
139 // See e.g. http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/dwarfext.html
140 private enum : ubyte {
141   DW_EH_PE_omit    = 0xff, // value is not present
142 
143   // value format
144   DW_EH_PE_absptr  = 0x00, // literal pointer
145   DW_EH_PE_uleb128 = 0x01,
146   DW_EH_PE_udata2  = 0x02, // unsigned 2-byte
147   DW_EH_PE_udata4  = 0x03,
148   DW_EH_PE_udata8  = 0x04,
149   DW_EH_PE_sleb128 = 0x09,
150   DW_EH_PE_sdata2  = 0x0a,
151   DW_EH_PE_sdata4  = 0x0b,
152   DW_EH_PE_sdata8  = 0x0c,
153 
154   // value meaning
155   DW_EH_PE_pcrel    = 0x10, // relative to program counter
156   DW_EH_PE_textrel  = 0x20, // relative to .text
157   DW_EH_PE_datarel  = 0x30, // relative to .got or .eh_frame_hdr
158   DW_EH_PE_funcrel  = 0x40, // relative to beginning of function
159   DW_EH_PE_aligned  = 0x50, // is an aligned void*
160 
161   // value is a pointer to the actual value
162   // this is a mask on top of one of the above
163   DW_EH_PE_indirect = 0x80
164 }
165 
166 // Helpers for reading DWARF data
167 
168 // Given an encoding and a context, return the base to which the encoding is
169 // relative
170 private size_t base_of_encoded(_Unwind_Context_Ptr context, ubyte encoding)
171 {
172   if (encoding == DW_EH_PE_omit)
173     return 0;
174 
175   switch (encoding & 0x70) // ignore DW_EH_PE_indirect
176   {
177     case DW_EH_PE_absptr, DW_EH_PE_pcrel, DW_EH_PE_aligned:
178       return 0;
179 
180     case DW_EH_PE_textrel: return _Unwind_GetTextRelBase(context);
181     case DW_EH_PE_datarel: return _Unwind_GetDataRelBase(context);
182     case DW_EH_PE_funcrel: return _Unwind_GetRegionStart(context);
183 
184     default: fatalerror("Unrecognized base for DWARF value");
185   }
186   assert(0);
187 }
188 
189 // Only defined for fixed-size encodings
190 private size_t size_of_encoded(ubyte encoding)
191 {
192   if (encoding == DW_EH_PE_omit)
193     return 0;
194 
195   switch (encoding & 0x07) // ignore leb128
196   {
197     case DW_EH_PE_absptr: return (void*).sizeof;
198     case DW_EH_PE_udata2: return 2;
199     case DW_EH_PE_udata4: return 4;
200     case DW_EH_PE_udata8: return 8;
201 
202     default: fatalerror("Unrecognized fixed-size DWARF value encoding");
203   }
204   assert(0);
205 }
206 
207 // Actual value readers below: read a value from the given ubyte* into the
208 // output parameter and return the pointer incremented past the value.
209 
210 // Like read_encoded_with_base but gets the base from the given context
211 private ubyte* read_encoded(_Unwind_Context_Ptr context, ubyte encoding, ubyte* p, out size_t val)
212 {
213   return read_encoded_with_base(encoding, base_of_encoded(context, encoding), p, val);
214 }
215 
216 private ubyte* read_encoded_with_base(ubyte encoding, size_t base, ubyte* p, out size_t val)
217 {
218   if (encoding == DW_EH_PE_aligned)
219   {
220     auto a = cast(size_t)p;
221     a = (a + (void*).sizeof - 1) & -(void*).sizeof;
222     val = *cast(size_t*)a;
223     return cast(ubyte*)(a + (void*).sizeof);
224   }
225 
226   union U
227   {
228     size_t ptr;
229     ushort udata2;
230     uint   udata4;
231     ulong  udata8;
232     short  sdata2;
233     int    sdata4;
234     long   sdata8;
235   }
236 
237   auto u = cast(U*)p;
238 
239   size_t result;
240 
241   switch (encoding & 0x0f)
242   {
243     case DW_EH_PE_absptr:
244       result = u.ptr;
245       p += (void*).sizeof;
246       break;
247 
248     case DW_EH_PE_uleb128:
249     {
250       p = get_uleb128(p, result);
251       break;
252     }
253     case DW_EH_PE_sleb128:
254     {
255       ptrdiff_t sleb128;
256       p = get_sleb128(p, sleb128);
257       result = cast(size_t)sleb128;
258       break;
259     }
260 
261     case DW_EH_PE_udata2: result = cast(size_t)u.udata2; p += 2; break;
262     case DW_EH_PE_udata4: result = cast(size_t)u.udata4; p += 4; break;
263     case DW_EH_PE_udata8: result = cast(size_t)u.udata8; p += 8; break;
264     case DW_EH_PE_sdata2: result = cast(size_t)u.sdata2; p += 2; break;
265     case DW_EH_PE_sdata4: result = cast(size_t)u.sdata4; p += 4; break;
266     case DW_EH_PE_sdata8: result = cast(size_t)u.sdata8; p += 8; break;
267 
268     default: fatalerror("Unrecognized DWARF value encoding format");
269   }
270   if (result)
271   {
272     if ((encoding & 0x70) == DW_EH_PE_pcrel)
273       result += cast(size_t)u;
274     else
275       result += base;
276 
277     if (encoding & DW_EH_PE_indirect)
278       result = *cast(size_t*)result;
279   }
280   val = result;
281   return p;
282 }
283 
284 private ubyte* get_uleb128(ubyte* addr, ref size_t res)
285 {
286   res = 0;
287   size_t bitsize = 0;
288 
289   // read as long as high bit is set
290   while(*addr & 0x80) {
291     res |= (*addr & 0x7f) << bitsize;
292     bitsize += 7;
293     addr += 1;
294     if(bitsize >= size_t.sizeof*8)
295        fatalerror("tried to read uleb128 that exceeded size of size_t");
296   }
297   // read last
298   if(bitsize != 0 && *addr >= 1 << size_t.sizeof*8 - bitsize)
299     fatalerror("Fatal error in EH code: tried to read uleb128 that exceeded size of size_t");
300   res |= (*addr) << bitsize;
301 
302   return addr + 1;
303 }
304 
305 private ubyte* get_sleb128(ubyte* addr, ref ptrdiff_t res)
306 {
307   res = 0;
308   size_t bitsize = 0;
309 
310   // read as long as high bit is set
311   while(*addr & 0x80) {
312     res |= (*addr & 0x7f) << bitsize;
313     bitsize += 7;
314     addr += 1;
315     if(bitsize >= size_t.sizeof*8)
316        fatalerror("tried to read sleb128 that exceeded size of size_t");
317   }
318   // read last
319   if(bitsize != 0 && *addr >= 1 << size_t.sizeof*8 - bitsize)
320     fatalerror("tried to read sleb128 that exceeded size of size_t");
321   res |= (*addr) << bitsize;
322 
323   // take care of sign
324   if(bitsize < size_t.sizeof*8 && ((*addr) & 0x40))
325     res |= cast(ptrdiff_t)(-1) ^ ((1 << (bitsize+7)) - 1);
326 
327   return addr + 1;
328 }
329 
330 
331 // exception struct used by the runtime.
332 // _d_throw allocates a new instance and passes the address of its
333 // _Unwind_Exception member to the unwind call. The personality
334 // routine is then able to get the whole struct by looking at the data
335 // surrounding the unwind info.
336 struct _d_exception
337 {
338   Object exception_object;
339   _Unwind_Exception unwind_info;
340 }
341 
342 // the 8-byte string identifying the type of exception
343 // the first 4 are for vendor, the second 4 for language
344 //TODO: This may be the wrong way around
345 const char[8] _d_exception_class = "LLDCD1\0\0";
346 
347 
348 //
349 // x86 unwind specific implementation of personality function
350 // and helpers
351 //
352 version(X86_UNWIND)
353 {
354 
355 // Various stuff we need
356 struct Region
357 {
358   ubyte* callsite_table;
359   ubyte* action_table;
360 
361   // Note: classinfo_table points past the end of the table
362   ubyte* classinfo_table;
363 
364   ptrdiff_t start;
365   size_t lpStart_base; // landing pad base
366 
367   ubyte ttypeEnc;
368   size_t ttype_base; // typeinfo base
369 
370   ubyte callSiteEnc;
371 }
372 
373 // the personality routine gets called by the unwind handler and is responsible for
374 // reading the EH tables and deciding what to do
375 extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, ulong exception_class, _Unwind_Exception* exception_info, _Unwind_Context_Ptr context)
376 {
377   // check ver: the C++ Itanium ABI only allows ver == 1
378   if(ver != 1)
379     return _Unwind_Reason_Code.FATAL_PHASE1_ERROR;
380 
381   // check exceptionClass
382   //TODO: Treat foreign exceptions with more respect
383   if((cast(char*)&exception_class)[0..8] != _d_exception_class)
384     return _Unwind_Reason_Code.FATAL_PHASE1_ERROR;
385 
386   // find call site table, action table and classinfo table
387   // Note: callsite and action tables do not contain static-length
388   // data and will be parsed as needed
389 
390   Region region;
391 
392   _d_getLanguageSpecificTables(context, region);
393   if (!region.callsite_table)
394     return _Unwind_Reason_Code.CONTINUE_UNWIND;
395 
396   /*
397     find landing pad and action table index belonging to ip by walking
398     the callsite_table
399   */
400   ubyte* callsite_walker = region.callsite_table;
401 
402   // get the instruction pointer
403   // will be used to find the right entry in the callsite_table
404   // -1 because it will point past the last instruction
405   ptrdiff_t ip = _Unwind_GetIP(context) - 1;
406 
407   // table entries
408   size_t landing_pad;
409   size_t action_offset;
410 
411   while(true) {
412     // if we've gone through the list and found nothing...
413     if(callsite_walker >= region.action_table)
414       return _Unwind_Reason_Code.CONTINUE_UNWIND;
415 
416     size_t block_start, block_size;
417 
418     callsite_walker = read_encoded(null, region.callSiteEnc, callsite_walker, block_start);
419     callsite_walker = read_encoded(null, region.callSiteEnc, callsite_walker, block_size);
420     callsite_walker = read_encoded(null, region.callSiteEnc, callsite_walker, landing_pad);
421     callsite_walker = get_uleb128(callsite_walker, action_offset);
422 
423     debug(EH_personality_verbose) printf("ip=%zx %d %d %zx\n", ip, block_start, block_size, landing_pad);
424 
425     // since the list is sorted, as soon as we're past the ip
426     // there's no handler to be found
427     if(ip < region.start + block_start)
428       return _Unwind_Reason_Code.CONTINUE_UNWIND;
429 
430     if(landing_pad)
431       landing_pad += region.lpStart_base;
432 
433     // if we've found our block, exit
434     if(ip < region.start + block_start + block_size)
435       break;
436   }
437 
438   debug(EH_personality) printf("Found correct landing pad %zx and actionOffset %zx\n", landing_pad, action_offset);
439 
440   // now we need the exception's classinfo to find a handler
441   // the exception_info is actually a member of a larger _d_exception struct
442   // the runtime allocated. get that now
443   _d_exception* exception_struct = cast(_d_exception*)(cast(ubyte*)exception_info - _d_exception.unwind_info.offsetof);
444 
445   // if there's no action offset and no landing pad, continue unwinding
446   if(!action_offset && !landing_pad)
447     return _Unwind_Reason_Code.CONTINUE_UNWIND;
448 
449   // if there's no action offset but a landing pad, this is a cleanup handler
450   else if(!action_offset && landing_pad)
451     return _d_eh_install_finally_context(actions, cast(ptrdiff_t)landing_pad, exception_struct, context);
452 
453   /*
454    walk action table chain, comparing classinfos using _d_isbaseof
455   */
456   ubyte* action_walker = region.action_table + action_offset - 1;
457 
458   while(true) {
459     ptrdiff_t ti_offset, next_action_offset;
460 
461     action_walker = get_sleb128(action_walker, ti_offset);
462     // it is intentional that we not modify action_walker here
463     // next_action_offset is from current action_walker position
464     get_sleb128(action_walker, next_action_offset);
465 
466     // negative are 'filters' which we don't use
467     if(ti_offset < 0)
468       fatalerror("Filter actions are unsupported");
469 
470     // zero means cleanup, which we require to be the last action
471     if(ti_offset == 0) {
472       if(next_action_offset != 0)
473         fatalerror("Cleanup action must be last in chain");
474       return _d_eh_install_finally_context(actions, cast(ptrdiff_t)landing_pad, exception_struct, context);
475     }
476 
477     // get classinfo for action and check if the one in the
478     // exception structure is a base
479     size_t typeinfo;
480     auto filter = ti_offset * size_of_encoded(region.ttypeEnc);
481     read_encoded_with_base(region.ttypeEnc, region.ttype_base, region.classinfo_table - filter, typeinfo);
482 
483     debug(EH_personality_verbose)
484       printf("classinfo at %zx (enc %zx (size %zx) base %zx ptr %zx)\n", typeinfo, region.ttypeEnc, size_of_encoded(region.ttypeEnc), region.ttype_base, region.classinfo_table - filter);
485 
486     auto catch_ci = *cast(ClassInfo*)&typeinfo;
487 
488     debug(EH_personality) printf("Comparing catch %s to exception %s\n", catch_ci.name.ptr, exception_struct.exception_object.classinfo.name.ptr);
489     if(_d_isbaseof(exception_struct.exception_object.classinfo, catch_ci))
490       return _d_eh_install_catch_context(actions, ti_offset, cast(ptrdiff_t)landing_pad, exception_struct, context);
491 
492     // we've walked through all actions and found nothing...
493     if(next_action_offset == 0)
494       return _Unwind_Reason_Code.CONTINUE_UNWIND;
495     else
496       action_walker += next_action_offset;
497   }
498 
499   fatalerror("reached unreachable");
500   return _Unwind_Reason_Code.FATAL_PHASE1_ERROR;
501 }
502 
503 // These are the register numbers for SetGR that
504 // llvm's eh.exception and eh.selector intrinsics
505 // will pick up.
506 // Hints for these can be found by looking at the
507 // EH_RETURN_DATA_REGNO macro in GCC, careful testing
508 // is required though.
509 version (X86_64)
510 {
511   private int eh_exception_regno = 0;
512   private int eh_selector_regno = 1;
513 } else {
514   private int eh_exception_regno = 0;
515   private int eh_selector_regno = 2;
516 }
517 
518 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)
519 {
520   debug(EH_personality) printf("Found catch clause!\n");
521 
522   if(actions & _Unwind_Action.SEARCH_PHASE)
523     return _Unwind_Reason_Code.HANDLER_FOUND;
524 
525   else if(actions & _Unwind_Action.HANDLER_PHASE)
526   {
527     debug(EH_personality) printf("Setting switch value to: %d!\n", switchval);
528     _Unwind_SetGR(context, eh_exception_regno, cast(ptrdiff_t)cast(void*)(exception_struct.exception_object));
529     _Unwind_SetGR(context, eh_selector_regno, cast(ptrdiff_t)switchval);
530     _Unwind_SetIP(context, landing_pad);
531     return _Unwind_Reason_Code.INSTALL_CONTEXT;
532   }
533 
534   fatalerror("reached unreachable");
535   return _Unwind_Reason_Code.FATAL_PHASE2_ERROR;
536 }
537 
538 private _Unwind_Reason_Code _d_eh_install_finally_context(_Unwind_Action actions, ptrdiff_t landing_pad, _d_exception* exception_struct, _Unwind_Context_Ptr context)
539 {
540   // if we're merely in search phase, continue
541   if(actions & _Unwind_Action.SEARCH_PHASE)
542     return _Unwind_Reason_Code.CONTINUE_UNWIND;
543 
544   debug(EH_personality) printf("Calling cleanup routine...\n");
545 
546   _Unwind_SetGR(context, eh_exception_regno, cast(ptrdiff_t)exception_struct);
547   _Unwind_SetGR(context, eh_selector_regno, 0);
548   _Unwind_SetIP(context, landing_pad);
549   return _Unwind_Reason_Code.INSTALL_CONTEXT;
550 }
551 
552 private void _d_getLanguageSpecificTables(_Unwind_Context_Ptr context, out Region region)
553 {
554   auto data = cast(ubyte*)_Unwind_GetLanguageSpecificData(context);
555   if (!data)
556     return;
557 
558   region.start = _Unwind_GetRegionStart(context);
559 
560   // Read the C++-style LSDA: this is implementation-defined by GCC but LLVM
561   // outputs the same kind of table
562 
563   // Get @LPStart: landing pad offsets are relative to it
564   auto lpStartEnc = *data++;
565   if (lpStartEnc == DW_EH_PE_omit)
566     region.lpStart_base = region.start;
567   else
568     data = read_encoded(context, lpStartEnc, data, region.lpStart_base);
569 
570   // Get @TType: the offset to the handler and typeinfo
571   region.ttypeEnc = *data++;
572   if (region.ttypeEnc == DW_EH_PE_omit)
573     // Not sure about this one...
574     fatalerror("@TType must not be omitted from DWARF header");
575 
576   size_t ciOffset;
577   data = get_uleb128(data, ciOffset);
578   region.classinfo_table = data + ciOffset;
579 
580   region.ttype_base = base_of_encoded(context, region.ttypeEnc);
581 
582   // Get encoding and length of the call site table, which precedes the action
583   // table.
584   region.callSiteEnc = *data++;
585   if (region.callSiteEnc == DW_EH_PE_omit)
586     fatalerror("Call site table encoding must not be omitted from DWARF header");
587 
588   size_t callSiteLength;
589   region.callsite_table = get_uleb128(data, callSiteLength);
590   region.action_table = region.callsite_table + callSiteLength;
591 }
592 
593 } // end of x86 Linux specific implementation
594 
595 
596 extern(C) void _d_throw_exception(Object e)
597 {
598     if (e !is null)
599     {
600         _d_exception* exc_struct = new _d_exception;
601         exc_struct.unwind_info.exception_class = *cast(ulong*)_d_exception_class.ptr;
602         exc_struct.exception_object = e;
603         _Unwind_Reason_Code ret = _Unwind_RaiseException(&exc_struct.unwind_info);
604         console("_Unwind_RaiseException failed with reason code: ")(ret)("\n");
605     }
606     abort();
607 }
608 
609 extern(C) void _d_eh_resume_unwind(_d_exception* exception_struct)
610 {
611   _Unwind_Resume(&exception_struct.unwind_info);
612 }