Index: chrome_frame/vtable_patch_manager.cc |
diff --git a/chrome_frame/vtable_patch_manager.cc b/chrome_frame/vtable_patch_manager.cc |
deleted file mode 100644 |
index da1bbabbff6ad7fa8748a35e38fc79e7084cb0c6..0000000000000000000000000000000000000000 |
--- a/chrome_frame/vtable_patch_manager.cc |
+++ /dev/null |
@@ -1,244 +0,0 @@ |
-// Copyright (c) 2011 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 "chrome_frame/vtable_patch_manager.h" |
- |
-#include <atlcomcli.h> |
- |
-#include <algorithm> |
- |
-#include "base/atomicops.h" |
-#include "base/logging.h" |
-#include "base/memory/scoped_ptr.h" |
-#include "base/synchronization/lock.h" |
-#include "chrome_frame/function_stub.h" |
-#include "chrome_frame/pin_module.h" |
- |
-namespace vtable_patch { |
- |
-// The number of times we retry a patch/unpatch operation in case of |
-// VM races with other 3rd party software trying to patch the same thing. |
-const int kMaxRetries = 3; |
- |
-// We hold a lock over all patching operations to make sure that we don't |
-// e.g. race on VM operations to the same patches, or to physical pages |
-// shared across different VTABLEs. |
-base::Lock patch_lock_; |
- |
-namespace internal { |
-// Because other parties in our process might be attempting to patch the same |
-// virtual tables at the same time, we have a race to modify the VM protections |
-// on the pages. We also need to do a compare/swap type operation when we |
-// modify the function, so as to be sure that we grab the most recent value. |
-// Hence the SEH blocks and the nasty-looking compare/swap operation. |
-bool ReplaceFunctionPointer(void** entry, void* new_proc, void* curr_proc) { |
- __try { |
- base::subtle::Atomic32 prev_value; |
- |
- prev_value = base::subtle::NoBarrier_CompareAndSwap( |
- reinterpret_cast<base::subtle::Atomic32 volatile*>(entry), |
- reinterpret_cast<base::subtle::Atomic32>(curr_proc), |
- reinterpret_cast<base::subtle::Atomic32>(new_proc)); |
- |
- return curr_proc == reinterpret_cast<void*>(prev_value); |
- } __except(EXCEPTION_EXECUTE_HANDLER) { |
- // Oops, we took exception on access. |
- } |
- |
- return false; |
-} |
- |
-} // namespace |
- |
-// Convenient definition of a VTABLE |
-typedef PROC* Vtable; |
- |
-// Returns a pointer to the VTable of a COM interface. |
-// @param unknown [in] The pointer of the COM interface. |
-inline Vtable GetIFVTable(void* unknown) { |
- return reinterpret_cast<Vtable>(*reinterpret_cast<void**>(unknown)); |
-} |
- |
-HRESULT PatchInterfaceMethods(void* unknown, MethodPatchInfo* patches) { |
- // Do some sanity checking of the input arguments. |
- if (NULL == unknown || NULL == patches) { |
- NOTREACHED(); |
- return E_INVALIDARG; |
- } |
- |
- Vtable vtable = GetIFVTable(unknown); |
- DCHECK(vtable); |
- |
- // All VM operations, patching and manipulation of MethodPatchInfo |
- // is done under a global lock, to ensure multiple threads don't |
- // race, whether on an individual patch, or on VM operations to |
- // the same physical pages. |
- base::AutoLock lock(patch_lock_); |
- |
- for (MethodPatchInfo* it = patches; it->index_ != -1; ++it) { |
- if (it->stub_ != NULL) { |
- // If this DCHECK fires it means that we are using the same VTable |
- // information to patch two different interfaces, or we've lost a |
- // race with another thread who's patching the same interface. |
- DLOG(WARNING) << "Attempting to patch two different VTables with the " |
- "same VTable information, or patching the same interface on " |
- "multiple threads"; |
- continue; |
- } |
- |
- PROC original_fn = vtable[it->index_]; |
- FunctionStub* stub = NULL; |
- |
-#ifndef NDEBUG |
- stub = FunctionStub::FromCode(original_fn); |
- if (stub != NULL) { |
- DLOG(ERROR) << "attempt to patch a function that's already patched"; |
- DCHECK(stub->destination_function() == |
- reinterpret_cast<uintptr_t>(it->method_)) << |
- "patching the same method multiple times with different hooks?"; |
- continue; |
- } |
-#endif |
- |
- stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(original_fn), |
- it->method_); |
- if (!stub) { |
- NOTREACHED(); |
- return E_OUTOFMEMORY; |
- } |
- |
- // Do the VM operations and the patching in a loop, to try and ensure |
- // we succeed even if there's a VM operation or a patch race against |
- // other 3rd parties patching. |
- bool succeeded = false; |
- for (int i = 0; !succeeded && i < kMaxRetries; ++i) { |
- DWORD protect = 0; |
- if (!::VirtualProtect(&vtable[it->index_], sizeof(PROC), |
- PAGE_EXECUTE_READWRITE, &protect)) { |
- HRESULT hr = AtlHresultFromLastError(); |
- DLOG(ERROR) << "VirtualProtect failed 0x" << std::hex << hr; |
- |
- // Go around again in the feeble hope that this is |
- // a temporary problem. |
- continue; |
- } |
- original_fn = vtable[it->index_]; |
- stub->set_argument(reinterpret_cast<uintptr_t>(original_fn)); |
- succeeded = internal::ReplaceFunctionPointer( |
- reinterpret_cast<void**>(&vtable[it->index_]), stub->code(), |
- original_fn); |
- |
- if (!::VirtualProtect(&vtable[it->index_], sizeof(PROC), protect, |
- &protect)) { |
- DLOG(ERROR) << "VirtualProtect failed to restore protection"; |
- } |
- } |
- |
- if (!succeeded) { |
- FunctionStub::Destroy(stub); |
- stub = NULL; |
- |
- DLOG(ERROR) << "Failed to patch VTable."; |
- return E_FAIL; |
- } else { |
- // Success, save the stub we created. |
- it->stub_ = stub; |
- chrome_frame::PinModule(); |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT UnpatchInterfaceMethods(MethodPatchInfo* patches) { |
- base::AutoLock lock(patch_lock_); |
- |
- for (MethodPatchInfo* it = patches; it->index_ != -1; ++it) { |
- if (it->stub_) { |
- DCHECK(it->stub_->destination_function() == |
- reinterpret_cast<uintptr_t>(it->method_)); |
- // Modify the stub to just jump directly to the original function. |
- it->stub_->BypassStub(reinterpret_cast<void*>(it->stub_->argument())); |
- it->stub_ = NULL; |
- // Leave the stub in memory so that we won't break any possible chains. |
- |
- // TODO(siggi): why not restore the original VTBL pointer here, provided |
- // we haven't been chained? |
- } else { |
- DLOG(WARNING) << "attempt to unpatch a function that wasn't patched"; |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-// Disabled for now as we're not using it atm. |
-#if 0 |
- |
-DynamicPatchManager::DynamicPatchManager(const MethodPatchInfo* patch_prototype) |
- : patch_prototype_(patch_prototype) { |
- DCHECK(patch_prototype_); |
- DCHECK(patch_prototype_->stub_ == NULL); |
-} |
- |
-DynamicPatchManager::~DynamicPatchManager() { |
- UnpatchAll(); |
-} |
- |
-HRESULT DynamicPatchManager::PatchObject(void* unknown) { |
- int patched_methods = 0; |
- for (; patch_prototype_[patched_methods].index_ != -1; patched_methods++) { |
- // If you hit this, then you are likely using the prototype instance for |
- // patching in _addition_ to this class. This is not a good idea :) |
- DCHECK(patch_prototype_[patched_methods].stub_ == NULL); |
- } |
- |
- // Prepare a new patch object using the patch info from the prototype. |
- int mem_size = sizeof(PatchedObject) + |
- sizeof(MethodPatchInfo) * patched_methods; |
- PatchedObject* entry = reinterpret_cast<PatchedObject*>(new char[mem_size]); |
- entry->vtable_ = GetIFVTable(unknown); |
- memcpy(entry->patch_info_, patch_prototype_, |
- sizeof(MethodPatchInfo) * (patched_methods + 1)); |
- |
- patch_list_lock_.Acquire(); |
- |
- // See if we've already patched this vtable before. |
- // The search is done via the == operator of the PatchedObject class. |
- PatchList::const_iterator it = std::find(patch_list_.begin(), |
- patch_list_.end(), entry); |
- HRESULT hr; |
- if (it == patch_list_.end()) { |
- hr = PatchInterfaceMethods(unknown, entry->patch_info_); |
- if (SUCCEEDED(hr)) { |
- patch_list_.push_back(entry); |
- entry = NULL; // Ownership transferred to the array. |
- } |
- } else { |
- hr = S_FALSE; |
- } |
- |
- patch_list_lock_.Release(); |
- |
- delete entry; |
- |
- return hr; |
-} |
- |
-bool DynamicPatchManager::UnpatchAll() { |
- patch_list_lock_.Acquire(); |
- PatchList::iterator it; |
- for (it = patch_list_.begin(); it != patch_list_.end(); it++) { |
- UnpatchInterfaceMethods((*it)->patch_info_); |
- delete (*it); |
- } |
- patch_list_.clear(); |
- patch_list_lock_.Release(); |
- |
- return true; |
-} |
- |
-#endif // disabled DynamicPatchManager |
- |
-} // namespace vtable_patch |