mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 06:16:30 +03:00
189 lines
6.4 KiB
C++
189 lines
6.4 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 "base/memory/ptr_util.h"
|
|
|
|
namespace base {
|
|
|
|
// Win32UnwindFunctions -------------------------------------------------------
|
|
|
|
const HMODULE ModuleHandleTraits::kNonNullModuleForTesting =
|
|
reinterpret_cast<HMODULE>(static_cast<uintptr_t>(-1));
|
|
|
|
// static
|
|
bool ModuleHandleTraits::CloseHandle(HMODULE handle) {
|
|
if (handle == kNonNullModuleForTesting)
|
|
return true;
|
|
|
|
return ::FreeLibrary(handle) != 0;
|
|
}
|
|
|
|
// static
|
|
bool ModuleHandleTraits::IsHandleValid(HMODULE handle) {
|
|
return handle != nullptr;
|
|
}
|
|
|
|
// static
|
|
HMODULE ModuleHandleTraits::NullHandle() {
|
|
return nullptr;
|
|
}
|
|
|
|
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;
|
|
|
|
ScopedModuleHandle GetModuleForProgramCounter(
|
|
DWORD64 program_counter) 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
|
|
}
|
|
|
|
ScopedModuleHandle Win32UnwindFunctions::GetModuleForProgramCounter(
|
|
DWORD64 program_counter) {
|
|
HMODULE module_handle = nullptr;
|
|
// GetModuleHandleEx() increments the module reference count, which is then
|
|
// managed and ultimately decremented by ScopedModuleHandle.
|
|
if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
|
reinterpret_cast<LPCTSTR>(program_counter),
|
|
&module_handle)) {
|
|
const DWORD error = ::GetLastError();
|
|
DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error));
|
|
}
|
|
return ScopedModuleHandle(module_handle);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Win32StackFrameUnwinder ----------------------------------------------------
|
|
|
|
Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {}
|
|
Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {}
|
|
|
|
Win32StackFrameUnwinder::Win32StackFrameUnwinder()
|
|
: Win32StackFrameUnwinder(WrapUnique(new Win32UnwindFunctions)) {}
|
|
|
|
Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {}
|
|
|
|
bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context,
|
|
ScopedModuleHandle* module) {
|
|
#ifdef _WIN64
|
|
// TODO(chengx): update base::ModuleCache to return a ScopedModuleHandle and
|
|
// use it for this module lookup.
|
|
ScopedModuleHandle frame_module =
|
|
unwind_functions_->GetModuleForProgramCounter(context->Rip);
|
|
if (!frame_module.IsValid()) {
|
|
// There's no loaded module containing the instruction pointer. This can be
|
|
// due to executing code that is not in a module. In particular,
|
|
// runtime-generated code associated with third-party injected DLLs
|
|
// typically is not in a module. It can also be due to the the module having
|
|
// been unloaded since we recorded the stack. In the latter case the
|
|
// function unwind information was part of the unloaded module, so it's not
|
|
// possible to unwind further.
|
|
//
|
|
// If a module was found, it's still theoretically possible for the detected
|
|
// module module to be different than the one that was loaded when the stack
|
|
// was copied (i.e. if the module was unloaded and a different module loaded
|
|
// in overlapping memory). This likely would cause a crash, but has not been
|
|
// observed in practice.
|
|
return false;
|
|
}
|
|
|
|
ULONG64 image_base;
|
|
// Try to look up unwind metadata for the current function.
|
|
PRUNTIME_FUNCTION runtime_function =
|
|
unwind_functions_->LookupFunctionEntry(context->Rip, &image_base);
|
|
|
|
if (runtime_function) {
|
|
unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function,
|
|
context);
|
|
at_top_frame_ = false;
|
|
} else {
|
|
if (at_top_frame_) {
|
|
at_top_frame_ = false;
|
|
|
|
// This is a leaf function (i.e. a function that neither calls a function,
|
|
// nor allocates any stack space itself) so the return address is at RSP.
|
|
context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
|
|
context->Rsp += 8;
|
|
} else {
|
|
// 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.
|
|
//
|
|
// Note that dodgy third-party generated code that otherwise would enter
|
|
// this path should be caught by the module check above, since the code
|
|
// typically is located outside of a module.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
module->Set(frame_module.Take());
|
|
return true;
|
|
#else
|
|
NOTREACHED();
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
Win32StackFrameUnwinder::Win32StackFrameUnwinder(
|
|
std::unique_ptr<UnwindFunctions> unwind_functions)
|
|
: at_top_frame_(true), unwind_functions_(std::move(unwind_functions)) {}
|
|
|
|
} // namespace base
|