| // 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 <cxxabi.h> |
| #include <exception> |
| #include <unwind.h> |
| #include "helper_func_internal.h" |
| |
| #include <android/log.h> |
| #include <dlfcn.h> |
| #include <stdio.h> |
| |
| namespace __cxxabiv1 { |
| |
| const __shim_type_info* getTypePtr(uint64_t ttypeIndex, |
| const uint8_t* classInfo, |
| uint8_t ttypeEncoding, |
| _Unwind_Exception* unwind_exception); |
| |
| 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" bool __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++; |
| const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); |
| uintptr_t funcStart = _Unwind_GetRegionStart(ctx); |
| 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; |
| } |
| |
| 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__ |
| |
| void fatalError(const char* message) { |
| |
| // Note: Printing to stderr is only useful when running an executable |
| // from a shell, e.g. when using 'adb shell'. For regular |
| // applications, stderr is redirected to /dev/null by default. |
| fprintf(stderr, "PANIC:GAbi++:%s\n", message); |
| |
| // Always print the message to the log, when possible. Use |
| // dlopen()/dlsym() to avoid adding an explicit dependency |
| // to -llog in GAbi++ for this sole feature. |
| // |
| // An explicit dependency to -ldl can be avoided because these |
| // functions are implemented directly by the dynamic linker. |
| // That is, except when this code is linked into a static |
| // executable. In this case, adding -ldl to the final link command |
| // will be necessary, but the dlopen() will always return NULL. |
| // |
| // There is unfortunately no way to detect where this code is going |
| // to be used at compile time, but static executables are strongly |
| // discouraged on the platform because they can't implement ASLR. |
| // |
| typedef void (*logfunc_t)(int, const char*, const char*); |
| logfunc_t logger = NULL; |
| |
| // Note that this should always succeed in a regular application, |
| // because the library is already loaded into the process' address |
| // space by Zygote before forking the application process. |
| // This will fail in static executables, because the static |
| // version of -ldl only contains empty stubs. |
| void* liblog = dlopen("liblog.so", RTLD_NOW); |
| |
| if (liblog != NULL) { |
| logger = reinterpret_cast<logfunc_t>(dlsym(liblog, "__android_log_print")); |
| if (logger != NULL) { |
| (*logger)(ANDROID_LOG_FATAL, "GAbi++", message); |
| } |
| dlclose(liblog); |
| } |
| |
| std::terminate(); |
| } |
| |
| } // namespace __cxxabiv1 |