blob: 15621879e4f841a334eb0f53a41d9ecee8e73d72 [file] [log] [blame]
Howard Hinnant66d93272012-01-04 20:49:43 +00001//===------------------------- cxa_exception.cpp --------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is dual licensed under the MIT and the University of Illinois Open
6// Source Licenses. See LICENSE.TXT for details.
7//
8//
9// This file implements the "Exception Handling APIs"
10// http://www.codesourcery.com/public/cxx-abi/abi-eh.html
Howard Hinnantd3dba312012-01-06 20:39:47 +000011// http://www.intel.com/design/itanium/downloads/245358.htm
Howard Hinnant66d93272012-01-04 20:49:43 +000012//
13//===----------------------------------------------------------------------===//
14
15#include "unwind.h"
Howard Hinnantd3dba312012-01-06 20:39:47 +000016#include "cxa_exception.hpp"
17#include <typeinfo>
18#include <stdlib.h>
19#include <assert.h>
Howard Hinnant66d93272012-01-04 20:49:43 +000020
Howard Hinnantafcf7ac2012-01-22 19:14:27 +000021// +---------------------------+-----------------------------+---------------+
22// | __cxa_exception | _Unwind_Exception CLNGC++\0 | thrown object |
23// +---------------------------+-----------------------------+---------------+
24// ^
25// |
26// +-------------------------------------------------------+
27// |
28// +---------------------------+-----------------------------+
29// | __cxa_dependent_exception | _Unwind_Exception CLNGC++\1 |
30// +---------------------------+-----------------------------+
31
Howard Hinnant66d93272012-01-04 20:49:43 +000032namespace __cxxabiv1
33{
34
35extern "C"
36{
37
38// private API
39
Howard Hinnantd3dba312012-01-06 20:39:47 +000040// Heavily borrowed from llvm/examples/ExceptionDemo/ExceptionDemo.cpp
41
42// DWARF Constants
43enum
44{
45 DW_EH_PE_absptr = 0x00,
46 DW_EH_PE_uleb128 = 0x01,
47 DW_EH_PE_udata2 = 0x02,
48 DW_EH_PE_udata4 = 0x03,
49 DW_EH_PE_udata8 = 0x04,
50 DW_EH_PE_sleb128 = 0x09,
51 DW_EH_PE_sdata2 = 0x0A,
52 DW_EH_PE_sdata4 = 0x0B,
53 DW_EH_PE_sdata8 = 0x0C,
54 DW_EH_PE_pcrel = 0x10,
55 DW_EH_PE_textrel = 0x20,
56 DW_EH_PE_datarel = 0x30,
57 DW_EH_PE_funcrel = 0x40,
58 DW_EH_PE_aligned = 0x50,
59 DW_EH_PE_indirect = 0x80,
60 DW_EH_PE_omit = 0xFF
61};
62
63/// Read a uleb128 encoded value and advance pointer
64/// See Variable Length Data Appendix C in:
65/// @link http://dwarfstd.org/Dwarf4.pdf @unlink
66/// @param data reference variable holding memory pointer to decode from
67/// @returns decoded value
68static
69uintptr_t
70readULEB128(const uint8_t** data)
71{
72 uintptr_t result = 0;
73 uintptr_t shift = 0;
74 unsigned char byte;
75 const uint8_t *p = *data;
76 do
77 {
78 byte = *p++;
79 result |= static_cast<uintptr_t>(byte & 0x7F) << shift;
80 shift += 7;
81 } while (byte & 0x80);
82 *data = p;
83 return result;
84}
85
86/// Read a sleb128 encoded value and advance pointer
87/// See Variable Length Data Applendix C in:
88/// @link http://dwarfstd.org/Dwarf4.pdf @unlink
89/// @param data reference variable holding memory pointer to decode from
90/// @returns decoded value
91static
92uintptr_t
93readSLEB128(const uint8_t** data)
94{
95 uintptr_t result = 0;
96 uintptr_t shift = 0;
97 unsigned char byte;
98 const uint8_t *p = *data;
99 do
100 {
101 byte = *p++;
102 result |= static_cast<uintptr_t>(byte & 0x7F) << shift;
103 shift += 7;
104 } while (byte & 0x80);
105 *data = p;
106 if ((byte & 0x40) && (shift < (sizeof(result) << 3)))
107 result |= static_cast<uintptr_t>(~0) << shift;
108 return result;
109}
110
111/// Read a pointer encoded value and advance pointer
112/// See Variable Length Data in:
113/// @link http://dwarfstd.org/Dwarf3.pdf @unlink
114/// @param data reference variable holding memory pointer to decode from
115/// @param encoding dwarf encoding type
116/// @returns decoded value
117static
118uintptr_t
119readEncodedPointer(const uint8_t** data, uint8_t encoding)
120{
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000121// TODO: Not quite rgiht. This should be able to read a 0 from the TType table
122// and not dereference it. Pasted in temporayr workaround
123// TODO: Sometimes this is clearly not always reading an encoded pointer, for
124// example a length in the call site table. Needs new name?
Howard Hinnantd3dba312012-01-06 20:39:47 +0000125 uintptr_t result = 0;
126 const uint8_t* p = *data;
127 if (encoding == DW_EH_PE_omit)
128 return result;
129 // first get value
130 switch (encoding & 0x0F)
131 {
132 case DW_EH_PE_absptr:
133 result = *((uintptr_t*)p);
134 p += sizeof(uintptr_t);
135 break;
136 case DW_EH_PE_uleb128:
137 result = readULEB128(&p);
138 break;
139 case DW_EH_PE_sleb128:
140 result = readSLEB128(&p);
141 break;
142 case DW_EH_PE_udata2:
143 result = *((uint16_t*)p);
144 p += sizeof(uint16_t);
145 break;
146 case DW_EH_PE_udata4:
147 result = *((uint32_t*)p);
148 p += sizeof(uint32_t);
149 break;
150 case DW_EH_PE_udata8:
151 result = *((uint64_t*)p);
152 p += sizeof(uint64_t);
153 break;
154 case DW_EH_PE_sdata2:
155 result = *((int16_t*)p);
156 p += sizeof(int16_t);
157 break;
158 case DW_EH_PE_sdata4:
159 result = *((int32_t*)p);
160 p += sizeof(int32_t);
161 break;
162 case DW_EH_PE_sdata8:
163 result = *((int64_t*)p);
164 p += sizeof(int64_t);
165 break;
166 default:
167 // not supported
168 abort();
169 break;
170 }
171 // then add relative offset
172 switch (encoding & 0x70)
173 {
174 case DW_EH_PE_absptr:
175 // do nothing
176 break;
177 case DW_EH_PE_pcrel:
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000178 if (result)
179 result += (uintptr_t)(*data);
Howard Hinnantd3dba312012-01-06 20:39:47 +0000180 break;
181 case DW_EH_PE_textrel:
182 case DW_EH_PE_datarel:
183 case DW_EH_PE_funcrel:
184 case DW_EH_PE_aligned:
185 default:
186 // not supported
187 abort();
188 break;
189 }
190 // then apply indirection
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000191 if (result && (encoding & DW_EH_PE_indirect))
Howard Hinnantd3dba312012-01-06 20:39:47 +0000192 result = *((uintptr_t*)result);
193 *data = p;
194 return result;
195}
196
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000197static
198const uint8_t*
199getTTypeEntry(int64_t typeOffset, const uint8_t* classInfo, uint8_t ttypeEncoding)
200{
201 switch (ttypeEncoding & 0x0F)
202 {
203 case DW_EH_PE_absptr:
204 typeOffset *= sizeof(void*);
205 break;
206 case DW_EH_PE_udata2:
207 case DW_EH_PE_sdata2:
208 typeOffset *= 2;
209 break;
210 case DW_EH_PE_udata4:
211 case DW_EH_PE_sdata4:
212 typeOffset *= 4;
213 break;
214 case DW_EH_PE_udata8:
215 case DW_EH_PE_sdata8:
216 typeOffset *= 8;
217 break;
218 }
219 return classInfo - typeOffset;
220}
221
Howard Hinnantd3dba312012-01-06 20:39:47 +0000222/// Deals with Dwarf actions matching our type infos
223/// (OurExceptionType_t instances). Returns whether or not a dwarf emitted
224/// action matches the supplied exception type. If such a match succeeds,
225/// the handlerSwitchValue will be set with > 0 index value. Only
226/// corresponding llvm.eh.selector type info arguments, cleanup arguments
227/// are supported. Filters are not supported.
228/// See Variable Length Data in:
229/// @link http://dwarfstd.org/Dwarf3.pdf @unlink
230/// Also see @link http://refspecs.freestandards.org/abi-eh-1.21.html @unlink
231/// @param classInfo our array of type info pointers (to globals)
232/// @param actionEntry index into above type info array or 0 (clean up).
233/// We do not support filters.
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000234/// @param unwind_exception thrown _Unwind_Exception instance.
Howard Hinnantd3dba312012-01-06 20:39:47 +0000235/// @returns whether or not a type info was found. False is returned if only
236/// a cleanup was found
237static
238bool
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000239handleActionValue(const uint8_t* classInfo, uintptr_t actionEntry,
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000240 _Unwind_Exception* unwind_exception, uint8_t ttypeEncoding)
Howard Hinnantd3dba312012-01-06 20:39:47 +0000241{
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000242 __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1;
243 const std::type_info* excpType = exception_header->exceptionType;
Howard Hinnantd3dba312012-01-06 20:39:47 +0000244 const uint8_t* actionPos = (uint8_t*)actionEntry;
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000245 while (true)
Howard Hinnantd3dba312012-01-06 20:39:47 +0000246 {
247 // Each emitted dwarf action corresponds to a 2 tuple of
248 // type info address offset, and action offset to the next
249 // emitted action.
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000250 const uint8_t* SactionPos = actionPos;
Howard Hinnantd3dba312012-01-06 20:39:47 +0000251 int64_t typeOffset = readSLEB128(&actionPos);
252 const uint8_t* tempActionPos = actionPos;
253 int64_t actionOffset = readSLEB128(&tempActionPos);
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000254 if (typeOffset > 0) // a catch handler
Howard Hinnantd3dba312012-01-06 20:39:47 +0000255 {
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000256 const uint8_t* TTypeEntry = getTTypeEntry(typeOffset, classInfo,
257 ttypeEncoding);
258 const std::type_info* catchType =
259 (const std::type_info*)readEncodedPointer(&TTypeEntry,
260 ttypeEncoding);
261 // catchType == 0 -> catch (...)
262 if (catchType == 0 || excpType == catchType)
263 {
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000264 exception_header->handlerSwitchValue = typeOffset;
265 exception_header->actionRecord = SactionPos;
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000266 return true;
267 }
Howard Hinnantd3dba312012-01-06 20:39:47 +0000268 }
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000269 else if (typeOffset < 0) // an exception spec
270 {
271 }
272 else // typeOffset == 0 // a clean up
273 {
274 }
275 if (actionOffset == 0)
Howard Hinnantd3dba312012-01-06 20:39:47 +0000276 break;
277 actionPos += actionOffset;
278 }
279 return false;
280}
281
Howard Hinnant66d93272012-01-04 20:49:43 +0000282// Return true if there is a handler and false otherwise
283// cache handlerSwitchValue, actionRecord, languageSpecificData,
284// catchTemp and adjustedPtr here.
285static
286bool
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000287contains_handler(_Unwind_Exception* unwind_exception, _Unwind_Context* context)
Howard Hinnant66d93272012-01-04 20:49:43 +0000288{
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000289 __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1;
Howard Hinnantd3dba312012-01-06 20:39:47 +0000290 const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(context);
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000291 exception_header->languageSpecificData = lsda;
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000292 // set adjustedPtr! __cxa_get_exception_ptr and __cxa_begin_catch use it.
293 // TODO: Put it where it is supposed to be and adjust it properly
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000294 exception_header->adjustedPtr = unwind_exception+1;
Howard Hinnantd3dba312012-01-06 20:39:47 +0000295 if (lsda)
296 {
297 // Get the current instruction pointer and offset it before next
298 // instruction in the current frame which threw the exception.
299 uintptr_t pc = _Unwind_GetIP(context) - 1;
300 // Get beginning current frame's code (as defined by the
301 // emitted dwarf code)
302 uintptr_t funcStart = _Unwind_GetRegionStart(context);
303 uintptr_t pcOffset = pc - funcStart;
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000304 const uint8_t* classInfo = NULL;
Howard Hinnantd3dba312012-01-06 20:39:47 +0000305 // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding
306 // dwarf emission
307 // Parse LSDA header.
308 uint8_t lpStartEncoding = *lsda++;
309 if (lpStartEncoding != DW_EH_PE_omit)
310 (void)readEncodedPointer(&lsda, lpStartEncoding);
311 uint8_t ttypeEncoding = *lsda++;
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000312 // TODO: preflight ttypeEncoding here and return error if there's a problem
Howard Hinnantd3dba312012-01-06 20:39:47 +0000313 if (ttypeEncoding != DW_EH_PE_omit)
314 {
315 // Calculate type info locations in emitted dwarf code which
316 // were flagged by type info arguments to llvm.eh.selector
317 // intrinsic
318 uintptr_t classInfoOffset = readULEB128(&lsda);
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000319 classInfo = lsda + classInfoOffset;
Howard Hinnantd3dba312012-01-06 20:39:47 +0000320 }
321 // Walk call-site table looking for range that
322 // includes current PC.
323 uint8_t callSiteEncoding = *lsda++;
324 uint32_t callSiteTableLength = readULEB128(&lsda);
325 const uint8_t* callSiteTableStart = lsda;
326 const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength;
327 const uint8_t* actionTableStart = callSiteTableEnd;
328 const uint8_t* callSitePtr = callSiteTableStart;
Howard Hinnantd3dba312012-01-06 20:39:47 +0000329 while (callSitePtr < callSiteTableEnd)
330 {
331 uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding);
332 uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding);
333 uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding);
334 // Note: Action value
335 uintptr_t actionEntry = readULEB128(&callSitePtr);
336 if (landingPad == 0)
337 continue; // no landing pad for this entry
338 if (actionEntry)
339 actionEntry += ((uintptr_t)actionTableStart) - 1;
Howard Hinnantd3dba312012-01-06 20:39:47 +0000340 if ((start <= pcOffset) && (pcOffset < (start + length)))
341 {
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000342 exception_header->catchTemp = (void*)(funcStart + landingPad);
Howard Hinnantd3dba312012-01-06 20:39:47 +0000343 if (actionEntry)
344 return handleActionValue(classInfo,
345 actionEntry,
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000346 unwind_exception,
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000347 ttypeEncoding);
Howard Hinnantd3dba312012-01-06 20:39:47 +0000348 // Note: Only non-clean up handlers are marked as
349 // found. Otherwise the clean up handlers will be
350 // re-found and executed during the clean up
351 // phase.
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000352 return true; //?
Howard Hinnantd3dba312012-01-06 20:39:47 +0000353 }
354 }
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000355 // Not found, need to properly terminate
Howard Hinnantd3dba312012-01-06 20:39:47 +0000356 }
357 return false;
Howard Hinnant66d93272012-01-04 20:49:43 +0000358}
359
Howard Hinnant66d93272012-01-04 20:49:43 +0000360static
361_Unwind_Reason_Code
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000362transfer_control_to_landing_pad(_Unwind_Exception* unwind_exception,
Howard Hinnantd3dba312012-01-06 20:39:47 +0000363 _Unwind_Context* context)
Howard Hinnant66d93272012-01-04 20:49:43 +0000364{
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000365 __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1;
366 _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)unwind_exception);
367 _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), exception_header->handlerSwitchValue);
368 _Unwind_SetIP(context, (uintptr_t)exception_header->catchTemp);
Howard Hinnantd3dba312012-01-06 20:39:47 +0000369 return _URC_INSTALL_CONTEXT;
Howard Hinnant66d93272012-01-04 20:49:43 +0000370}
371
Howard Hinnant66d93272012-01-04 20:49:43 +0000372static
373_Unwind_Reason_Code
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000374perform_cleanup(_Unwind_Exception* unwind_exception, _Unwind_Context* context)
Howard Hinnant66d93272012-01-04 20:49:43 +0000375{
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000376 __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1;
377 _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)unwind_exception);
Howard Hinnantd3dba312012-01-06 20:39:47 +0000378 _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000379 _Unwind_SetIP(context, (uintptr_t)exception_header->catchTemp);
Howard Hinnantd3dba312012-01-06 20:39:47 +0000380 return _URC_INSTALL_CONTEXT;
Howard Hinnant66d93272012-01-04 20:49:43 +0000381}
382
383// public API
384
385// Requires: version == 1
386// actions == _UA_SEARCH_PHASE, or
387// == _UA_CLEANUP_PHASE, or
388// == _UA_CLEANUP_PHASE | _UA_HANDLER_FRAME, or
389// == _UA_CLEANUP_PHASE | _UA_FORCE_UNWIND
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000390// unwind_exception != nullptr
Howard Hinnant66d93272012-01-04 20:49:43 +0000391// context != nullptr
392_Unwind_Reason_Code
393__gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass,
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000394 _Unwind_Exception* unwind_exception, _Unwind_Context* context)
Howard Hinnant66d93272012-01-04 20:49:43 +0000395{
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000396 if (version == 1 && unwind_exception != 0 && context != 0)
Howard Hinnant66d93272012-01-04 20:49:43 +0000397 {
Howard Hinnant86b4dfa2012-01-08 23:50:46 +0000398 bool native_exception = (exceptionClass & 0xFFFFFF00) == 0x432B2B00;
Howard Hinnant66d93272012-01-04 20:49:43 +0000399 bool force_unwind = actions & _UA_FORCE_UNWIND;
400 if (native_exception && !force_unwind)
401 {
402 if (actions & _UA_SEARCH_PHASE)
403 {
404 if (actions & _UA_CLEANUP_PHASE)
405 return _URC_FATAL_PHASE1_ERROR;
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000406 if (contains_handler(unwind_exception, context))
Howard Hinnantd3dba312012-01-06 20:39:47 +0000407 return _URC_HANDLER_FOUND;
Howard Hinnant66d93272012-01-04 20:49:43 +0000408 return _URC_CONTINUE_UNWIND;
409 }
410 if (actions & _UA_CLEANUP_PHASE)
411 {
412 if (actions & _UA_HANDLER_FRAME)
413 {
414 // return _URC_INSTALL_CONTEXT or _URC_FATAL_PHASE2_ERROR
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000415 return transfer_control_to_landing_pad(unwind_exception, context);
Howard Hinnant66d93272012-01-04 20:49:43 +0000416 }
417 // return _URC_CONTINUE_UNWIND or _URC_FATAL_PHASE2_ERROR
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000418 return perform_cleanup(unwind_exception, context);
Howard Hinnant66d93272012-01-04 20:49:43 +0000419 }
420 }
421 else // foreign exception or force_unwind
422 {
423 if (actions & _UA_SEARCH_PHASE)
424 {
425 if (actions & _UA_CLEANUP_PHASE)
426 return _URC_FATAL_PHASE1_ERROR;
427 return _URC_CONTINUE_UNWIND;
428 }
429 if (actions & _UA_CLEANUP_PHASE)
430 {
431 if (actions & _UA_HANDLER_FRAME)
432 return _URC_FATAL_PHASE2_ERROR;
433 // return _URC_CONTINUE_UNWIND or _URC_FATAL_PHASE2_ERROR
Howard Hinnantafcf7ac2012-01-22 19:14:27 +0000434 return perform_cleanup(unwind_exception, context);
Howard Hinnant66d93272012-01-04 20:49:43 +0000435 }
436 }
437 }
438 return _URC_FATAL_PHASE1_ERROR;
439}
440
441} // extern "C"
442
443} // __cxxabiv1