普通文本  |  512行  |  19.54 KB

// Copyright (C) 2012 The Android Open Source Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//===----------------------------------------------------------------------===//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//
//  This file implements the "Exception Handling APIs"
//  http://www.codesourcery.com/public/cxx-abi/abi-eh.html
//  http://www.intel.com/design/itanium/downloads/245358.htm
//
//===----------------------------------------------------------------------===//

#include <exception>
#include <unwind.h>
#include "cxxabi_defines.h"
#include "helper_func_internal.h"

namespace __cxxabiv1 {

  const __shim_type_info* getTypePtr(uint64_t ttypeIndex,
                                     const uint8_t* classInfo,
                                     uint8_t ttypeEncoding,
                                     _Unwind_Exception* unwind_exception);

  _GABIXX_NORETURN void call_terminate(_Unwind_Exception* unwind_exception) {
    __cxa_begin_catch(unwind_exception);  // terminate is also a handler
    std::terminate();
  }

  // Boring stuff which has lots of encode/decode details
  void scanEHTable(ScanResultInternal& results,
                   _Unwind_Action actions,
                   bool native_exception,
                   _Unwind_Exception* unwind_exception,
                   _Unwind_Context* context) {
    // Initialize results to found nothing but an error
    results.ttypeIndex = 0;
    results.actionRecord = 0;
    results.languageSpecificData = 0;
    results.landingPad = 0;
    results.adjustedPtr = 0;
    results.reason = _URC_FATAL_PHASE1_ERROR;

    // Check for consistent actions
    if (actions & _UA_SEARCH_PHASE) {
      if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) {
        results.reason = _URC_FATAL_PHASE1_ERROR;
        return;
      }
    } else if (actions & _UA_CLEANUP_PHASE) {
      if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) {
        results.reason = _URC_FATAL_PHASE2_ERROR;
        return;
      }
    } else {
      results.reason = _URC_FATAL_PHASE1_ERROR;
      return;
    }


    // Start scan by getting exception table address
    const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(context);
    if (lsda == 0) {
      // No exception table
      results.reason = _URC_CONTINUE_UNWIND;
      return;
    }
    results.languageSpecificData = lsda;
    uintptr_t ip = _Unwind_GetIP(context) - 1;
    uintptr_t funcStart = _Unwind_GetRegionStart(context);
    uintptr_t ipOffset = ip - funcStart;
    const uint8_t* classInfo = NULL;
    uint8_t lpStartEncoding = *lsda++;
    const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding);
    if (lpStart == 0) {
      lpStart = (const uint8_t*)funcStart;
    }
    uint8_t ttypeEncoding = *lsda++;
    if (ttypeEncoding != DW_EH_PE_omit) {
      uintptr_t classInfoOffset = readULEB128(&lsda);
      classInfo = lsda + classInfoOffset;
    }
    uint8_t callSiteEncoding = *lsda++;
    uint32_t callSiteTableLength = static_cast<uint32_t>(readULEB128(&lsda));
    const uint8_t* callSiteTableStart = lsda;
    const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength;
    const uint8_t* actionTableStart = callSiteTableEnd;
    const uint8_t* callSitePtr = callSiteTableStart;


    while (callSitePtr < callSiteTableEnd) {
      uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding);
      uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding);
      uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding);
      uintptr_t actionEntry = readULEB128(&callSitePtr);
      if ((start <= ipOffset) && (ipOffset < (start + length))) {
        if (landingPad == 0) {
          // No handler here
          results.reason = _URC_CONTINUE_UNWIND;
          return;
        }

        landingPad = (uintptr_t)lpStart + landingPad;
        if (actionEntry == 0) {
          if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME))
          {
            results.ttypeIndex = 0;
            results.landingPad = landingPad;
            results.reason = _URC_HANDLER_FOUND;
            return;
          }
          // No handler here
          results.reason = _URC_CONTINUE_UNWIND;
          return;
        }

        const uint8_t* action = actionTableStart + (actionEntry - 1);
        while (true) {
          const uint8_t* actionRecord = action;
          int64_t ttypeIndex = readSLEB128(&action);
          if (ttypeIndex > 0) {
            // Found a catch, does it actually catch?
            // First check for catch (...)
            const __shim_type_info* catchType =
              getTypePtr(static_cast<uint64_t>(ttypeIndex),
                         classInfo, ttypeEncoding, unwind_exception);
            if (catchType == 0) {
              // Found catch (...) catches everything, including foreign exceptions
              if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME))
              {
                // Save state and return _URC_HANDLER_FOUND
                results.ttypeIndex = ttypeIndex;
                results.actionRecord = actionRecord;
                results.landingPad = landingPad;
                results.adjustedPtr = unwind_exception+1;
                results.reason = _URC_HANDLER_FOUND;
                return;
              }
              else if (!(actions & _UA_FORCE_UNWIND))
              {
                // It looks like the exception table has changed
                //    on us.  Likely stack corruption!
                call_terminate(unwind_exception);
              }
            } else if (native_exception) {
              __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1;
              void* adjustedPtr = unwind_exception+1;
              const __shim_type_info* excpType =
                  static_cast<const __shim_type_info*>(exception_header->exceptionType);
              if (adjustedPtr == 0 || excpType == 0) {
                // Such a disaster! What's wrong?
                call_terminate(unwind_exception);
              }

              // Only derefence once, so put ouside the recursive search below
              if (dynamic_cast<const __pointer_type_info*>(excpType)) {
                adjustedPtr = *static_cast<void**>(adjustedPtr);
              }

              // Let's play!
              if (catchType->can_catch(excpType, adjustedPtr)) {
                if (actions & _UA_SEARCH_PHASE) {
                  // Cache it.
                  results.ttypeIndex = ttypeIndex;
                  results.actionRecord = actionRecord;
                  results.landingPad = landingPad;
                  results.adjustedPtr = adjustedPtr;
                  results.reason = _URC_HANDLER_FOUND;
                  return;
                } else if (!(actions & _UA_FORCE_UNWIND)) {
                  // It looks like the exception table has changed
                  //    on us.  Likely stack corruption!
                  call_terminate(unwind_exception);
                }
              } // catchType->can_catch
            } // if (catchType == 0)
          } else if (ttypeIndex < 0) {
            // Found an exception spec.
            if (native_exception) {
              __cxa_exception* header = reinterpret_cast<__cxa_exception*>(unwind_exception+1)-1;
              void* adjustedPtr = unwind_exception+1;
              const std::type_info* excpType = header->exceptionType;
              if (adjustedPtr == 0 || excpType == 0) {
                // Such a disaster! What's wrong?
                call_terminate(unwind_exception);
              }

              // Let's play!
              if (canExceptionSpecCatch(ttypeIndex, classInfo,
                                        ttypeEncoding, excpType,
                                        adjustedPtr, unwind_exception)) {
                if (actions & _UA_SEARCH_PHASE) {
                  // Cache it.
                  results.ttypeIndex = ttypeIndex;
                  results.actionRecord = actionRecord;
                  results.landingPad = landingPad;
                  results.adjustedPtr = adjustedPtr;
                  results.reason = _URC_HANDLER_FOUND;
                  return;
                } else if (!(actions & _UA_FORCE_UNWIND)) {
                  // It looks like the exception table has changed
                  //    on us.  Likely stack corruption!
                  call_terminate(unwind_exception);
                }
              }
            } else {  // ! native_exception
              // foreign exception must be caught by exception spec
              if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME)) {
                results.ttypeIndex = ttypeIndex;
                results.actionRecord = actionRecord;
                results.landingPad = landingPad;
                results.adjustedPtr = unwind_exception+1;
                results.reason = _URC_HANDLER_FOUND;
                return;
              }
              else if (!(actions & _UA_FORCE_UNWIND)) {
                // It looks like the exception table has changed
                //    on us.  Likely stack corruption!
                call_terminate(unwind_exception);
              }
            }
          } else {  // ttypeIndex == 0
            // Found a cleanup, or nothing
            if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) {
              results.ttypeIndex = ttypeIndex;
              results.actionRecord = actionRecord;
              results.landingPad = landingPad;
              results.adjustedPtr = unwind_exception+1;
              results.reason = _URC_HANDLER_FOUND;
              return;
            }
          }


          const uint8_t* temp = action;
          int64_t actionOffset = readSLEB128(&temp);
          if (actionOffset == 0) {
            // End of action list, no matching handler or cleanup found
            results.reason = _URC_CONTINUE_UNWIND;
            return;
          }

          // Go to next action
          action += actionOffset;
        }
      } else if (ipOffset < start) {
        // There is no call site for this ip
        call_terminate(unwind_exception);
      }
    } // while (callSitePtr < callSiteTableEnd)

    call_terminate(unwind_exception);
  }

  /*
   * Below is target-dependent part
   */

