/* * Lookup a catch trace for the given TCA, returning nullptr if none was * found. Will abort if a nullptr catch trace was registered, meaning this call * isn't allowed to throw. */ TCA lookup_catch_trace(TCA rip, _Unwind_Exception* exn) { if (auto catchTraceOpt = mcg->getCatchTrace(rip)) { if (auto catchTrace = *catchTraceOpt) return 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; 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), typeInfoFromUnwindException(exn).name(), rip); } return nullptr; }
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; }
// XED callback function to get a symbol from an address static int addressToSymbol(xed_uint64_t address, char* symbolBuffer, xed_uint32_t bufferLength, xed_uint64_t* offset, void* context) { auto name = boost::trim_copy(getNativeFunctionName((void*)address)); if (boost::starts_with(name, "0x")) { return 0; } auto pos = name.find_first_of('('); auto copyLength = pos != std::string::npos ? std::min(pos, size_t(bufferLength - 1)) : bufferLength - 1; strncpy(symbolBuffer, name.c_str(), copyLength); symbolBuffer[copyLength] = '\0'; *offset = 0; return 1; }