/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/WindowsDiagnostics.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/Types.h" #include #include #if defined(_M_AMD64) namespace mozilla { MOZ_RUNINIT static OnSingleStepCallback sOnSingleStepCallback{}; static void* sOnSingleStepCallbackState = nullptr; static bool sIsSingleStepping = false; MFBT_API AutoOnSingleStepCallback::AutoOnSingleStepCallback( OnSingleStepCallback aOnSingleStepCallback, void* aState) { MOZ_RELEASE_ASSERT(!sIsSingleStepping && !sOnSingleStepCallback && !sOnSingleStepCallbackState, "Single-stepping is already active"); sOnSingleStepCallback = std::move(aOnSingleStepCallback); sOnSingleStepCallbackState = aState; sIsSingleStepping = true; } MFBT_API AutoOnSingleStepCallback::~AutoOnSingleStepCallback() { sOnSingleStepCallback = OnSingleStepCallback(); sOnSingleStepCallbackState = nullptr; sIsSingleStepping = false; } // Going though this assembly code turns on the trap flag, which will trigger // a first single-step exception. It is then up to the exception handler to // keep the trap flag enabled so that a new single step exception gets // triggered with the following instruction. MFBT_API MOZ_NEVER_INLINE MOZ_NAKED void EnableTrapFlag() { asm volatile( "pushfq;" "orw $0x100,(%rsp);" "popfq;" "retq;"); } // This function does not do anything special, but when we reach its address // while single-stepping the exception handler will know that it is now time to // leave the trap flag turned off. MFBT_API MOZ_NEVER_INLINE MOZ_NAKED void DisableTrapFlag() { asm volatile("retq;"); } MFBT_API LONG SingleStepExceptionHandler(_EXCEPTION_POINTERS* aExceptionInfo) { if (sIsSingleStepping && sOnSingleStepCallback && aExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) { auto instructionPointer = aExceptionInfo->ContextRecord->Rip; bool keepOnSingleStepping = false; if (instructionPointer != reinterpret_cast(&DisableTrapFlag)) { keepOnSingleStepping = sOnSingleStepCallback( sOnSingleStepCallbackState, aExceptionInfo->ContextRecord); } if (keepOnSingleStepping) { aExceptionInfo->ContextRecord->EFlags |= 0x100; } else { sIsSingleStepping = false; } return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; } } // namespace mozilla #endif // _M_AMD64