#ifdef __arm__

  /* Decode an R_ARM_TARGET2 relocation.  */
  uint32_t decodeRelocTarget2 (uint32_t ptr) {
    uint32_t tmp;

    tmp = *reinterpret_cast<uint32_t*>(ptr);
    if (!tmp) {
      return 0;
    }

    tmp += ptr;
    tmp = *reinterpret_cast<uint32_t*>(tmp);
    return tmp;
  }

  const __shim_type_info* getTypePtr(uint64_t ttypeIndex,
                                     const uint8_t* classInfo,
                                     uint8_t ttypeEncoding,
                                     _Unwind_Exception* unwind_exception) {
    if (classInfo == 0) { // eh table corrupted!
      call_terminate(unwind_exception);
    }
    const uint8_t* ptr = classInfo - ttypeIndex * 4;
    return (const __shim_type_info*)decodeRelocTarget2((uint32_t)ptr);
  }

  bool canExceptionSpecCatch(int64_t specIndex,
                             const uint8_t* classInfo,
                             uint8_t ttypeEncoding,
                             const std::type_info* excpType,
                             void* adjustedPtr,
                             _Unwind_Exception* unwind_exception) {
    if (classInfo == 0) { // eh table corrupted!
      call_terminate(unwind_exception);
    }

    specIndex = -specIndex;
    specIndex -= 1;
    const uint32_t* temp = reinterpret_cast<const uint32_t*>(classInfo) + specIndex;

    while (true) {
      uint32_t ttypeIndex = *temp;
      if (ttypeIndex == 0) {
        break;
      }
      ttypeIndex = decodeRelocTarget2((uint32_t)temp);
      temp += 1;
      const __shim_type_info* catchType = (const __shim_type_info*) ttypeIndex;
      void* tempPtr = adjustedPtr;
      if (catchType->can_catch(
              static_cast<const __shim_type_info*>(excpType), tempPtr)) {
        return false;
      }
    } // while
    return true;
  }

  // lower-level runtime library API function that unwinds the frame
  extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*,
                                                    _Unwind_Context*);

  void setRegisters(_Unwind_Exception* unwind_exception,
                    _Unwind_Context* context,
                    const ScanResultInternal& results) {
    _Unwind_SetGR(context, 0, reinterpret_cast<uintptr_t>(unwind_exception));
    _Unwind_SetGR(context, 1, static_cast<uintptr_t>(results.ttypeIndex));
    _Unwind_SetIP(context, results.landingPad);
  }

  _Unwind_Reason_Code continueUnwinding(_Unwind_Exception *ex,
                                        _Unwind_Context *context) {
    if (__gnu_unwind_frame(ex, context) != _URC_OK) {
      return _URC_FAILURE;
    }
    return _URC_CONTINUE_UNWIND;
  }

  void saveDataToBarrierCache(_Unwind_Exception* exc,
                              _Unwind_Context* ctx,
                              const ScanResultInternal& results) {
    exc->barrier_cache.sp = _Unwind_GetGR(ctx, UNWIND_STACK_REG);
    exc->barrier_cache.bitpattern[0] = (uint32_t)results.adjustedPtr;
    exc->barrier_cache.bitpattern[1] = (uint32_t)results.ttypeIndex;
    exc->barrier_cache.bitpattern[3] = (uint32_t)results.landingPad;
  }

  void loadDataFromBarrierCache(_Unwind_Exception* exc,
                                ScanResultInternal& results) {
    results.adjustedPtr = (void*) exc->barrier_cache.bitpattern[0];
    results.ttypeIndex = (int64_t) exc->barrier_cache.bitpattern[1];
    results.landingPad = (uintptr_t) exc->barrier_cache.bitpattern[3];
  }

  void prepareBeginCleanup(_Unwind_Exception* exc) {
    __cxa_begin_cleanup(exc);
  }

  void saveUnexpectedDataToBarrierCache(_Unwind_Exception* exc,
                                        _Unwind_Context* ctx,
                                        const ScanResultInternal& results) {
    prepareBeginCleanup(exc);

    const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(ctx);
    const uint8_t* classInfo = NULL;
    uint8_t lpStartEncoding = *lsda++;
    __attribute__((unused))
    const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding);
    __attribute__((unused))
    uintptr_t funcStart = _Unwind_GetRegionStart(ctx);
    uint8_t ttypeEncoding = *lsda++;
    if (ttypeEncoding != DW_EH_PE_omit) {
      uintptr_t classInfoOffset = readULEB128(&lsda);
      classInfo = lsda + classInfoOffset;
    }

    const uint32_t* e = (const uint32_t*) classInfo - results.ttypeIndex - 1;
    uint32_t n = 0;
    while (e[n] != 0) {
      ++n;
    }

    exc->barrier_cache.bitpattern[1] = n;
    exc->barrier_cache.bitpattern[3] = 4;
    exc->barrier_cache.bitpattern[4] = (uint32_t)e;
  }

