blob: 700e747957cb4c44986f13016d8141cb7ec056e1 [file] [log] [blame]
// 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 = 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<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