COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, struct _Unwind_Context *context) #endif { /* Since C does not have catch clauses, there is nothing to do during */ /* phase 1 (the search phase). */ if ( actions & _UA_SEARCH_PHASE ) return _URC_CONTINUE_UNWIND; /* There is nothing to do if there is no LSDA for this frame. */ const uint8_t* lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context); if ( lsda == (uint8_t*) 0 ) return _URC_CONTINUE_UNWIND; uintptr_t pc = _Unwind_GetIP(context)-1; uintptr_t funcStart = _Unwind_GetRegionStart(context); uintptr_t pcOffset = pc - funcStart; /* Parse LSDA header. */ uint8_t lpStartEncoding = *lsda++; if (lpStartEncoding != DW_EH_PE_omit) { readEncodedPointer(&lsda, lpStartEncoding); } uint8_t ttypeEncoding = *lsda++; if (ttypeEncoding != DW_EH_PE_omit) { readULEB128(&lsda); } /* Walk call-site table looking for range that includes current PC. */ uint8_t callSiteEncoding = *lsda++; uint32_t callSiteTableLength = readULEB128(&lsda); const uint8_t* callSiteTableStart = lsda; const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; const uint8_t* p=callSiteTableStart; while (p < callSiteTableEnd) { uintptr_t start = readEncodedPointer(&p, callSiteEncoding); uintptr_t length = readEncodedPointer(&p, callSiteEncoding); uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding); readULEB128(&p); /* action value not used for C code */ if ( landingPad == 0 ) continue; /* no landing pad for this entry */ if ( (start <= pcOffset) && (pcOffset < (start+length)) ) { /* Found landing pad for the PC. * Set Instruction Pointer to so we re-enter function * at landing pad. The landing pad is created by the compiler * to take two parameters in registers. */ _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); _Unwind_SetIP(context, (funcStart + landingPad)); return _URC_INSTALL_CONTEXT; } } /* No landing pad found, continue unwinding. */ return _URC_CONTINUE_UNWIND; }
static const __shim_type_info* get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, uint8_t ttypeEncoding, bool native_exception, _Unwind_Exception* unwind_exception) { if (classInfo == 0) { // this should not happen. Indicates corrupted eh_table. call_terminate(native_exception, 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. Indicates corrupted eh_table. call_terminate(native_exception, unwind_exception); } classInfo -= ttypeIndex; return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding); }
static int dlCallback(struct dl_phdr_info *info, size_t size, void *data) { CBData* cbdata = static_cast<CBData*>(data); bool addrMatch = false; void* maxAddr = 0; const eh_frame_hdr* ehdr = 0; if (cbdata->info->dwarf_section) // we already have a match return 0; //std::cout << "Looking into " << info->dlpi_name << std::endl; if (size < offsetof(struct dl_phdr_info, dlpi_phnum)) return 0; for (int i = 0; i < info->dlpi_phnum; i++) { const ElfW(Phdr)* phdr = &info->dlpi_phdr[i]; /* if (strstr(info->dlpi_name, "cxxdarwin")) { std::cout << "type: " << phdr->p_type << "; vaddr: " << phdr->p_vaddr << std::endl; } */ if (phdr->p_type == PT_LOAD) { void* from = reinterpret_cast<void*>(uintptr_t(info->dlpi_addr) + uintptr_t(phdr->p_vaddr)); void* to = reinterpret_cast<char*>(from) + phdr->p_memsz; if (cbdata->addr >= from && cbdata->addr < to) addrMatch = true; if (to > maxAddr) maxAddr = to; // TODO: could this be improved? libunwind does the same } else if (phdr->p_type == PT_GNU_EH_FRAME) { //std::cout << "Found .eh_frame_hdr in " << info->dlpi_name << std::endl; ehdr = reinterpret_cast<eh_frame_hdr*>(uintptr_t(info->dlpi_addr) + phdr->p_vaddr); // cbdata->info->dwarf_section_length = phdr->p_memsz; } } if (addrMatch && ehdr) { //std::cout << "*** Match found! " << info->dlpi_name << std::endl; // Now we find .eh_frame from .eh_frame_hdr if (ehdr->version != 1) return 0; cbdata->info->dwarf_section = reinterpret_cast<void*>(readEncodedPointer(ehdr)); cbdata->info->dwarf_section_length = uintptr_t(maxAddr) - uintptr_t(cbdata->info->dwarf_section); } return 0; }
void __cxa_call_unexpected(void* arg) { _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg); if (unwind_exception == 0) call_terminate(false, unwind_exception); __cxa_begin_catch(unwind_exception); bool native_old_exception = (reinterpret_cast<uint64_t>(unwind_exception->exception_class) & get_vendor_and_language) == (kOurExceptionClass & get_vendor_and_language); std::unexpected_handler u_handler; std::terminate_handler t_handler; __cxa_exception* old_exception_header = 0; int64_t ttypeIndex; const uint8_t* lsda; if (native_old_exception) { old_exception_header = (__cxa_exception*)(unwind_exception+1) - 1; t_handler = old_exception_header->terminateHandler; u_handler = old_exception_header->unexpectedHandler; // If std::__unexpected(u_handler) rethrows the same exception, // these values get overwritten by the rethrow. So save them now: #ifndef __ARM_EABI_UNWINDER__ ttypeIndex = old_exception_header->handlerSwitchValue; lsda = old_exception_header->languageSpecificData; #endif } else { t_handler = std::get_terminate(); u_handler = std::get_unexpected(); } try { std::__unexpected(u_handler); } catch (...) { // If the old exception is foreign, then all we can do is terminate. // We have no way to recover the needed old exception spec. There's // no way to pass that information here. And the personality routine // can't call us directly and do anything but terminate() if we throw // from here. if (native_old_exception) { // Have: // old_exception_header->languageSpecificData // old_exception_header->actionRecord // Need // const uint8_t* classInfo // uint8_t ttypeEncoding uint8_t lpStartEncoding = *lsda++; const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); (void)lpStart; // purposefully unused. Just needed to increment lsda. uint8_t ttypeEncoding = *lsda++; if (ttypeEncoding == DW_EH_PE_omit) std::__terminate(t_handler); uintptr_t classInfoOffset = readULEB128(&lsda); const uint8_t* classInfo = lsda + classInfoOffset; // Is this new exception catchable by the exception spec at ttypeIndex? // The answer is obviously yes if the new and old exceptions are the same exception // If no // throw; __cxa_eh_globals* globals = __cxa_get_globals_fast(); __cxa_exception* new_exception_header = globals->caughtExceptions; if (new_exception_header == 0) // This shouldn't be able to happen! std::__terminate(t_handler); bool native_new_exception = (reinterpret_cast<uint64_t>(new_exception_header->unwindHeader.exception_class) & get_vendor_and_language) == (kOurExceptionClass & get_vendor_and_language); void* adjustedPtr; if (native_new_exception && (new_exception_header != old_exception_header)) { const __shim_type_info* excpType = static_cast<const __shim_type_info*>(new_exception_header->exceptionType); adjustedPtr = reinterpret_cast<uint64_t>(new_exception_header->unwindHeader.exception_class) == kOurDependentExceptionClass ? ((__cxa_dependent_exception*)new_exception_header)->primaryException : new_exception_header + 1; if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, excpType, adjustedPtr, unwind_exception)) { // We need to __cxa_end_catch, but for the old exception, // not the new one. This is a little tricky ... // Disguise new_exception_header as a rethrown exception, but // don't actually rethrow it. This means you can temporarily // end the catch clause enclosing new_exception_header without // __cxa_end_catch destroying new_exception_header. new_exception_header->handlerCount = -new_exception_header->handlerCount; globals->uncaughtExceptions += 1; // Call __cxa_end_catch for new_exception_header __cxa_end_catch(); // Call __cxa_end_catch for old_exception_header __cxa_end_catch(); // Renter this catch clause with new_exception_header __cxa_begin_catch(&new_exception_header->unwindHeader); // Rethrow new_exception_header throw; } } // Will a std::bad_exception be catchable by the exception spec at // ttypeIndex? // If no // throw std::bad_exception(); const __shim_type_info* excpType = static_cast<const __shim_type_info*>(&typeid(std::bad_exception)); std::bad_exception be; adjustedPtr = &be; if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, excpType, adjustedPtr, unwind_exception)) { // We need to __cxa_end_catch for both the old exception and the // new exception. Technically we should do it in that order. // But it is expedient to do it in the opposite order: // Call __cxa_end_catch for new_exception_header __cxa_end_catch(); // Throw std::bad_exception will __cxa_end_catch for // old_exception_header throw be; } } } std::__terminate(t_handler); }
static void scan_eh_tab(scan_results& 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) { // Do Phase 1 if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) { // None of these flags should be set during Phase 1 // Client error results.reason = _URC_FATAL_PHASE1_ERROR; return; } } else if (actions & _UA_CLEANUP_PHASE) { if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) { // _UA_HANDLER_FRAME should only be set if phase 1 found a handler. // If _UA_FORCE_UNWIND is set, phase 1 shouldn't have happened. // Client error results.reason = _URC_FATAL_PHASE2_ERROR; return; } } else // Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE is set { // One of these should be set. // Client error 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) { // There is no exception table results.reason = _URC_CONTINUE_UNWIND; return; } results.languageSpecificData = lsda; // Get the current instruction pointer and offset it before next // instruction in the current frame which threw the exception. uintptr_t ip = _Unwind_GetIP(context) - 1; // Get beginning current frame's code (as defined by the // emitted dwarf code) uintptr_t funcStart = _Unwind_GetRegionStart(context); #if __arm__ if (ip == uintptr_t(-1)) { // no action results.reason = _URC_CONTINUE_UNWIND; return; } else if (ip == 0) call_terminate(native_exception, unwind_exception); // ip is 1-based index into call site table #else // __arm__ uintptr_t ipOffset = ip - funcStart; #endif // __arm__ const uint8_t* classInfo = NULL; // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding // dwarf emission // Parse LSDA header. 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) { // Calculate type info locations in emitted dwarf code which // were flagged by type info arguments to llvm.eh.selector // intrinsic uintptr_t classInfoOffset = readULEB128(&lsda); classInfo = lsda + classInfoOffset; } // Walk call-site table looking for range that // includes current PC. uint8_t callSiteEncoding = *lsda++; #if __arm__ (void)callSiteEncoding; // On arm callSiteEncoding is never used #endif 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) { // There is one entry per call site. #if !__arm__ // The call sites are non-overlapping in [start, start+length) // The call sites are ordered in increasing value of start 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))) #else // __arm__ // ip is 1-based index into this table uintptr_t landingPad = readULEB128(&callSitePtr); uintptr_t actionEntry = readULEB128(&callSitePtr); if (--ip == 0) #endif // __arm__ { // Found the call site containing ip. #if !__arm__ if (landingPad == 0) { // No handler here results.reason = _URC_CONTINUE_UNWIND; return; } landingPad = (uintptr_t)lpStart + landingPad; #else // __arm__ ++landingPad; #endif // __arm__ if (actionEntry == 0) { // Found a cleanup // If this is a type 1 or type 2 search, there are no handlers // If this is a type 3 search, you want to install the cleanup. if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) { results.ttypeIndex = 0; // Redundant but clarifying results.landingPad = landingPad; results.reason = _URC_HANDLER_FOUND; return; } // No handler here results.reason = _URC_CONTINUE_UNWIND; return; } // Convert 1-based byte offset into const uint8_t* action = actionTableStart + (actionEntry - 1); // Scan action entries until you find a matching handler, cleanup, or the end of action list 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 = get_shim_type_info(static_cast<uint64_t>(ttypeIndex), classInfo, ttypeEncoding, native_exception, unwind_exception); if (catchType == 0) { // Found catch (...) catches everything, including foreign exceptions // If this is a type 1 search save state and return _URC_HANDLER_FOUND // If this is a type 2 search save state and return _URC_HANDLER_FOUND // If this is a type 3 search !_UA_FORCE_UNWIND, we should have found this in phase 1! // If this is a type 3 search _UA_FORCE_UNWIND, ignore handler and continue scan 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 = get_thrown_object_ptr(unwind_exception); 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(native_exception, unwind_exception); } } // Else this is a catch (T) clause and will never // catch a foreign exception else if (native_exception) { __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; void* adjustedPtr = get_thrown_object_ptr(unwind_exception); const __shim_type_info* excpType = static_cast<const __shim_type_info*>(exception_header->exceptionType); if (adjustedPtr == 0 || excpType == 0) { // Something very bad happened call_terminate(native_exception, unwind_exception); } if (catchType->can_catch(excpType, adjustedPtr)) { // Found a matching handler // If this is a type 1 search save state and return _URC_HANDLER_FOUND // If this is a type 3 search and !_UA_FORCE_UNWIND, we should have found this in phase 1! // If this is a type 3 search and _UA_FORCE_UNWIND, ignore handler and continue scan if (actions & _UA_SEARCH_PHASE) { // Save state and return _URC_HANDLER_FOUND 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(native_exception, unwind_exception); } } } // Scan next action ... } else if (ttypeIndex < 0) { // Found an exception spec. If this is a foreign exception, // it is always caught. if (native_exception) { // Does the exception spec catch this native exception? __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; void* adjustedPtr = get_thrown_object_ptr(unwind_exception); const __shim_type_info* excpType = static_cast<const __shim_type_info*>(exception_header->exceptionType); if (adjustedPtr == 0 || excpType == 0) { // Something very bad happened call_terminate(native_exception, unwind_exception); } if (exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, excpType, adjustedPtr, unwind_exception)) { // native exception caught by exception spec // If this is a type 1 search, save state and return _URC_HANDLER_FOUND // If this is a type 3 search !_UA_FORCE_UNWIND, we should have found this in phase 1! // If this is a type 3 search _UA_FORCE_UNWIND, ignore handler and continue scan if (actions & _UA_SEARCH_PHASE) { // Save state and return _URC_HANDLER_FOUND 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(native_exception, unwind_exception); } } } else { // foreign exception caught by exception spec // If this is a type 1 search, save state and return _URC_HANDLER_FOUND // If this is a type 2 search, save state and return _URC_HANDLER_FOUND // If this is a type 3 search !_UA_FORCE_UNWIND, we should have found this in phase 1! // If this is a type 3 search _UA_FORCE_UNWIND, ignore handler and continue scan 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 = get_thrown_object_ptr(unwind_exception); 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(native_exception, unwind_exception); } } // Scan next action ... } else // ttypeIndex == 0 { // Found a cleanup // If this is a type 1 search, ignore it and continue scan // If this is a type 2 search, ignore it and continue scan // If this is a type 3 search, save state and return _URC_HANDLER_FOUND if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) { // Save state and return _URC_HANDLER_FOUND results.ttypeIndex = ttypeIndex; results.actionRecord = actionRecord; results.landingPad = landingPad; results.adjustedPtr = get_thrown_object_ptr(unwind_exception); 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; } // there is no break out of this loop, only return } #if !__arm__ else if (ipOffset < start) { // There is no call site for this ip // Something bad has happened. We should never get here. // Possible stack corruption. call_terminate(native_exception, unwind_exception); } #endif // !__arm__ } // there might be some tricky cases which break out of this loop // It is possible that no eh table entry specify how to handle // this exception. By spec, terminate it immediately. call_terminate(native_exception, unwind_exception); }