// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/profiler/win32_stack_frame_unwinder.h" #include #include #include "base/macros.h" #include "build/build_config.h" namespace base { // Win32UnwindFunctions ------------------------------------------------------- namespace { // Implements the UnwindFunctions interface for the corresponding Win32 // functions. class Win32UnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions { public: Win32UnwindFunctions(); ~Win32UnwindFunctions() override; PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter, PDWORD64 image_base) override; void VirtualUnwind(DWORD64 image_base, DWORD64 program_counter, PRUNTIME_FUNCTION runtime_function, CONTEXT* context) override; private: DISALLOW_COPY_AND_ASSIGN(Win32UnwindFunctions); }; Win32UnwindFunctions::Win32UnwindFunctions() {} Win32UnwindFunctions::~Win32UnwindFunctions() {} PRUNTIME_FUNCTION Win32UnwindFunctions::LookupFunctionEntry( DWORD64 program_counter, PDWORD64 image_base) { #ifdef _WIN64 return ::RtlLookupFunctionEntry(program_counter, image_base, nullptr); #else NOTREACHED(); return nullptr; #endif } void Win32UnwindFunctions::VirtualUnwind(DWORD64 image_base, DWORD64 program_counter, PRUNTIME_FUNCTION runtime_function, CONTEXT* context) { #ifdef _WIN64 void* handler_data = nullptr; ULONG64 establisher_frame; KNONVOLATILE_CONTEXT_POINTERS nvcontext = {}; ::RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, program_counter, runtime_function, context, &handler_data, &establisher_frame, &nvcontext); #else NOTREACHED(); #endif } } // namespace // Win32StackFrameUnwinder ---------------------------------------------------- Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() = default; Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() = default; Win32StackFrameUnwinder::Win32StackFrameUnwinder() : Win32StackFrameUnwinder(std::make_unique()) {} Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {} bool Win32StackFrameUnwinder::TryUnwind( bool at_top_frame, CONTEXT* context, // The module parameter, while not directly used, is still passed because it // represents an implicit dependency for this function. Having the Module // ensures that we have incremented the HMODULE reference count, which is // critical to ensuring that the module is not unloaded during the // unwinding. Otherwise the module could be unloaded between the // LookupFunctionEntry and VirtualUnwind calls, resulting in crashes // accessing unwind information from the unloaded module. const ModuleCache::Module* module) { #ifdef _WIN64 // Ensure we found a valid module for the program counter. DCHECK(module); ULONG64 image_base; // Try to look up unwind metadata for the current function. PRUNTIME_FUNCTION runtime_function = unwind_functions_->LookupFunctionEntry(ContextPC(context), &image_base); DCHECK_EQ(module->GetBaseAddress(), image_base); if (runtime_function) { unwind_functions_->VirtualUnwind(image_base, ContextPC(context), runtime_function, context); return true; } if (at_top_frame) { // This is a leaf function (i.e. a function that neither calls a function, // nor allocates any stack space itself). #if defined(ARCH_CPU_X86_64) // For X64, return address is at RSP. context->Rip = *reinterpret_cast(context->Rsp); context->Rsp += 8; #elif defined(ARCH_CPU_ARM64) // For leaf function on Windows ARM64, return address is at LR(X30). Add // CONTEXT_UNWOUND_TO_CALL flag to avoid unwind ambiguity for tailcall on // ARM64, because padding after tailcall is not guaranteed. context->Pc = context->Lr; context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; #else #error Unsupported Windows 64-bit Arch #endif return true; } // In theory we shouldn't get here, as it means we've encountered a function // without unwind information below the top of the stack, which is forbidden // by the Microsoft x64 calling convention. // // The one known case in Chrome code that executes this path occurs because // of BoringSSL unwind information inconsistent with the actual function // code. See https://crbug.com/542919. return false; #else NOTREACHED(); return false; #endif } Win32StackFrameUnwinder::Win32StackFrameUnwinder( std::unique_ptr unwind_functions) : unwind_functions_(std::move(unwind_functions)) {} } // namespace base