1 // Copyright (C) 2012 The Android Open Source Project
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions
6 // are met:
7 // 1. Redistributions of source code must retain the above copyright
8 //    notice, this list of conditions and the following disclaimer.
9 // 2. Redistributions in binary form must reproduce the above copyright
10 //    notice, this list of conditions and the following disclaimer in the
11 //    documentation and/or other materials provided with the distribution.
12 // 3. Neither the name of the project nor the names of its contributors
13 //    may be used to endorse or promote products derived from this software
14 //    without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 // ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 // SUCH DAMAGE.
27 //===----------------------------------------------------------------------===//
28 //                     The LLVM Compiler Infrastructure
29 //
30 // This file is dual licensed under the MIT and the University of Illinois Open
31 // Source Licenses. See LICENSE.TXT for details.
32 //
33 //
34 //  This file implements the "Exception Handling APIs"
35 //  http://www.codesourcery.com/public/cxx-abi/abi-eh.html
36 //  http://www.intel.com/design/itanium/downloads/245358.htm
37 //
38 //===----------------------------------------------------------------------===//
39 
40 #include <exception>
41 #include <unwind.h>
42 #include "cxxabi_defines.h"
43 #include "helper_func_internal.h"
44 
45 namespace __cxxabiv1 {
46 
47   const __shim_type_info* getTypePtr(uint64_t ttypeIndex,
48                                      const uint8_t* classInfo,
49                                      uint8_t ttypeEncoding,
50                                      _Unwind_Exception* unwind_exception);
51 
call_terminate(_Unwind_Exception * unwind_exception)52   _GABIXX_NORETURN void call_terminate(_Unwind_Exception* unwind_exception) {
53     __cxa_begin_catch(unwind_exception);  // terminate is also a handler
54     std::terminate();
55   }
56 
57   // Boring stuff which has lots of encode/decode details
scanEHTable(ScanResultInternal & results,_Unwind_Action actions,bool native_exception,_Unwind_Exception * unwind_exception,_Unwind_Context * context)58   void scanEHTable(ScanResultInternal& results,
59                    _Unwind_Action actions,
60                    bool native_exception,
61                    _Unwind_Exception* unwind_exception,
62                    _Unwind_Context* context) {
63     // Initialize results to found nothing but an error
64     results.ttypeIndex = 0;
65     results.actionRecord = 0;
66     results.languageSpecificData = 0;
67     results.landingPad = 0;
68     results.adjustedPtr = 0;
69     results.reason = _URC_FATAL_PHASE1_ERROR;
70 
71     // Check for consistent actions
72     if (actions & _UA_SEARCH_PHASE) {
73       if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) {
74         results.reason = _URC_FATAL_PHASE1_ERROR;
75         return;
76       }
77     } else if (actions & _UA_CLEANUP_PHASE) {
78       if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) {
79         results.reason = _URC_FATAL_PHASE2_ERROR;
80         return;
81       }
82     } else {
83       results.reason = _URC_FATAL_PHASE1_ERROR;
84       return;
85     }
86 
87 
88     // Start scan by getting exception table address
89     const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(context);
90     if (lsda == 0) {
91       // No exception table
92       results.reason = _URC_CONTINUE_UNWIND;
93       return;
94     }
95     results.languageSpecificData = lsda;
96     uintptr_t ip = _Unwind_GetIP(context) - 1;
97     uintptr_t funcStart = _Unwind_GetRegionStart(context);
98     uintptr_t ipOffset = ip - funcStart;
99     const uint8_t* classInfo = NULL;
100     uint8_t lpStartEncoding = *lsda++;
101     const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding);
102     if (lpStart == 0) {
103       lpStart = (const uint8_t*)funcStart;
104     }
105     uint8_t ttypeEncoding = *lsda++;
106     if (ttypeEncoding != DW_EH_PE_omit) {
107       uintptr_t classInfoOffset = readULEB128(&lsda);
108       classInfo = lsda + classInfoOffset;
109     }
110     uint8_t callSiteEncoding = *lsda++;
111     uint32_t callSiteTableLength = static_cast<uint32_t>(readULEB128(&lsda));
112     const uint8_t* callSiteTableStart = lsda;
113     const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength;
114     const uint8_t* actionTableStart = callSiteTableEnd;
115     const uint8_t* callSitePtr = callSiteTableStart;
116 
117 
118     while (callSitePtr < callSiteTableEnd) {
119       uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding);
120       uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding);
121       uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding);
122       uintptr_t actionEntry = readULEB128(&callSitePtr);
123       if ((start <= ipOffset) && (ipOffset < (start + length))) {
124         if (landingPad == 0) {
125           // No handler here
126           results.reason = _URC_CONTINUE_UNWIND;
127           return;
128         }
129 
130         landingPad = (uintptr_t)lpStart + landingPad;
131         if (actionEntry == 0) {
132           if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME))
133           {
134             results.ttypeIndex = 0;
135             results.landingPad = landingPad;
136             results.reason = _URC_HANDLER_FOUND;
137             return;
138           }
139           // No handler here
140           results.reason = _URC_CONTINUE_UNWIND;
141           return;
142         }
143 
144         const uint8_t* action = actionTableStart + (actionEntry - 1);
145         while (true) {
146           const uint8_t* actionRecord = action;
147           int64_t ttypeIndex = readSLEB128(&action);
148           if (ttypeIndex > 0) {
149             // Found a catch, does it actually catch?
150             // First check for catch (...)
151             const __shim_type_info* catchType =
152               getTypePtr(static_cast<uint64_t>(ttypeIndex),
153                          classInfo, ttypeEncoding, unwind_exception);
154             if (catchType == 0) {
155               // Found catch (...) catches everything, including foreign exceptions
156               if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME))
157               {
158                 // Save state and return _URC_HANDLER_FOUND
159                 results.ttypeIndex = ttypeIndex;
160                 results.actionRecord = actionRecord;
161                 results.landingPad = landingPad;
162                 results.adjustedPtr = unwind_exception+1;
163                 results.reason = _URC_HANDLER_FOUND;
164                 return;
165               }
166               else if (!(actions & _UA_FORCE_UNWIND))
167               {
168                 // It looks like the exception table has changed
169                 //    on us.  Likely stack corruption!
170                 call_terminate(unwind_exception);
171               }
172             } else if (native_exception) {
173               __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1;
174               void* adjustedPtr = unwind_exception+1;
175               const __shim_type_info* excpType =
176                   static_cast<const __shim_type_info*>(exception_header->exceptionType);
177               if (adjustedPtr == 0 || excpType == 0) {
178                 // Such a disaster! What's wrong?
179                 call_terminate(unwind_exception);
180               }
181 
182               // Only derefence once, so put ouside the recursive search below
183               if (dynamic_cast<const __pointer_type_info*>(excpType)) {
184                 adjustedPtr = *static_cast<void**>(adjustedPtr);
185               }
186 
187               // Let's play!
188               if (catchType->can_catch(excpType, adjustedPtr)) {
189                 if (actions & _UA_SEARCH_PHASE) {
190                   // Cache it.
191                   results.ttypeIndex = ttypeIndex;
192                   results.actionRecord = actionRecord;
193                   results.landingPad = landingPad;
194                   results.adjustedPtr = adjustedPtr;
195                   results.reason = _URC_HANDLER_FOUND;
196                   return;
197                 } else if (!(actions & _UA_FORCE_UNWIND)) {
198                   // It looks like the exception table has changed
199                   //    on us.  Likely stack corruption!
200                   call_terminate(unwind_exception);
201                 }
202               } // catchType->can_catch
203             } // if (catchType == 0)
204           } else if (ttypeIndex < 0) {
205             // Found an exception spec.
206             if (native_exception) {
207               __cxa_exception* header = reinterpret_cast<__cxa_exception*>(unwind_exception+1)-1;
208               void* adjustedPtr = unwind_exception+1;
209               const std::type_info* excpType = header->exceptionType;
210               if (adjustedPtr == 0 || excpType == 0) {
211                 // Such a disaster! What's wrong?
212                 call_terminate(unwind_exception);
213               }
214 
215               // Let's play!
216               if (canExceptionSpecCatch(ttypeIndex, classInfo,
217                                         ttypeEncoding, excpType,
218                                         adjustedPtr, unwind_exception)) {
219                 if (actions & _UA_SEARCH_PHASE) {
220                   // Cache it.
221                   results.ttypeIndex = ttypeIndex;
222                   results.actionRecord = actionRecord;
223                   results.landingPad = landingPad;
224                   results.adjustedPtr = adjustedPtr;
225                   results.reason = _URC_HANDLER_FOUND;
226                   return;
227                 } else if (!(actions & _UA_FORCE_UNWIND)) {
228                   // It looks like the exception table has changed
229                   //    on us.  Likely stack corruption!
230                   call_terminate(unwind_exception);
231                 }
232               }
233             } else {  // ! native_exception
234               // foreign exception must be caught by exception spec
235               if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME)) {
236                 results.ttypeIndex = ttypeIndex;
237                 results.actionRecord = actionRecord;
238                 results.landingPad = landingPad;
239                 results.adjustedPtr = unwind_exception+1;
240                 results.reason = _URC_HANDLER_FOUND;
241                 return;
242               }
243               else if (!(actions & _UA_FORCE_UNWIND)) {
244                 // It looks like the exception table has changed
245                 //    on us.  Likely stack corruption!
246                 call_terminate(unwind_exception);
247               }
248             }
249           } else {  // ttypeIndex == 0
250             // Found a cleanup, or nothing
251             if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) {
252               results.ttypeIndex = ttypeIndex;
253               results.actionRecord = actionRecord;
254               results.landingPad = landingPad;
255               results.adjustedPtr = unwind_exception+1;
256               results.reason = _URC_HANDLER_FOUND;
257               return;
258             }
259           }
260 
261 
262           const uint8_t* temp = action;
263           int64_t actionOffset = readSLEB128(&temp);
264           if (actionOffset == 0) {
265             // End of action list, no matching handler or cleanup found
266             results.reason = _URC_CONTINUE_UNWIND;
267             return;
268           }
269 
270           // Go to next action
271           action += actionOffset;
272         }
273       } else if (ipOffset < start) {
274         // There is no call site for this ip
275         call_terminate(unwind_exception);
276       }
277     } // while (callSitePtr < callSiteTableEnd)
278 
279     call_terminate(unwind_exception);
280   }
281 
282   /*
283    * Below is target-dependent part
284    */
285 
286 #ifdef __arm__
287 
288   /* Decode an R_ARM_TARGET2 relocation.  */
decodeRelocTarget2(uint32_t ptr)289   uint32_t decodeRelocTarget2 (uint32_t ptr) {
290     uint32_t tmp;
291 
292     tmp = *reinterpret_cast<uint32_t*>(ptr);
293     if (!tmp) {
294       return 0;
295     }
296 
297     tmp += ptr;
298     tmp = *reinterpret_cast<uint32_t*>(tmp);
299     return tmp;
300   }
301 
getTypePtr(uint64_t ttypeIndex,const uint8_t * classInfo,uint8_t ttypeEncoding,_Unwind_Exception * unwind_exception)302   const __shim_type_info* getTypePtr(uint64_t ttypeIndex,
303                                      const uint8_t* classInfo,
304                                      uint8_t ttypeEncoding,
305                                      _Unwind_Exception* unwind_exception) {
306     if (classInfo == 0) { // eh table corrupted!
307       call_terminate(unwind_exception);
308     }
309     const uint8_t* ptr = classInfo - ttypeIndex * 4;
310     return (const __shim_type_info*)decodeRelocTarget2((uint32_t)ptr);
311   }
312 
canExceptionSpecCatch(int64_t specIndex,const uint8_t * classInfo,uint8_t ttypeEncoding,const std::type_info * excpType,void * adjustedPtr,_Unwind_Exception * unwind_exception)313   bool canExceptionSpecCatch(int64_t specIndex,
314                              const uint8_t* classInfo,
315                              uint8_t ttypeEncoding,
316                              const std::type_info* excpType,
317                              void* adjustedPtr,
318                              _Unwind_Exception* unwind_exception) {
319     if (classInfo == 0) { // eh table corrupted!
320       call_terminate(unwind_exception);
321     }
322 
323     specIndex = -specIndex;
324     specIndex -= 1;
325     const uint32_t* temp = reinterpret_cast<const uint32_t*>(classInfo) + specIndex;
326 
327     while (true) {
328       uint32_t ttypeIndex = *temp;
329       if (ttypeIndex == 0) {
330         break;
331       }
332       ttypeIndex = decodeRelocTarget2((uint32_t)temp);
333       temp += 1;
334       const __shim_type_info* catchType = (const __shim_type_info*) ttypeIndex;
335       void* tempPtr = adjustedPtr;
336       if (catchType->can_catch(
337               static_cast<const __shim_type_info*>(excpType), tempPtr)) {
338         return false;
339       }
340     } // while
341     return true;
342   }
343 
344   // lower-level runtime library API function that unwinds the frame
345   extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*,
346                                                     _Unwind_Context*);
347 
setRegisters(_Unwind_Exception * unwind_exception,_Unwind_Context * context,const ScanResultInternal & results)348   void setRegisters(_Unwind_Exception* unwind_exception,
349                     _Unwind_Context* context,
350                     const ScanResultInternal& results) {
351     _Unwind_SetGR(context, 0, reinterpret_cast<uintptr_t>(unwind_exception));
352     _Unwind_SetGR(context, 1, static_cast<uintptr_t>(results.ttypeIndex));
353     _Unwind_SetIP(context, results.landingPad);
354   }
355 
continueUnwinding(_Unwind_Exception * ex,_Unwind_Context * context)356   _Unwind_Reason_Code continueUnwinding(_Unwind_Exception *ex,
357                                         _Unwind_Context *context) {
358     if (__gnu_unwind_frame(ex, context) != _URC_OK) {
359       return _URC_FAILURE;
360     }
361     return _URC_CONTINUE_UNWIND;
362   }
363 
saveDataToBarrierCache(_Unwind_Exception * exc,_Unwind_Context * ctx,const ScanResultInternal & results)364   void saveDataToBarrierCache(_Unwind_Exception* exc,
365                               _Unwind_Context* ctx,
366                               const ScanResultInternal& results) {
367     exc->barrier_cache.sp = _Unwind_GetGR(ctx, UNWIND_STACK_REG);
368     exc->barrier_cache.bitpattern[0] = (uint32_t)results.adjustedPtr;
369     exc->barrier_cache.bitpattern[1] = (uint32_t)results.ttypeIndex;
370     exc->barrier_cache.bitpattern[3] = (uint32_t)results.landingPad;
371   }
372 
loadDataFromBarrierCache(_Unwind_Exception * exc,ScanResultInternal & results)373   void loadDataFromBarrierCache(_Unwind_Exception* exc,
374                                 ScanResultInternal& results) {
375     results.adjustedPtr = (void*) exc->barrier_cache.bitpattern[0];
376     results.ttypeIndex = (int64_t) exc->barrier_cache.bitpattern[1];
377     results.landingPad = (uintptr_t) exc->barrier_cache.bitpattern[3];
378   }
379 
prepareBeginCleanup(_Unwind_Exception * exc)380   void prepareBeginCleanup(_Unwind_Exception* exc) {
381     __cxa_begin_cleanup(exc);
382   }
383 
saveUnexpectedDataToBarrierCache(_Unwind_Exception * exc,_Unwind_Context * ctx,const ScanResultInternal & results)384   void saveUnexpectedDataToBarrierCache(_Unwind_Exception* exc,
385                                         _Unwind_Context* ctx,
386                                         const ScanResultInternal& results) {
387     prepareBeginCleanup(exc);
388 
389     const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(ctx);
390     const uint8_t* classInfo = NULL;
391     uint8_t lpStartEncoding = *lsda++;
392     __attribute__((unused))
393     const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding);
394     __attribute__((unused))
395     uintptr_t funcStart = _Unwind_GetRegionStart(ctx);
396     uint8_t ttypeEncoding = *lsda++;
397     if (ttypeEncoding != DW_EH_PE_omit) {
398       uintptr_t classInfoOffset = readULEB128(&lsda);
399       classInfo = lsda + classInfoOffset;
400     }
401 
402     const uint32_t* e = (const uint32_t*) classInfo - results.ttypeIndex - 1;
403     uint32_t n = 0;
404     while (e[n] != 0) {
405       ++n;
406     }
407 
408     exc->barrier_cache.bitpattern[1] = n;
409     exc->barrier_cache.bitpattern[3] = 4;
410     exc->barrier_cache.bitpattern[4] = (uint32_t)e;
411   }
412 
413 #else // ! __arm__
414 
getTypePtr(uint64_t ttypeIndex,const uint8_t * classInfo,uint8_t ttypeEncoding,_Unwind_Exception * unwind_exception)415   const __shim_type_info* getTypePtr(uint64_t ttypeIndex,
416                                      const uint8_t* classInfo,
417                                      uint8_t ttypeEncoding,
418                                      _Unwind_Exception* unwind_exception) {
419     if (classInfo == 0) { // eh table corrupted!
420       call_terminate(unwind_exception);
421     }
422 
423     switch (ttypeEncoding & 0x0F) {
424     case DW_EH_PE_absptr:
425       ttypeIndex *= sizeof(void*);
426       break;
427     case DW_EH_PE_udata2:
428     case DW_EH_PE_sdata2:
429       ttypeIndex *= 2;
430       break;
431     case DW_EH_PE_udata4:
432     case DW_EH_PE_sdata4:
433       ttypeIndex *= 4;
434       break;
435     case DW_EH_PE_udata8:
436     case DW_EH_PE_sdata8:
437       ttypeIndex *= 8;
438       break;
439     default:
440       // this should not happen.
441       call_terminate(unwind_exception);
442     }
443     classInfo -= ttypeIndex;
444     return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding);
445   }
446 
canExceptionSpecCatch(int64_t specIndex,const uint8_t * classInfo,uint8_t ttypeEncoding,const std::type_info * excpType,void * adjustedPtr,_Unwind_Exception * unwind_exception)447   bool canExceptionSpecCatch(int64_t specIndex,
448                              const uint8_t* classInfo,
449                              uint8_t ttypeEncoding,
450                              const std::type_info* excpType,
451                              void* adjustedPtr,
452                              _Unwind_Exception* unwind_exception) {
453     if (classInfo == 0) { // eh table corrupted!
454       call_terminate(unwind_exception);
455     }
456 
457     specIndex = -specIndex;
458     specIndex -= 1;
459     const uint8_t* temp = classInfo + specIndex;
460 
461     while (true) {
462       uint64_t ttypeIndex = readULEB128(&temp);
463       if (ttypeIndex == 0) {
464         break;
465       }
466       const __shim_type_info* catchType = getTypePtr(ttypeIndex,
467                                                      classInfo,
468                                                      ttypeEncoding,
469                                                      unwind_exception);
470       void* tempPtr = adjustedPtr;
471       if (catchType->can_catch(
472               static_cast<const __shim_type_info*>(excpType), tempPtr)) {
473         return false;
474       }
475     } // while
476     return true;
477   }
478 
setRegisters(_Unwind_Exception * unwind_exception,_Unwind_Context * context,const ScanResultInternal & results)479   void setRegisters(_Unwind_Exception* unwind_exception,
480                     _Unwind_Context* context,
481                     const ScanResultInternal& results) {
482     _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
483                   reinterpret_cast<uintptr_t>(unwind_exception));
484     _Unwind_SetGR(context, __builtin_eh_return_data_regno(1),
485                   static_cast<uintptr_t>(results.ttypeIndex));
486     _Unwind_SetIP(context, results.landingPad);
487   }
488 
continueUnwinding(_Unwind_Exception * ex,_Unwind_Context * context)489   _Unwind_Reason_Code continueUnwinding(_Unwind_Exception *ex,
490                                         _Unwind_Context *context) {
491     return _URC_CONTINUE_UNWIND;
492   }
493 
494   // Do nothing, only for API compatibility
495   // We don't use C++ polymorphism since we hope no virtual table cost.
saveDataToBarrierCache(_Unwind_Exception * exc,_Unwind_Context * ctx,const ScanResultInternal & results)496   void saveDataToBarrierCache(_Unwind_Exception* exc,
497                               _Unwind_Context* ctx,
498                               const ScanResultInternal& results) {}
499 
loadDataFromBarrierCache(_Unwind_Exception * exc,ScanResultInternal & results)500   void loadDataFromBarrierCache(_Unwind_Exception* exc,
501                                 ScanResultInternal& results) {}
502 
prepareBeginCleanup(_Unwind_Exception * exc)503   void prepareBeginCleanup(_Unwind_Exception* exc) {}
504 
saveUnexpectedDataToBarrierCache(_Unwind_Exception * exc,_Unwind_Context * ctx,const ScanResultInternal & results)505   void saveUnexpectedDataToBarrierCache(_Unwind_Exception* exc,
506                                         _Unwind_Context* ctx,
507                                         const ScanResultInternal& results) {}
508 
509 #endif // __arm__
510 
511 } // namespace __cxxabiv1
512