void handleStackOverflow(ActRec* calleeAR) { /* * First synchronize registers. * * We're called in two situations: either this is the first frame after a * re-entry, in which case calleeAR->m_sfp is enterTCHelper's native stack, * or we're called in the middle of one VM entry (from a func prologue). We * want to raise the exception from the caller's FCall instruction in the * second case, and in the first case we have to raise in a special way * inside this re-entry. * * Either way the stack depth is below the calleeAR by numArgs, because we * haven't run func prologue duties yet. */ auto& unsafeRegs = vmRegsUnsafe(); auto const isReentry = calleeAR == vmFirstAR(); auto const arToSync = isReentry ? calleeAR : calleeAR->m_sfp; unsafeRegs.fp = arToSync; unsafeRegs.stack.top() = reinterpret_cast<Cell*>(calleeAR) - calleeAR->numArgs(); auto const func_base = arToSync->func()->base(); // calleeAR m_soff is 0 in the re-entry case, so we'll set pc to the func // base. But it also doesn't matter because we're going to throw a special // VMReenterStackOverflow in that case so the unwinder won't worry about it. unsafeRegs.pc = arToSync->func()->unit()->at(func_base + calleeAR->m_soff); tl_regState = VMRegState::CLEAN; if (!isReentry) { /* * The normal case - we were called via FCall, or FCallArray. We need to * construct the pc of the fcall from the return address (which will be * after the fcall). Because fcall is a variable length instruction, and * because we sometimes delete instructions from the instruction stream, we * need to use fpi regions to find the fcall. */ const FPIEnt* fe = liveFunc()->findPrecedingFPI( liveUnit()->offsetOf(vmpc())); vmpc() = liveUnit()->at(fe->m_fcallOff); assertx(isFCallStar(peek_op(vmpc()))); raise_error("Stack overflow"); } else { /* * We were called via re-entry. Leak the params and the ActRec, and tell * the unwinder that there's nothing left to do in this "entry". * * Also, the caller hasn't set up the m_invName area on the ActRec (unless * it was a magic call), since it's the prologue's responsibility if it's a * non-magic call. We can just null it out since we're fatalling. */ vmsp() = reinterpret_cast<Cell*>(calleeAR + 1); calleeAR->setVarEnv(nullptr); throw VMReenterStackOverflow(); } not_reached(); }
void enterTC(TCA start, ActRec* stashedAR) { if (debug) { fflush(stdout); fflush(stderr); } assertx(tc::isValidCodeAddress(start)); assertx(((uintptr_t)vmsp() & (sizeof(Cell) - 1)) == 0); assertx(((uintptr_t)vmfp() & (sizeof(Cell) - 1)) == 0); INC_TPC(enter_tc); if (Trace::moduleEnabled(Trace::ringbuffer, 1)) { auto skData = SrcKey{liveFunc(), vmpc(), liveResumed()}.toAtomicInt(); Trace::ringbufferEntry(Trace::RBTypeEnterTC, skData, (uint64_t)start); } tl_regState = VMRegState::DIRTY; enterTCImpl(start, stashedAR); tl_regState = VMRegState::CLEAN; assertx(isValidVMStackAddress(vmsp())); vmfp() = nullptr; }