bool install_catch_trace(_Unwind_Context* ctx, _Unwind_Exception* exn, bool do_side_exit, TypedValue unwinder_tv) { auto const rip = (TCA)_Unwind_GetIP(ctx); auto catchTraceOpt = mcg->getCatchTrace(rip); FTRACE(1, "No catch trace entry for ip {}; bailing\n", rip); if (!catchTraceOpt) return false; auto catchTrace = *catchTraceOpt; if (!catchTrace) { // A few of our optimization passes must be aware of every path out of the // trace, so throwing through jitted code without a catch block is very // bad. This is indicated with a present but nullptr entry in the catch // trace map. const size_t kCallSize = 5; const uint8_t kCallOpcode = 0xe8; auto callAddr = rip - kCallSize; TCA helperAddr = nullptr; std::string helperName; if (*callAddr == kCallOpcode) { helperAddr = rip + *reinterpret_cast<int32_t*>(callAddr + 1); } always_assert_flog(false, "Translated call to {} threw '{}' without " "catch block, return address: {}\n", getNativeFunctionName(helperAddr), exceptionFromUnwindException(exn)->what(), rip); return false; } FTRACE(1, "installing catch trace {} for call {} with tv {}, " "returning _URC_INSTALL_CONTEXT\n", catchTrace, rip, unwinder_tv.pretty()); // In theory the unwind api will let us set registers in the frame // before executing our landing pad. In practice, trying to use // their recommended scratch registers results in a SEGV inside // _Unwind_SetGR, so we pass things to the handler using the // RDS. This also simplifies the handler code because it doesn't // have to worry about saving its arguments somewhere while // executing the exit trace. unwindRdsInfo->unwinderScratch = (int64_t)exn; unwindRdsInfo->doSideExit = do_side_exit; if (do_side_exit) { unwindRdsInfo->unwinderTv = unwinder_tv; } _Unwind_SetIP(ctx, (uint64_t)catchTrace); tl_regState = VMRegState::DIRTY; return true; }
bool install_catch_trace(_Unwind_Context* ctx, _Unwind_Exception* exn, bool do_side_exit, TypedValue unwinder_tv) { auto const rip = (TCA)_Unwind_GetIP(ctx); auto catchTrace = lookup_catch_trace(rip, exn); if (!catchTrace) { FTRACE(1, "No catch trace entry for ip {}; bailing\n", rip); return false; } FTRACE(1, "installing catch trace {} for call {} with tv {}, " "returning _URC_INSTALL_CONTEXT\n", catchTrace, rip, unwinder_tv.pretty()); // If the catch trace isn't going to finish by calling _Unwind_Resume, we // consume the exception here. Otherwise, we leave a pointer to it in RDS so // endCatchHelper can pass it to _Unwind_Resume when it's done. if (do_side_exit) { unwindRdsInfo->exn = nullptr; #ifndef _MSC_VER __cxxabiv1::__cxa_begin_catch(exn); __cxxabiv1::__cxa_end_catch(); #endif } else { unwindRdsInfo->exn = exn; } // In theory the unwind api will let us set registers in the frame before // executing our landing pad. In practice, trying to use their recommended // scratch registers results in a SEGV inside _Unwind_SetGR, so we pass // things to the handler using the RDS. This also simplifies the handler code // because it doesn't have to worry about saving its arguments somewhere // while executing the exit trace. unwindRdsInfo->doSideExit = do_side_exit; if (do_side_exit) unwindRdsInfo->unwinderTv = unwinder_tv; _Unwind_SetIP(ctx, (uint64_t)catchTrace); tl_regState = VMRegState::DIRTY; return true; }