#else // ! __arm__

  const __shim_type_info* getTypePtr(uint64_t ttypeIndex,
                                     const uint8_t* classInfo,
                                     uint8_t ttypeEncoding,
                                     _Unwind_Exception* unwind_exception) {
    if (classInfo == 0) { // eh table corrupted!
      call_terminate(unwind_exception);
    }

    switch (ttypeEncoding & 0x0F) {
    case DW_EH_PE_absptr:
      ttypeIndex *= sizeof(void*);
      break;
    case DW_EH_PE_udata2:
    case DW_EH_PE_sdata2:
      ttypeIndex *= 2;
      break;
    case DW_EH_PE_udata4:
    case DW_EH_PE_sdata4:
      ttypeIndex *= 4;
      break;
    case DW_EH_PE_udata8:
    case DW_EH_PE_sdata8:
      ttypeIndex *= 8;
      break;
    default:
      // this should not happen.
      call_terminate(unwind_exception);
    }
    classInfo -= ttypeIndex;
    return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding);
  }

  bool canExceptionSpecCatch(int64_t specIndex,
                             const uint8_t* classInfo,
                             uint8_t ttypeEncoding,
                             const std::type_info* excpType,
                             void* adjustedPtr,
                             _Unwind_Exception* unwind_exception) {
    if (classInfo == 0) { // eh table corrupted!
      call_terminate(unwind_exception);
    }

    specIndex = -specIndex;
    specIndex -= 1;
    const uint8_t* temp = classInfo + specIndex;

    while (true) {
      uint64_t ttypeIndex = readULEB128(&temp);
      if (ttypeIndex == 0) {
        break;
      }
      const __shim_type_info* catchType = getTypePtr(ttypeIndex,
                                                     classInfo,
                                                     ttypeEncoding,
                                                     unwind_exception);
      void* tempPtr = adjustedPtr;
      if (catchType->can_catch(
              static_cast<const __shim_type_info*>(excpType), tempPtr)) {
        return false;
      }
    } // while
    return true;
  }

  void setRegisters(_Unwind_Exception* unwind_exception,
                    _Unwind_Context* context,
                    const ScanResultInternal& results) {
    _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
                  reinterpret_cast<uintptr_t>(unwind_exception));
    _Unwind_SetGR(context, __builtin_eh_return_data_regno(1),
                  static_cast<uintptr_t>(results.ttypeIndex));
    _Unwind_SetIP(context, results.landingPad);
  }

  _Unwind_Reason_Code continueUnwinding(_Unwind_Exception *ex,
                                        _Unwind_Context *context) {
    return _URC_CONTINUE_UNWIND;
  }

  // Do nothing, only for API compatibility
  // We don't use C++ polymorphism since we hope no virtual table cost.
  void saveDataToBarrierCache(_Unwind_Exception* exc,
                              _Unwind_Context* ctx,
                              const ScanResultInternal& results) {}

  void loadDataFromBarrierCache(_Unwind_Exception* exc,
                                ScanResultInternal& results) {}

  void prepareBeginCleanup(_Unwind_Exception* exc) {}

  void saveUnexpectedDataToBarrierCache(_Unwind_Exception* exc,
                                        _Unwind_Context* ctx,
                                        const ScanResultInternal& results) {}

#endif // __arm__

} // namespace __cxxabiv1