comparison druntime/src/compiler/ldc/eh.d @ 759:d3eb054172f9

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