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 }