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