comparison lphobos/internal/eh.d @ 473:373489eeaf90

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