void smashJmp(TCA inst, TCA target) { always_assert(is_aligned(inst, Alignment::SmashJmp)); auto& cb = mcg->code().blockFor(inst); CodeCursor cursor { cb, inst }; X64Assembler a { cb }; if (target > inst && target - inst <= smashableJmpLen()) { a.emitNop(target - inst); } else { a.jmp(target); } }
TCA emitCallToExit(CodeBlock& cb) { X64Assembler a { cb }; // Emit a byte of padding. This is a kind of hacky way to avoid // hitting an assert in recordGdbStub when we call it with stub - 1 // as the start address. a.emitNop(1); auto const start = a.frontier(); if (RuntimeOption::EvalHHIRGenerateAsserts) { Label ok; a.emitImmReg(uintptr_t(enterTCExit), reg::rax); a.cmpq(reg::rax, *rsp()); a.je8 (ok); a.ud2(); asm_label(a, ok); } // Emulate a ret to enterTCExit without actually doing one to avoid // unbalancing the return stack buffer. The call from enterTCHelper() that // got us into the TC was popped off the RSB by the ret that got us to this // stub. a.addq(8, rsp()); if (a.jmpDeltaFits(TCA(enterTCExit))) { a.jmp(TCA(enterTCExit)); } else { // can't do a near jmp and a rip-relative load/jmp would require threading // through extra state to allocate a literal. use an indirect jump through // a register a.emitImmReg(uintptr_t(enterTCExit), reg::rax); a.jmp(reg::rax); } // On a backtrace, gdb tries to locate the calling frame at address // returnRIP-1. However, for the first VM frame, there is no code at // returnRIP-1, since the AR was set up manually. For this frame, // record the tracelet address as starting from this callToExit-1, // so gdb does not barf. return start; }