145 lines
4.9 KiB
C++
145 lines
4.9 KiB
C++
|
// 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
|