Mercurial > projects > ldc
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 } |