text: // 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 <windows.h>
  
  #include <utility>
  
  #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;
    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<Win32UnwindFunctions>()) {}
  
  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<DWORD64*>(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<UnwindFunctions> unwind_functions)
      : unwind_functions_(std::move(unwind_functions)) {}
  
  }  // namespace base
  