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 * Copyright 2010-2011 PathScale, Inc. All rights reserved.
41 *
42 * Redistribution and use in source and binary forms, with or without
43 * modification, are permitted provided that the following conditions are met:
44 *
45 * 1. Redistributions of source code must retain the above copyright notice,
46 *    this list of conditions and the following disclaimer.
47 *
48 * 2. Redistributions in binary form must reproduce the above copyright notice,
49 *    this list of conditions and the following disclaimer in the documentation
50 *    and/or other materials provided with the distribution.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
53 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
54 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
55 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
56 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
57 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
58 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
59 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
60 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
61 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
62 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63 */
64
65
66#include <cstdlib>
67#include <unwind.h>
68
69#include "cxxabi_defines.h"
70#include "dwarf_helper.h"
71#include "helper_func_internal.h"
72
73namespace __cxxabiv1 {
74
75#ifdef __arm__
76extern "C" enum type_match_result {
77  ctm_failed = 0,
78  ctm_succeeded = 1,
79  ctm_succeeded_with_ptr_to_base = 2
80};
81
82
83extern "C" type_match_result __attribute__((visibility("default")))
84__cxa_type_match(_Unwind_Exception* ucbp,
85                 const __shim_type_info* rttip,
86                 bool is_reference_type,
87                 void** matched_object) {
88
89  __cxa_exception* header = reinterpret_cast<__cxa_exception*>(ucbp+1)-1;
90  type_match_result result = ctm_succeeded;
91
92  void* adjustedPtr = header+1;
93  if (dynamic_cast<const __pointer_type_info*>(header->exceptionType)) {
94    adjustedPtr = *reinterpret_cast<void**>(adjustedPtr);
95    result = ctm_succeeded_with_ptr_to_base;
96  }
97
98  const __shim_type_info* catch_type = rttip;
99  const __shim_type_info* thrown_type =
100      static_cast<const __shim_type_info*>(header->exceptionType);
101  if (!catch_type || !thrown_type) {
102    return ctm_failed;
103  }
104
105  if (catch_type->can_catch(thrown_type, adjustedPtr)) {
106    *matched_object = adjustedPtr;
107    return result;
108  }
109
110  return ctm_failed;
111}
112#endif  // __arm__
113
114namespace {
115
116void terminate_helper(std::terminate_handler t_handler) {
117  try {
118    t_handler();
119    abort();
120  } catch (...) {
121    abort();
122  }
123}
124
125void unexpected_helper(std::unexpected_handler u_handler) {
126  u_handler();
127  std::terminate();
128}
129
130}  // namespace
131
132#ifdef __arm__
133  extern "C" bool   __attribute__((visibility("default")))
134  __cxa_begin_cleanup(_Unwind_Exception* exc) {
135    __cxa_eh_globals *globals = __cxa_get_globals();
136    __cxa_exception *header = reinterpret_cast<__cxa_exception*>(exc+1)-1;
137    bool native = header->unwindHeader.exception_class == __gxx_exception_class;
138
139    if (native) {
140      header->cleanupCount += 1;
141      if (header->cleanupCount == 1) {  // First time
142        header->nextCleanup = globals->cleanupExceptions;
143        globals->cleanupExceptions = header;
144      }
145    } else {
146      globals->cleanupExceptions = header;
147    }
148
149    return true;
150  }
151
152  extern "C" _Unwind_Exception * helper_end_cleanup() {
153    __cxa_eh_globals *globals = __cxa_get_globals();
154    __cxa_exception* header = globals->cleanupExceptions;
155
156    if (!header) {
157      std::terminate();
158    }
159
160    if (header->unwindHeader.exception_class == __gxx_exception_class) {
161      header->cleanupCount -= 1;
162      if (header->cleanupCount == 0) {  // Last one
163        globals->cleanupExceptions = header->nextCleanup;
164        header->nextCleanup = NULL;
165      }
166    } else {
167      globals->cleanupExceptions = NULL;
168    }
169
170    return &header->unwindHeader;
171  }
172
173  asm (
174  ".pushsection .text.__cxa_end_cleanup    \n"
175  ".global __cxa_end_cleanup               \n"
176  ".type __cxa_end_cleanup, \"function\"   \n"
177  "__cxa_end_cleanup:                      \n"
178  " push\t{r1, r2, r3, r4}                 \n"
179  " bl helper_end_cleanup                  \n"
180  " pop\t{r1, r2, r3, r4}                  \n"
181  " bl _Unwind_Resume                      \n"
182  " bl abort                               \n"
183  ".popsection                             \n"
184  );
185
186  extern "C" void __attribute__((visibility("default")))
187  __cxa_call_unexpected(void* arg) {
188    _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg);
189    __cxa_exception* header = reinterpret_cast<__cxa_exception*>(unwind_exception+1)-1;
190    bool native_exception = unwind_exception->exception_class == __gxx_exception_class;
191
192    if (!native_exception) {
193      __cxa_begin_catch(unwind_exception);    // unexpected is also a handler
194      try {
195        std::unexpected();
196      } catch (...) {
197        std::terminate();
198      }
199
200      return;
201    }
202
203    // Cache previous data first since we will change contents below.
204    uint32_t count = unwind_exception->barrier_cache.bitpattern[1];
205    uint32_t stride = unwind_exception->barrier_cache.bitpattern[3];
206    uint32_t* list = reinterpret_cast<uint32_t*>(
207                            unwind_exception->barrier_cache.bitpattern[4]);
208
209    __cxa_begin_catch(unwind_exception);    // unexpected is also a handler
210    try {
211      unexpected_helper(header->unexpectedHandler);
212    } catch (...) {
213      // A new exception thrown when calling unexpected.
214      bool allow_bad_exception = false;
215
216      for (uint32_t i = 0; i != count; ++i) {
217        uint32_t offset = reinterpret_cast<uint32_t>(&list[i * (stride >> 2)]);
218        offset = decodeRelocTarget2(offset);
219        const __shim_type_info* catch_type = reinterpret_cast<const __shim_type_info*>(offset);
220
221        __cxa_exception* new_header = __cxa_get_globals()->caughtExceptions;
222        void* adjustedPtr = new_header + 1;
223        if (__cxa_type_match(&new_header->unwindHeader,
224                             catch_type,
225                             false/* is_ref_type */,
226                             &adjustedPtr) != ctm_failed) {
227          throw;
228        }
229
230        void* null_adjustedPtr = NULL;
231        const __shim_type_info* bad_excp =
232            static_cast<const __shim_type_info*>(&typeid(std::bad_exception));
233        if (catch_type->can_catch(bad_excp, null_adjustedPtr)) {
234          allow_bad_exception = true;
235        }
236      }
237
238      // If no other ones match, throw bad_exception.
239      if (allow_bad_exception) {
240        __cxa_end_catch();
241        __cxa_end_catch();
242        throw std::bad_exception();
243      }
244
245      terminate_helper(header->terminateHandler);
246    }
247  }
248#else // ! __arm__
249  extern "C" void __attribute__((visibility("default")))
250  __cxa_call_unexpected(void* arg) {
251    _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg);
252    if (unwind_exception == 0) {
253      call_terminate(unwind_exception);
254    }
255    __cxa_begin_catch(unwind_exception);    // unexpected is also a handler
256
257    bool native_old_exception = unwind_exception->exception_class == __gxx_exception_class;
258    std::unexpected_handler u_handler;
259    std::terminate_handler t_handler;
260    __cxa_exception* old_exception_header = 0;
261    int64_t ttypeIndex;
262    const uint8_t* lsda;
263    if (native_old_exception) {
264      old_exception_header = reinterpret_cast<__cxa_exception*>(unwind_exception+1)-1;
265      t_handler = old_exception_header->terminateHandler;
266      u_handler = old_exception_header->unexpectedHandler;
267      // If unexpected_helper(u_handler) rethrows the same exception,
268      //   these values get overwritten by the rethrow.  So save them now:
269      ttypeIndex = old_exception_header->handlerSwitchValue;
270      lsda = old_exception_header->languageSpecificData;
271    } else {
272      t_handler = std::get_terminate();
273      u_handler = std::get_unexpected();
274    }
275
276    try {
277      unexpected_helper(u_handler);
278    } catch (...) {
279      // A new exception thrown when calling unexpected.
280
281      if (!native_old_exception) {
282        std::terminate();
283      }
284      uint8_t lpStartEncoding = *lsda++;
285      const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding);
286      uint8_t ttypeEncoding = *lsda++;
287      if (ttypeEncoding == DW_EH_PE_omit) {
288        terminate_helper(t_handler);
289      }
290      uintptr_t classInfoOffset = readULEB128(&lsda);
291      const uint8_t* classInfo = lsda + classInfoOffset;
292      __cxa_eh_globals* globals = __cxa_get_globals_fast();
293      __cxa_exception* new_exception_header = globals->caughtExceptions;
294      if (new_exception_header == 0) {  // This shouldn't be able to happen!
295        terminate_helper(t_handler);
296      }
297      bool native_new_exception =
298        new_exception_header->unwindHeader.exception_class == __gxx_exception_class;
299
300      if (native_new_exception && (new_exception_header != old_exception_header)) {
301        const std::type_info* excpType = new_exception_header->exceptionType;
302        if (!canExceptionSpecCatch(ttypeIndex, classInfo, ttypeEncoding,
303                                   excpType, new_exception_header+1, unwind_exception)) {
304          // We need to __cxa_end_catch, but for the old exception,
305          //   not the new one.  This is a little tricky ...
306          // Disguise new_exception_header as a rethrown exception, but
307          //   don't actually rethrow it.  This means you can temporarily
308          //   end the catch clause enclosing new_exception_header without
309          //   __cxa_end_catch destroying new_exception_header.
310          new_exception_header->handlerCount = -new_exception_header->handlerCount;
311          globals->uncaughtExceptions += 1;
312          __cxa_end_catch();
313          __cxa_end_catch();
314          __cxa_begin_catch(&new_exception_header->unwindHeader);
315          throw;
316        }
317      }
318
319      const std::type_info* excpType = &typeid(std::bad_exception);
320      if (!canExceptionSpecCatch(ttypeIndex, classInfo, ttypeEncoding,
321                                 excpType, NULL, unwind_exception)) {
322        __cxa_end_catch();
323        __cxa_end_catch();
324        throw std::bad_exception();
325      }
326    } // catch (...)
327
328    // Call terminate after unexpected normally done
329    terminate_helper(t_handler);
330  }
331#endif // __arm__
332
333} // namespace __cxxabiv1
334